Merge pull request #1688 from oqtane/dev

2.3.0 release
This commit is contained in:
Shaun Walker 2021-09-24 17:11:32 -04:00 committed by GitHub
commit 6cd1b1ceaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
186 changed files with 6465 additions and 4706 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2018-2020 .NET Foundation Copyright (c) 2018-2021 .NET Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -12,11 +12,13 @@
{ {
@if (string.IsNullOrEmpty(_installation.Message)) @if (string.IsNullOrEmpty(_installation.Message))
{ {
<div style="@_display">
<CascadingAuthenticationState> <CascadingAuthenticationState>
<CascadingValue Value="@PageState"> <CascadingValue Value="@PageState">
<SiteRouter OnStateChange="@ChangeState" /> <SiteRouter Runtime="@Runtime" RenderMode="@RenderMode" OnStateChange="@ChangeState" />
</CascadingValue> </CascadingValue>
</CascadingAuthenticationState> </CascadingAuthenticationState>
</div>
} }
else else
{ {
@ -28,27 +30,50 @@
} }
@code { @code {
[Parameter]
public string AntiForgeryToken { get; set; }
[Parameter]
public string Runtime { get; set; }
[Parameter]
public string RenderMode { get; set; }
private bool _initialized = false; private bool _initialized = false;
private string _display = "display: none;";
private Installation _installation = new Installation { Success = false, Message = "" }; private Installation _installation = new Installation { Success = false, Message = "" };
private PageState PageState { get; set; } private PageState PageState { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnParametersSetAsync()
{ {
if (firstRender && !_initialized)
{
var interop = new Interop(JSRuntime);
SiteState.AntiForgeryToken = await interop.GetElementByName(Constants.RequestVerificationToken);
_installation = await InstallationService.IsInstalled(); _installation = await InstallationService.IsInstalled();
if (_installation.Alias != null) if (_installation.Alias != null)
{ {
SiteState.Alias = _installation.Alias; SiteState.Alias = _installation.Alias;
SiteState.AntiForgeryToken = AntiForgeryToken;
} }
else else
{ {
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name"; _installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
} }
_initialized = true; _initialized = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (string.IsNullOrEmpty(AntiForgeryToken))
{
// parameter values are not set when running on WebAssembly (seems to be a .NET 5 bug) - need to retrieve using JSInterop
var interop = new Interop(JSRuntime);
AntiForgeryToken = await interop.GetElementByName(Constants.RequestVerificationToken);
SiteState.AntiForgeryToken = AntiForgeryToken;
Runtime = await interop.GetElementByName("app_runtime");
RenderMode = await interop.GetElementByName("app_rendermode");
}
_display = "";
StateHasChanged(); StateHasChanged();
} }
} }

View File

@ -1,41 +1,30 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<Installer> Localizer
@{ <div class="row mb-1 align-items-center">
foreach (var field in _connectionStringFields) <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
{ <div class="col-sm-9">
var fieldId = field.Name.ToLowerInvariant(); <input id="server" type="text" class="form-control" @bind="@_server" />
field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); </div>
</div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label> <div class="col-sm-9">
</td> <input id="database" type="text" class="form-control" @bind="@_database" />
<td> </div>
<input id="@fieldId" type="text" class="form-control" @bind="@field.Value" /> </div>
</td>
</tr>
}
}
@code { @code {
private readonly List<ConnectionStringField> _connectionStringFields = new() private string _server = "(LocalDb)\\MSSQLLocalDB";
{ private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
new() {Name = "Server", FriendlyName = "Server", Value = "(LocalDb)\\MSSQLLocalDB", HelpText="Enter the database server"},
new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"}
};
public string GetConnectionString() public string GetConnectionString()
{ {
var connectionString = String.Empty; var connectionString = String.Empty;
var server = _connectionStringFields[0].Value; if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database))
var database = _connectionStringFields[1].Value;
if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database))
{ {
connectionString = $"Data Source={server};AttachDbFilename=|DataDirectory|\\{database}.mdf;Initial Catalog={database};Integrated Security=SSPI;"; connectionString = $"Data Source={_server};AttachDbFilename=|DataDirectory|\\{_database}.mdf;Initial Catalog={_database};Integrated Security=SSPI;";
} }
return connectionString; return connectionString;

View File

@ -1,54 +1,58 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<Installer> Localizer
@{ <div class="row mb-1 align-items-center">
foreach (var field in _connectionStringFields) <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
{ <div class="col-sm-9">
var fieldId = field.Name.ToLowerInvariant(); <input id="server" type="text" class="form-control" @bind="@_server" />
var fieldType = (field.Name == "Pwd") ? "password" : "text"; </div>
field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); </div>
<div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="port" HelpText="Enter the port used to connect to the server" ResourceKey="Port">Port:</Label>
<td> <div class="col-sm-9">
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label> <input id="port" type="text" class="form-control" @bind="@_port" />
</td> </div>
<td> </div>
<input id="@fieldId" type="@fieldType" class="form-control" @bind="@field.Value" /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
</tr> <div class="col-sm-9">
} <input id="database" type="text" class="form-control" @bind="@_database" />
} </div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
<div class="col-sm-9">
<input id="uid" type="text" class="form-control" @bind="@_uid" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
</div>
</div>
@code { @code {
private readonly List<ConnectionStringField> _connectionStringFields = new() private string _server = "127.0.0.1";
{ private string _port = "3306";
new() {Name = "Server", FriendlyName = "Server", Value = "127.0.0.1", HelpText="Enter the database server"}, private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
new() {Name = "Port", FriendlyName = "Port", Value = "3306", HelpText="Enter the port used to connect to the server"}, private string _uid = String.Empty;
new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"}, private string _pwd = String.Empty;
new() {Name = "Uid", FriendlyName = "User Id", Value = "", HelpText="Enter the username to use for the database"},
new() {Name = "Pwd", FriendlyName = "Password", Value = "", HelpText="Enter the password to use for the database"}
};
public string GetConnectionString() public string GetConnectionString()
{ {
var connectionString = String.Empty; var connectionString = String.Empty;
var server = _connectionStringFields[0].Value; if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database) && !String.IsNullOrEmpty(_uid) && !String.IsNullOrEmpty(_pwd))
var port = _connectionStringFields[1].Value;
var database = _connectionStringFields[2].Value;
var userId = _connectionStringFields[3].Value;
var password = _connectionStringFields[4].Value;
if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database) && !String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password))
{ {
connectionString = $"Server={server};Database={database};Uid={userId};Pwd={password};"; connectionString = $"Server={_server};Database={_database};Uid={_uid};Pwd={_pwd};";
} }
if (!String.IsNullOrEmpty(port)) if (!String.IsNullOrEmpty(_port))
{ {
connectionString += $"Port={port};"; connectionString += $"Port={_port};";
} }
return connectionString; return connectionString;
} }
} }

View File

@ -1,89 +1,76 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<Installer> Localizer @inject IStringLocalizer<PostgreSQLConfig> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@{ <div class="row mb-1 align-items-center">
foreach (var field in _connectionStringFields) <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
{ <div class="col-sm-9">
var fieldId = field.Name.ToLowerInvariant(); <input id="server" type="text" class="form-control" @bind="@_server" />
if (field.Name != "IntegratedSecurity") </div>
{ </div>
var isVisible = ""; <div class="row mb-1 align-items-center">
var fieldType = (field.Name == "Pwd") ? "password" : "text"; <Label Class="col-sm-3" For="port" HelpText="Enter the port used to connect to the server" ResourceKey="Port">Port:</Label>
if ((field.Name == "Uid" || field.Name == "Pwd")) <div class="col-sm-9">
{ <input id="port" type="text" class="form-control" @bind="@_port" />
var intSecurityField = _connectionStringFields.Single(f => f.Name == "IntegratedSecurity"); </div>
if (intSecurityField != null) </div>
{ <div class="row mb-1 align-items-center">
isVisible = (Convert.ToBoolean(intSecurityField.Value)) ? "display: none;" : ""; <Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
} <div class="col-sm-9">
} <input id="database" type="text" class="form-control" @bind="@_database" />
</div>
field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); </div>
<div class="row mb-1 align-items-center">
<tr style="@isVisible"> <Label Class="col-sm-3" For="security" HelpText="Select your security method" ResourceKey="Security">Security:</Label>
<td> <div class="col-sm-9">
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label> <select id="security" class="form-select custom-select" @bind="@_security">
</td> <option value="integrated" selected>@Localizer["Integrated"]</option>
<td> <option value="custom">@Localizer["Custom"]</option>
<input id="@fieldId" type="@fieldType" class="form-control" @bind="@field.Value" />
</td>
</tr>
}
else
{
<tr>
<td>
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label>
</td>
<td>
<select id="@fieldId" class="custom-select" @bind="@field.Value">
<option value="true" selected>@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select> </select>
</td> </div>
</tr> </div>
} @if (_security == "custom")
} {
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
<div class="col-sm-9">
<input id="uid" type="text" class="form-control" @bind="@_uid" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
</div>
</div>
} }
@code { @code {
private readonly List<ConnectionStringField> _connectionStringFields = new() private string _server = "127.0.0.1";
{ private string _port = "5432";
new() { Name = "Server", FriendlyName = "Server", Value = "127.0.0.1", HelpText = "Enter the database server" }, private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
new() { Name = "Port", FriendlyName = "Port", Value = "5432", HelpText = "Enter the port used to connect to the server" }, private string _security = "integrated";
new() { Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText = "Enter the name of the database" }, private string _uid = String.Empty;
new() { Name = "IntegratedSecurity", FriendlyName = "Integrated Security", Value = "true", HelpText = "Select if you want integrated security or not" }, private string _pwd = String.Empty;
new() { Name = "Uid", FriendlyName = "User Id", Value = "", HelpText = "Enter the username to use for the database" },
new() { Name = "Pwd", FriendlyName = "Password", Value = "", HelpText = "Enter the password to use for the database" }
};
public string GetConnectionString() public string GetConnectionString()
{ {
var connectionString = String.Empty; var connectionString = String.Empty;
var server = _connectionStringFields[0].Value; if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database) && !String.IsNullOrEmpty(_port))
var port = _connectionStringFields[1].Value;
var database = _connectionStringFields[2].Value;
var integratedSecurity = Boolean.Parse(_connectionStringFields[3].Value);
var userId = _connectionStringFields[4].Value;
var password = _connectionStringFields[5].Value;
if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database) && !String.IsNullOrEmpty(port))
{ {
connectionString = $"Server={server};Port={port};Database={database};"; connectionString = $"Server={_server};Port={_port};Database={_database};";
} }
if (integratedSecurity) if (_security == "integrated")
{ {
connectionString += "Integrated Security=true;"; connectionString += "Integrated Security=true;";
} }
else else
{ {
if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password)) if (!String.IsNullOrEmpty(_uid) && !String.IsNullOrEmpty(_pwd))
{ {
connectionString += $"User ID={userId};Password={password};"; connectionString += $"User ID={_uid};Password={_pwd};";
} }
else else
{ {

View File

@ -1,87 +1,69 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<Installer> Localizer @inject IStringLocalizer<SqlServerConfig> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@{ <div class="row mb-1 align-items-center">
foreach (var field in _connectionStringFields) <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
{ <div class="col-sm-9">
var fieldId = field.Name.ToLowerInvariant(); <input id="server" type="text" class="form-control" @bind="@_server" />
if (field.Name != "IntegratedSecurity") </div>
{ </div>
var isVisible = ""; <div class="row mb-1 align-items-center">
var fieldType = (field.Name == "Pwd") ? "password" : "text"; <Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
if ((field.Name == "Uid" || field.Name == "Pwd")) <div class="col-sm-9">
{ <input id="database" type="text" class="form-control" @bind="@_database" />
var intSecurityField = _connectionStringFields.Single(f => f.Name == "IntegratedSecurity"); </div>
if (intSecurityField != null) </div>
{ <div class="row mb-1 align-items-center">
isVisible = (Convert.ToBoolean(intSecurityField.Value)) ? "display: none;" : ""; <Label Class="col-sm-3" For="security" HelpText="Select your security method" ResourceKey="Security">Security:</Label>
} <div class="col-sm-9">
} <select id="security" class="form-select custom-select" @bind="@_security">
<option value="integrated" selected>@Localizer["Integrated"]</option>
field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); <option value="custom">@Localizer["Custom"]</option>
<tr style="@isVisible">
<td>
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label>
</td>
<td>
<input id="@fieldId" type="@fieldType" class="form-control" @bind="@field.Value" />
</td>
</tr>
}
else
{
<tr>
<td>
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label>
</td>
<td>
<select id="@fieldId" class="custom-select" @bind="@field.Value">
<option value="true" selected>@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select> </select>
</td> </div>
</tr> </div>
} @if (_security == "custom")
} {
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
<div class="col-sm-9">
<input id="uid" type="text" class="form-control" @bind="@_uid" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" />
</div>
</div>
} }
@code { @code {
private readonly List<ConnectionStringField> _connectionStringFields = new() private string _server = String.Empty;
{ private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
new() { Name = "Server", FriendlyName = "Server", Value = ".", HelpText = "Enter the database server" }, private string _security = "integrated";
new() { Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText = "Enter the name of the database" }, private string _uid = String.Empty;
new() { Name = "IntegratedSecurity", FriendlyName = "Integrated Security", Value = "true", HelpText = "Select if you want integrated security or not" }, private string _pwd = String.Empty;
new() { Name = "Uid", FriendlyName = "User Id", Value = "", HelpText = "Enter the username to use for the database" },
new() { Name = "Pwd", FriendlyName = "Password", Value = "", HelpText = "Enter the password to use for the database" }
};
public string GetConnectionString() public string GetConnectionString()
{ {
var connectionString = String.Empty; var connectionString = String.Empty;
var server = _connectionStringFields[0].Value; if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database))
var database = _connectionStringFields[1].Value;
var integratedSecurity = Boolean.Parse(_connectionStringFields[2].Value);
var userId = _connectionStringFields[3].Value;
var password = _connectionStringFields[4].Value;
if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database))
{ {
connectionString = $"Data Source={server};Initial Catalog={database};"; connectionString = $"Data Source={_server};Initial Catalog={_database};";
} }
if (integratedSecurity) if (_security == "integrated")
{ {
connectionString += "Integrated Security=SSPI;"; connectionString += "Integrated Security=SSPI;";
} }
else else
{ {
if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password)) if (!String.IsNullOrEmpty(_uid) && !String.IsNullOrEmpty(_pwd))
{ {
connectionString += $"User ID={userId};Password={password};"; connectionString += $"User ID={_uid};Password={_pwd};";
} }
else else
{ {
@ -91,5 +73,4 @@
return connectionString; return connectionString;
} }
} }

View File

@ -1,39 +1,23 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<Installer> Localizer
@{ <div class="row mb-1 align-items-center">
foreach (var field in _connectionStringFields) <Label Class="col-sm-3" For="server" HelpText="Enter the file name to use for the database" ResourceKey="Server">File Name:</Label>
{ <div class="col-sm-9">
var fieldId = field.Name.ToLowerInvariant(); <input id="server" type="text" class="form-control" @bind="@_server" />
field.Value = field.Value.Replace("{{Date}}", DateTime.UtcNow.ToString("yyyyMMddHHmm")); </div>
</div>
<tr>
<td>
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label>
</td>
<td>
<input id="@fieldId" type="text" class="form-control" @bind="@field.Value" />
</td>
</tr>
}
}
@code { @code {
private readonly List<ConnectionStringField> _connectionStringFields = new() private string _server = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm") + ".db";
{
new() {Name = "Server", FriendlyName = "File Name", Value = "Oqtane-{{Date}}.db", HelpText="Enter the file name to use for the database"}
};
public string GetConnectionString() public string GetConnectionString()
{ {
var connectionstring = String.Empty; var connectionstring = String.Empty;
var server = _connectionStringFields[0].Value; if (!String.IsNullOrEmpty(_server))
if (!String.IsNullOrEmpty(server))
{ {
connectionstring = $"Data Source={server};"; connectionstring = $"Data Source={_server};";
} }
return connectionstring; return connectionstring;

View File

@ -20,14 +20,11 @@
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col text-center"> <div class="col text-center">
<h2>@Localizer["DatabaseConfig"]</h2><br /> <h2>@Localizer["DatabaseConfig"]</h2><br />
<table class="form-group" cellpadding="4" cellspacing="4" style="margin: auto;"> <div class="container">
<tbody> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="databasetype" HelpText="Select the type of database you wish to use" ResourceKey="DatabaseType">Database:</Label>
<td> <div class="col-sm-9">
<Label For="databasetype" HelpText="Select the type of database you wish to create" ResourceKey="DatabaseType">Database Type:</Label> <select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
</td>
<td>
<select id="databasetype" class="custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
@if (_databases != null) @if (_databases != null)
{ {
foreach (var database in _databases) foreach (var database in _databases)
@ -43,55 +40,44 @@
} }
} }
</select> </select>
</td> </div>
</tr> </div>
@{ @{
if (_databaseConfigType != null) if (_databaseConfigType != null)
{ {
@DatabaseConfigComponent; @DatabaseConfigComponent;
} }
} }
</tbody> </div>
</table>
</div> </div>
<div class="col text-center"> <div class="col text-center">
<h2>@Localizer["ApplicationAdmin"]</h2><br /> <h2>@Localizer["ApplicationAdmin"]</h2><br />
<table class="form-group" cellpadding="4" cellspacing="4" style="margin: auto;"> <div class="container">
<tbody> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="username" HelpText="Provide a username for the primary user accountt" ResourceKey="Username">Username:</Label>
<td> <div class="col-sm-9">
<Label For="username" HelpText="The username of the host user account ( this is not customizable )" ResourceKey="Username">Username:</Label> <input id="username" type="text" class="form-control" @bind="@_hostUsername" />
</td> </div>
<td> </div>
<input id="username" type="text" class="form-control" @bind="@_hostUsername" readonly /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
</tr> <div class="col-sm-9">
<tr>
<td>
<Label For="password" HelpText="Provide the password for the host user account" ResourceKey="Password">Password:</Label>
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@_hostPassword" /> <input id="password" type="password" class="form-control" @bind="@_hostPassword" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
<Label For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label> <div class="col-sm-9">
</td>
<td>
<input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" /> <input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label>
<Label For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label> <div class="col-sm-9">
</td>
<td>
<input type="text" class="form-control" @bind="@_hostEmail" /> <input type="text" class="form-control" @bind="@_hostEmail" />
</td> </div>
</tr> </div>
</tbody> </div>
</table>
</div> </div>
</div> </div>
<hr class="app-rule" /> <hr class="app-rule" />
@ -116,7 +102,7 @@
private object _databaseConfig; private object _databaseConfig;
private RenderFragment DatabaseConfigComponent { get; set; } private RenderFragment DatabaseConfigComponent { get; set; }
private string _hostUsername = UserNames.Host; private string _hostUsername = string.Empty;
private string _hostPassword = string.Empty; private string _hostPassword = string.Empty;
private string _confirmPassword = string.Empty; private string _confirmPassword = string.Empty;
private string _hostEmail = string.Empty; private string _hostEmail = string.Empty;
@ -164,7 +150,8 @@
if (firstRender) if (firstRender)
{ {
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.IncludeLink("app-stylesheet", "stylesheet", "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css", "text/css", "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T", "anonymous", ""); await interop.IncludeLink("", "stylesheet", "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", "text/css", "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC", "anonymous", "");
await interop.IncludeScript("", "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM", "anonymous", "", "head", "");
} }
} }
@ -176,7 +163,7 @@
connectionString = databaseConfigControl.GetConnectionString(); connectionString = databaseConfigControl.GetConnectionString();
} }
if (connectionString != "" && _hostUsername != "" && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && _hostEmail != "" && _hostEmail.Contains("@")) if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && _hostPassword.Length >= 6 && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
{ {
_loadingDisplay = ""; _loadingDisplay = "";
StateHasChanged(); StateHasChanged();
@ -190,9 +177,10 @@
DatabaseType = database.DBType, DatabaseType = database.DBType,
ConnectionString = connectionString, ConnectionString = connectionString,
Aliases = uri.Authority, Aliases = uri.Authority,
HostEmail = _hostEmail, HostUsername = _hostUsername,
HostPassword = _hostPassword, HostPassword = _hostPassword,
HostName = UserNames.Host, HostEmail = _hostEmail,
HostName = _hostUsername,
TenantName = TenantNames.Master, TenantName = TenantNames.Master,
IsNewTenant = true, IsNewTenant = true,
SiteName = Constants.DefaultSite, SiteName = Constants.DefaultSite,
@ -215,5 +203,4 @@
_message = Localizer["Message.Require.DbInfo"]; _message = Localizer["Message.Require.DbInfo"];
} }
} }
} }

View File

@ -9,55 +9,60 @@
<TabStrip> <TabStrip>
<TabPanel Name="Upload" Heading="Upload Files" ResourceKey="UploadFiles"> <TabPanel Name="Upload" Heading="Upload Files" ResourceKey="UploadFiles">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="upload" HelpText="Upload the file you want" ResourceKey="Upload">Upload: </Label>
<Label For="upload" HelpText="Upload the file you want" ResourceKey="Upload">Upload: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" /> <FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" />
</td> </div>
</tr> </div>
</table> </div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel> </TabPanel>
<TabPanel Name="Download" Heading="Download Files" ResourceKey="DownloadFiles"> <TabPanel Name="Download" Heading="Download Files" ResourceKey="DownloadFiles">
@if (_folders != null) @if (_folders != null)
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td> <div class="row mb-1 align-items-center">
<Label For="url" HelpText="Enter the url of the file you wish to download" ResourceKey="Url">Url: </Label> <Label Class="col-sm-3" For="url" HelpText="Enter the url of the file you wish to download" ResourceKey="Url">Url: </Label>
</td> <div class="col-sm-9">
<td> <input id="url" class="form-control" @bind="@_url" required />
<input id="url" class="form-control" @bind="@url" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="folder" HelpText="Select the folder to save the file in" ResourceKey="Folder">Folder: </Label>
<td> <div class="col-sm-9">
<Label For="folder" HelpText="Select the folder to save the file in" ResourceKey="Folder">Folder: </Label> <select id="folder" class="form-select" @bind="@_folderId" required>
</td>
<td>
<select id="folder" class="form-select" @bind="@_folderId">
<option value="-1">&lt;@Localizer["Folder.Select"]&gt;</option> <option value="-1">&lt;@Localizer["Folder.Select"]&gt;</option>
@foreach (Folder folder in _folders) @foreach (Folder folder in _folders)
{ {
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
} }
</select> </select>
</td> </div>
</tr> </div>
</table> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the name of the file being downloaded" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" />
</div>
</div>
</div>
<button type="button" class="btn btn-success" @onclick="Download">@SharedLocalizer["Download"]</button> <button type="button" class="btn btn-success" @onclick="Download">@SharedLocalizer["Download"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</form>
} }
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
@code { @code {
private string url = string.Empty; private ElementReference form;
private bool validated = false;
private string _url = string.Empty;
private List<Folder> _folders; private List<Folder> _folders;
private int _folderId = -1; private int _folderId = -1;
private string _name = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -73,22 +78,28 @@
private async Task Download() private async Task Download()
{ {
if (url == string.Empty || _folderId == -1) validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
if (_url == string.Empty || _folderId == -1)
{ {
AddModuleMessage(Localizer["Message.Required.UrlFolder"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.UrlFolder"], MessageType.Warning);
return; return;
} }
var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); if (string.IsNullOrEmpty(_name))
{
_name = _url.Substring(_url.LastIndexOf("/", StringComparison.Ordinal) + 1);
}
if (!Constants.UploadableFiles.Split(',') if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
{ {
AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning); AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning);
return; return;
} }
if (!filename.IsPathOrFileValid()) if (!_name.IsPathOrFileValid())
{ {
AddModuleMessage(Localizer["Message.Required.UrlName"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.UrlName"], MessageType.Warning);
return; return;
@ -96,14 +107,19 @@
try try
{ {
await FileService.UploadFileAsync(url, _folderId); await FileService.UploadFileAsync(_url, _folderId, _name);
await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); await logger.LogInformation("File Downloaded Successfully From Url {Url}", _url);
AddModuleMessage(Localizer["Success.Download.File"], MessageType.Success); AddModuleMessage(Localizer["Success.Download.File"], MessageType.Success);
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", _url, ex.Message);
AddModuleMessage(Localizer["Error.Download.InvalidUrl"], MessageType.Error); AddModuleMessage(Localizer["Error.Download.InvalidUrl"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -8,49 +8,54 @@
@if (_folders != null) @if (_folders != null)
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label for="name" HelpText="The name of the file" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="The name of the file" ResourceKey="Name">Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="name" class="form-control" @bind="@_name" maxlength="50" required />
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="parent" HelpText="The folder where the file is located" ResourceKey="Folder">Folder: </Label>
<td> <div class="col-sm-9">
<Label For="parent" HelpText="The folder where the file is located" ResourceKey="Folder">Folder: </Label> <select id="parent" class="form-select" @bind="@_folderId" required>
</td>
<td>
<select id="parent" class="form-select" @bind="@_folderId">
@foreach (Folder folder in _folders) @foreach (Folder folder in _folders)
{ {
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="description" HelpText="A description of the file. This can be used as a caption for image files." ResourceKey="Description">Description: </Label>
<Label for="size" HelpText="The size of the file (in bytes)" ResourceKey="Size">Size: </Label> <div class="col-sm-9">
</td> <input id="description" class="form-control" @bind="@_description" />
<td> </div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="size" HelpText="The size of the file (in bytes)" ResourceKey="Size">Size: </Label>
<div class="col-sm-9">
<input id="size" class="form-control" @bind="@_size" readonly /> <input id="size" class="form-control" @bind="@_size" readonly />
</td> </div>
</tr> </div>
</table> </div>
<button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br /> <br />
<br /> <br />
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo> <AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
</form>
} }
@code { @code {
private ElementReference form;
private bool validated = false;
private int _fileId = -1; private int _fileId = -1;
private string _name; private string _name;
private List<Folder> _folders; private List<Folder> _folders;
private int _folderId = -1; private int _folderId = -1;
private string _description = string.Empty;
private int _size; private int _size;
private string _createdBy; private string _createdBy;
private DateTime _createdOn; private DateTime _createdOn;
@ -72,6 +77,7 @@
{ {
_name = file.Name; _name = file.Name;
_folderId = file.FolderId; _folderId = file.FolderId;
_description = file.Description;
_size = file.Size; _size = file.Size;
_createdBy = file.CreatedBy; _createdBy = file.CreatedBy;
_createdOn = file.CreatedOn; _createdOn = file.CreatedOn;
@ -87,6 +93,10 @@
} }
private async Task SaveFile() private async Task SaveFile()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -95,6 +105,7 @@
File file = await FileService.GetFileAsync(_fileId); File file = await FileService.GetFileAsync(_fileId);
file.Name = _name; file.Name = _name;
file.FolderId = _folderId; file.FolderId = _folderId;
file.Description = _description;
file = await FileService.UpdateFileAsync(file); file = await FileService.UpdateFileAsync(file);
await logger.LogInformation("File Saved {File}", file); await logger.LogInformation("File Saved {File}", file);
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
@ -110,4 +121,9 @@
AddModuleMessage(Localizer["Error.File.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.File.Save"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -8,13 +8,12 @@
@if (_folders != null) @if (_folders != null)
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label> <Label Class="col-sm-3" For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
</td> <div class="col-sm-9">
<td> <select id="parent" class="form-select" @bind="@_parentId" required>
<select id="parent" class="form-select" @bind="@_parentId">
@if (PageState.QueryString.ContainsKey("id")) @if (PageState.QueryString.ContainsKey("id"))
{ {
<option value="-1">&lt;@Localizer["NoParent"]&gt;</option> <option value="-1">&lt;@Localizer["NoParent"]&gt;</option>
@ -24,41 +23,52 @@
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
<Label for="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label> <div class="col-sm-9">
</td> <input id="name" class="form-control" @bind="@_name" maxlength="256" required />
<td> </div>
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="type" HelpText="Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users" ResourceKey="Type">Type: </Label>
<tr> <div class="col-sm-9">
<td>
<Label for="type" HelpText="Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users" ResourceKey="Name">Type: </Label>
</td>
<td>
@if (PageState.QueryString.ContainsKey("id")) @if (PageState.QueryString.ContainsKey("id"))
{ {
<input id="type" class="form-control" readonly @bind="@_type" /> <input id="type" class="form-control" readonly @bind="@_type" />
} }
else else
{ {
<select id="type" class="form-select" @bind="@_type"> <select id="type" class="form-select" @bind="@_type" required>
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option> <option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option> <option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
</select> </select>
} }
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td colspan="2" align="center"> <Label Class="col-sm-3" For="imagesizes" HelpText="Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)" ResourceKey="ImageSizes">Image Sizes: </Label>
<Label For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label> <div class="col-sm-9">
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="capacity" HelpText="Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited." ResourceKey="Capacity">Capacity: </Label>
<div class="col-sm-9">
<input id="capacity" class="form-control" @bind="@_capacity" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-12">
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" />
</td>
</tr> </div>
</table> </div>
</div>
</form>
@if (!_isSystem) @if (!_isSystem)
{ {
<button type="button" class="btn btn-success" @onclick="SaveFolder">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveFolder">@SharedLocalizer["Save"]</button>
@ -79,11 +89,15 @@
} }
@code { @code {
private ElementReference form;
private bool validated = false;
private List<Folder> _folders; private List<Folder> _folders;
private int _folderId = -1; private int _folderId = -1;
private int _parentId = -1; private int _parentId = -1;
private string _name; private string _name;
private string _type = FolderTypes.Private; private string _type = FolderTypes.Private;
private string _imagesizes = string.Empty;
private string _capacity = "0";
private bool _isSystem; private bool _isSystem;
private string _permissions = string.Empty; private string _permissions = string.Empty;
private string _createdBy; private string _createdBy;
@ -114,6 +128,8 @@
_parentId = folder.ParentId ?? -1; _parentId = folder.ParentId ?? -1;
_name = folder.Name; _name = folder.Name;
_type = folder.Type; _type = folder.Type;
_imagesizes = folder.ImageSizes;
_capacity = folder.Capacity.ToString();
_isSystem = folder.IsSystem; _isSystem = folder.IsSystem;
_permissions = folder.Permissions; _permissions = folder.Permissions;
_createdBy = folder.CreatedBy; _createdBy = folder.CreatedBy;
@ -125,7 +141,6 @@
else else
{ {
_parentId = _folders[0].FolderId; _parentId = _folders[0].FolderId;
_permissions = string.Empty;
} }
} }
catch (Exception ex) catch (Exception ex)
@ -136,6 +151,10 @@
} }
private async Task SaveFolder() private async Task SaveFolder()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_name == string.Empty || _parentId == -1) if (_name == string.Empty || _parentId == -1)
{ {
@ -174,6 +193,8 @@
folder.Name = _name; folder.Name = _name;
folder.Type = _type; folder.Type = _type;
folder.ImageSizes = _imagesizes;
folder.Capacity = int.Parse(_capacity);
folder.IsSystem = _isSystem; folder.IsSystem = _isSystem;
folder.Permissions = _permissionGrid.GetPermissions(); folder.Permissions = _permissionGrid.GetPermissions();
@ -203,6 +224,11 @@
AddModuleMessage(Localizer["Error.Folder.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Folder.Save"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task DeleteFolder() private async Task DeleteFolder()
{ {

View File

@ -8,26 +8,26 @@
@if (_files != null) @if (_files != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <div class="col-sm-2">
<label class="control-label">@Localizer["Folder"] </label> <label class="control-label">@Localizer["Folder"] </label>
</td> </div>
<td> <div class="col-sm-6">
<select class="form-select" @onchange="(e => FolderChanged(e))"> <select class="form-select" @onchange="(e => FolderChanged(e))">
@foreach (Folder folder in _folders) @foreach (Folder folder in _folders)
{ {
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
} }
</select> </select>
</td> </div>
<td> <div class="col-sm-4">
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />&nbsp; <ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />&nbsp;
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />&nbsp; <ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />&nbsp;
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" /> <ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
</td> </div>
</tr> </div>
</table> </div>
<Pager Items="@_files"> <Pager Items="@_files">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>

View File

@ -5,98 +5,111 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="name" HelpText="Enter the job name" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="Enter the job name" ResourceKey="Name">Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="name" class="form-control" @bind="@_name" maxlength="200" required />
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="type" HelpText="The fully qualified job type name" ResourceKey="Type">Type: </Label>
<td> <div class="col-sm-9">
<Label For="type" HelpText="The fully qualified job type name" ResourceKey="Type">Type: </Label>
</td>
<td>
<input id="type" class="form-control" @bind="@_jobType" readonly /> <input id="type" class="form-control" @bind="@_jobType" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="enabled" HelpText="Select whether you want the job enabled or not" ResourceKey="Enabled">Enabled? </Label>
<Label For="enabled" HelpText="Select whether you want the job enabled or not" ResourceKey="Enabled">Enabled? </Label> <div class="col-sm-9">
</td> <select id="enabled" class="form-select" @bind="@_isEnabled" required>
<td>
<select id="enabled" class="form-select" @bind="@_isEnabled">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label>
<Label For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label> <div class="col-sm-9">
</td> <input id="runs-every" class="form-control" @bind="@_interval" maxlength="4" required />
<td> <select id="runs-every" class="form-select" @bind="@_frequency" required>
<input id="runs-every" class="form-control" @bind="@_interval" />
<select id="runs-every" class="form-select" @bind="@_frequency">
<option value="m">@Localizer["Minute(s)"]</option> <option value="m">@Localizer["Minute(s)"]</option>
<option value="H">@Localizer["Hour(s)"]</option> <option value="H">@Localizer["Hour(s)"]</option>
<option value="d">@Localizer["Day(s)"]</option> <option value="d">@Localizer["Day(s)"]</option>
<option value="M">@Localizer["Month(s)"]</option> <option value="M">@Localizer["Month(s)"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
<Label For="starting" HelpText="What time do you want the job to start" ResourceKey="Starting">Starting: </Label> <div class="col-sm-9">
</td> <input id="retention" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
<td> </div>
<input id="starting" class="form-control" @bind="@_startDate" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="starting" HelpText="Optionally enter the date and time when this job should start executing" ResourceKey="Starting">Starting: </Label>
<tr> <div class="col-sm-9">
<td> <div class="row">
<Label For="ending" HelpText="When do you want the job to end" ResourceKey="Ending">Ending: </Label> <div class="col">
</td> <input id="starting" type="date" class="form-control" @bind="@_startDate" />
<td> </div>
<input id="ending" class="form-control" @bind="@_endDate" /> <div class="col">
</td> <input id="starting" type="text" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
</tr> </div>
<tr> </div>
<td> </div>
<Label For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label> </div>
</td> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="ending" HelpText="Optionally enter the date and time when this job should stop executing" ResourceKey="Ending">Ending: </Label>
<input id="retention" class="form-control" @bind="@_retentionHistory" /> <div class="col-sm-9">
</td> <div class="row">
</tr> <div class="col">
<tr> <input id="ending" type="date" class="form-control" @bind="@_endDate" />
<td> </div>
<Label For="next" HelpText="Next execution for this job." ResourceKey="NextExecution">Next Execution: </Label> <div class="col">
</td> <input id="ending" type="text" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
<td> </div>
<input id="next" class="form-control" @bind="@_nextExecution" /> </div>
</td> </div>
</tr> </div>
</table> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="next" HelpText="Optionally modify the date and time when this job should execute next" ResourceKey="NextExecution">Next Execution: </Label>
<div class="col-sm-9">
<div class="row">
<div class="col">
<input id="next" type="date" class="form-control" @bind="@_nextDate" />
</div>
<div class="col">
<input id="next" type="text" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
</div>
</div>
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveJob">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveJob">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br /> <br />
<br /> <br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo> <AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
</form>
@code { @code {
private ElementReference form;
private bool validated = false;
private int _jobId; private int _jobId;
private string _name = string.Empty; private string _name = string.Empty;
private string _jobType = string.Empty; private string _jobType = string.Empty;
private string _isEnabled = "True"; private string _isEnabled = "True";
private string _interval = string.Empty; private string _interval = string.Empty;
private string _frequency = string.Empty; private string _frequency = string.Empty;
private string _startDate = string.Empty; private DateTime? _startDate = null;
private string _endDate = string.Empty; private string _startTime = string.Empty;
private DateTime? _endDate = null;
private string _endTime = string.Empty;
private string _retentionHistory = string.Empty; private string _retentionHistory = string.Empty;
private string _nextExecution = string.Empty; private DateTime? _nextDate = null;
private string _nextTime = string.Empty;
private string createdby; private string createdby;
private DateTime createdon; private DateTime createdon;
private string modifiedby; private string modifiedby;
@ -117,10 +130,22 @@
_isEnabled = job.IsEnabled.ToString(); _isEnabled = job.IsEnabled.ToString();
_interval = job.Interval.ToString(); _interval = job.Interval.ToString();
_frequency = job.Frequency; _frequency = job.Frequency;
_startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty; _startDate = job.StartDate;
_endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty; if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
{
_startTime = job.StartDate.Value.ToString("HH:mm");
}
_endDate = job.EndDate;
if (job.EndDate != null && job.EndDate.Value.TimeOfDay.TotalSeconds != 0)
{
_endTime = job.EndDate.Value.ToString("HH:mm");
}
_retentionHistory = job.RetentionHistory.ToString(); _retentionHistory = job.RetentionHistory.ToString();
_nextExecution = job.NextExecution.ToString(); _nextDate = job.NextExecution;
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
{
_nextTime = job.NextExecution.Value.ToString("HH:mm");
}
createdby = job.CreatedBy; createdby = job.CreatedBy;
createdon = job.CreatedOn; createdon = job.CreatedOn;
modifiedby = job.ModifiedBy; modifiedby = job.ModifiedBy;
@ -136,7 +161,9 @@
private async Task SaveJob() private async Task SaveJob()
{ {
if (_name != string.Empty && !string.IsNullOrEmpty(_jobType) && _frequency != string.Empty && _interval != string.Empty && _retentionHistory != string.Empty) validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
var job = await JobService.GetJobAsync(_jobId); var job = await JobService.GetJobAsync(_jobId);
job.Name = _name; job.Name = _name;
@ -144,35 +171,34 @@
job.IsEnabled = Boolean.Parse(_isEnabled); job.IsEnabled = Boolean.Parse(_isEnabled);
job.Frequency = _frequency; job.Frequency = _frequency;
job.Interval = int.Parse(_interval); job.Interval = int.Parse(_interval);
job.StartDate = _startDate;
if (_startDate == string.Empty) if (job.StartDate != null)
{ {
job.StartDate = null; job.StartDate = job.StartDate.Value.Date;
} if (!string.IsNullOrEmpty(_startTime))
else
{ {
job.StartDate = DateTime.Parse(_startDate); job.StartDate = DateTime.Parse(job.StartDate.Value.ToShortDateString() + " " + _startTime);
} }
}
if (_endDate == string.Empty) job.EndDate = _endDate;
if (job.EndDate != null)
{ {
job.EndDate = null; job.EndDate = job.EndDate.Value.Date;
} if (!string.IsNullOrEmpty(_endTime))
else
{ {
job.EndDate = DateTime.Parse(_endDate); job.EndDate = DateTime.Parse(job.EndDate.Value.ToShortDateString() + " " + _endTime);
} }
if (_nextExecution == string.Empty)
{
job.NextExecution = null;
} }
else
{
job.NextExecution = DateTime.Parse(_nextExecution);
}
job.RetentionHistory = int.Parse(_retentionHistory); job.RetentionHistory = int.Parse(_retentionHistory);
job.NextExecution = _nextDate;
if (job.NextExecution != null)
{
job.NextExecution = job.NextExecution.Value.Date;
if (!string.IsNullOrEmpty(_nextTime))
{
job.NextExecution = DateTime.Parse(job.NextExecution.Value.ToShortDateString() + " " + _nextTime);
}
}
try try
{ {
@ -191,5 +217,4 @@
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
} }
} }
} }

View File

@ -23,50 +23,48 @@ else
} }
else else
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label> <Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
</td> <div class="col-sm-9">
<td> <select id="_code" class="form-select" @bind="@_code" required>
<select id="_code" class="form-select" @bind="@_code">
@foreach (var culture in _availableCultures) @foreach (var culture in _availableCultures)
{ {
<option value="@culture.Name">@culture.DisplayName</option> <option value="@culture.Name">@culture.DisplayName</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
<Label For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label> <div class="col-sm-9">
</td> <select id="default" class="form-select" @bind="@_isDefault" required>
<td>
<select id="default" class="form-select" @bind="@_isDefault">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
</form>
} }
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel> </TabPanel>
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host"> <TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
<ModuleMessage Type="MessageType.Info" Message="Download one or more translations from the list below. Once you are ready click Install to complete the installation."></ModuleMessage> <div class="row justify-content-center mb-3">
<div class="col-sm-6">
<table class="table table-borderless" style=" margin: auto; width: 50% !important;"> <div class="input-group">
<tr> <select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
<td> <option value="free">@SharedLocalizer["Free"]</option>
<option value="paid">@SharedLocalizer["Paid"]</option>
</select>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" /> <input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
</td> <button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<td>
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>&nbsp;
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button> <button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
</td> </div>
</tr> </div>
</table> </div>
@if (_packages != null) @if (_packages != null)
{ {
@ -77,10 +75,26 @@ else
<td> <td>
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;by:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br /> <h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;by:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br /> @(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Source"]: <strong>@context.PackageUrl</strong> <strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td> </td>
<td style="vertical-align: middle;"> <td style="width: 1px; vertical-align: middle;">
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadLanguage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button> @if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
}
else
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td> </td>
</Row> </Row>
</Pager> </Pager>
@ -97,30 +111,69 @@ else
} }
</TabPanel> </TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host"> <TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Language: </Label>
<Label HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Language: </Label> <div class="col-sm-9">
</td> <FileManager Filter="nupkg" ShowFiles="true" Folder="Packages" UploadMultiple="true" />
<td> </div>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="true" /> </div>
</td> </div>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button> <button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
} }
@if (_productname != "")
{
<div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
</div>
<div class="modal-body">
<p style="height: 200px; overflow-y: scroll;">
<h3>@_productname</h3>
@if (!string.IsNullOrEmpty(_license))
{
@((MarkupString)_license)
}
else
{
@SharedLocalizer["License Not Specified"]
}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
@code { @code {
private ElementReference form;
private bool validated = false;
private string _code = string.Empty; private string _code = string.Empty;
private string _isDefault = "False"; private string _isDefault = "False";
private string _message; private string _message;
private IEnumerable<Culture> _supportedCultures; private IEnumerable<Culture> _supportedCultures;
private IEnumerable<Culture> _availableCultures; private IEnumerable<Culture> _availableCultures;
private List<Package> _packages; private List<Package> _packages;
private string _price = "free";
private string _search = ""; private string _search = "";
private string _productname = "";
private string _license = "";
private string _packageid = "";
private string _version = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -146,7 +199,22 @@ else
private async Task LoadTranslations() private async Task LoadTranslations()
{ {
_packages = await PackageService.GetPackagesAsync("translation", _search); _packages = await PackageService.GetPackagesAsync("translation", _search, _price, "");
}
private async void PriceChanged(ChangeEventArgs e)
{
try
{
_price = (string)e.Value;
_search = "";
await LoadTranslations();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On PriceChanged");
}
} }
private async Task Search() private async Task Search()
@ -175,6 +243,10 @@ else
} }
private async Task SaveLanguage() private async Task SaveLanguage()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
var language = new Language var language = new Language
{ {
@ -203,6 +275,59 @@ else
AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error); AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private void HideModal()
{
_productname = "";
_license = "";
StateHasChanged();
}
private async Task GetPackage(string packageid, string version)
{
try
{
var package = await PackageService.GetPackageAsync(packageid, version);
if (package != null)
{
_productname = package.Name;
if (!string.IsNullOrEmpty(package.License))
{
_license = package.License.Replace("\n", "<br />");
}
_packageid = package.PackageId;
_version = package.Version;
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
private async Task DownloadPackage()
{
try
{
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages");
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _packageid, _version);
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
}
}
private async Task InstallLanguages() private async Task InstallLanguages()
{ {
@ -217,22 +342,6 @@ else
} }
} }
private async Task DownloadLanguage(string packageid, string version)
{
try
{
await PackageService.DownloadPackageAsync(packageid, version, "Packages");
await logger.LogInformation("Language Paclage {Name} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
}
}
private async Task SetCultureAsync(string culture) private async Task SetCultureAsync(string culture)
{ {
if (culture != CultureInfo.CurrentUICulture.Name) if (culture != CultureInfo.CurrentUICulture.Name)

View File

@ -84,10 +84,12 @@
if (user != null) if (user != null)
{ {
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
_message = Localizer["Success.Account.Verified"]; _message = Localizer["Success.Account.Verified"];
} }
else else
{ {
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
_message = Localizer["Message.Account.NotVerfied"]; _message = Localizer["Message.Account.NotVerfied"];
_type = MessageType.Warning; _type = MessageType.Warning;
} }
@ -97,10 +99,13 @@
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender) if (firstRender)
{
if(PageState.User == null)
{ {
await username.FocusAsync(); await username.FocusAsync();
} }
} }
}
private async Task Login() private async Task Login()
{ {
@ -118,7 +123,7 @@
if (user.IsAuthenticated) if (user.IsAuthenticated)
{ {
await logger.LogInformation("Login Successful For Username {Username}", _username); await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
// server-side Blazor needs to post to the Login page so that the cookies are set correctly // server-side Blazor needs to post to the Login page so that the cookies are set correctly
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl }; var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/"); string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
@ -126,7 +131,7 @@
} }
else else
{ {
await logger.LogInformation("Login Failed For Username {Username}", _username); await logger.LogError(LogFunction.Security, "Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error); AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
} }
} }
@ -140,14 +145,14 @@
user = await UserService.LoginUserAsync(user, true, _remember); user = await UserService.LoginUserAsync(user, true, _remember);
if (user.IsAuthenticated) if (user.IsAuthenticated)
{ {
await logger.LogInformation("Login Successful For Username {Username}", _username); await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged(); authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true)); NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
} }
else else
{ {
await logger.LogInformation("Login Failed For Username {Username}", _username); await logger.LogError(LogFunction.Security, "Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error); AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
} }
} }
@ -171,6 +176,7 @@
if (user != null) if (user != null)
{ {
await UserService.ForgotPasswordAsync(user); await UserService.ForgotPasswordAsync(user);
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
_message = "Please Check The Email Address Associated To Your User Account For A Password Reset Notification"; _message = "Please Check The Email Address Associated To Your User Account For A Password Reset Notification";
} }
else else

View File

@ -9,132 +9,172 @@
@inject IStringLocalizer<Detail> Localizer @inject IStringLocalizer<Detail> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%">
<Label For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label> <div class="col-sm-9">
</td>
<td> </div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
<div class="col-sm-9">
<input id="dateTime" class="form-control" @bind="@_logDate" readonly /> <input id="dateTime" class="form-control" @bind="@_logDate" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
<Label For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label> <div class="col-sm-9">
</td>
<td>
<input id="level" class="form-control" @bind="@_level" readonly /> <input id="level" class="form-control" @bind="@_level" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
<Label For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label> <div class="col-sm-9">
</td>
<td>
<input id="feature" class="form-control" @bind="@_feature" readonly /> <input id="feature" class="form-control" @bind="@_feature" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
<Label For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label> <div class="col-sm-9">
</td>
<td>
<input id="function" class="form-control" @bind="@_function" readonly /> <input id="function" class="form-control" @bind="@_function" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
<Label For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label> <div class="col-sm-9">
</td>
<td>
<input id="category" class="form-control" @bind="@_category" readonly /> <input id="category" class="form-control" @bind="@_category" readonly />
</td> </div>
</tr> </div>
@if (_pageName != string.Empty) @if (_pageName != string.Empty)
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
<Label For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label> <div class="col-sm-9">
</td>
<td>
<input id="page" class="form-control" @bind="@_pageName" readonly /> <input id="page" class="form-control" @bind="@_pageName" readonly />
</td> </div>
</tr> </div>
} }
@if (_moduleTitle != string.Empty) @if (_moduleTitle != string.Empty)
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
<Label For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label> <div class="col-sm-9">
</td>
<td>
<input id="module" class="form-control" @bind="@_moduleTitle" readonly /> <input id="module" class="form-control" @bind="@_moduleTitle" readonly />
</td> </div>
</tr> </div>
} }
@if (_username != string.Empty) @if (_username != string.Empty)
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
<Label For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label> <div class="col-sm-9">
</td>
<td>
<input id="user" class="form-control" @bind="@_username" readonly /> <input id="user" class="form-control" @bind="@_username" readonly />
</td> </div>
</tr> </div>
} }
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
<Label For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label> <div class="col-sm-9">
</td>
<td>
<input id="url" class="form-control" @bind="@_url" readonly /> <input id="url" class="form-control" @bind="@_url" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
<Label For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label> <div class="col-sm-9">
</td>
<td>
<input id="template" class="form-control" @bind="@_template" readonly /> <input id="template" class="form-control" @bind="@_template" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
<Label For="message" HelpText="The message that the system generated" class="control-label" ResourceKey="Message">Message: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea> <textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
</td> </div>
</tr> </div>
@if (!string.IsNullOrEmpty(_exception)) @if (!string.IsNullOrEmpty(_exception))
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
<Label For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea> <textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
</td> </div>
</tr> </div>
} }
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
<Label For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea> <textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
<Label For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label> <div class="col-sm-9">
</td>
<td>
<input id="server" class="form-control" @bind="@_server" readonly /> <input id="server" class="form-control" @bind="@_server" readonly />
</td> </div>
</tr> </div>
</table>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {

View File

@ -10,9 +10,9 @@
} }
else else
{ {
<table class="table table-borderless"> <div class="container g-0">
<tr> <div class="row mb-1 align-items-center">
<td> <div class="col-sm-4">
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br /> <Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
<select id="level" class="form-select" @onchange="(e => LevelChanged(e))"> <select id="level" class="form-select" @onchange="(e => LevelChanged(e))">
<option value="-">&lt;@Localizer["AllLevels"]&gt;</option> <option value="-">&lt;@Localizer["AllLevels"]&gt;</option>
@ -23,8 +23,8 @@ else
<option value="Error">@Localizer["Error"]</option> <option value="Error">@Localizer["Error"]</option>
<option value="Critical">@Localizer["Critical"]</option> <option value="Critical">@Localizer["Critical"]</option>
</select> </select>
</td> </div>
<td> <div class="col-sm-4">
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br /> <Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
<select id="function" class="form-select" @onchange="(e => FunctionChanged(e))"> <select id="function" class="form-select" @onchange="(e => FunctionChanged(e))">
<option value="-">&lt;@Localizer["AllFunctions"]&gt;</option> <option value="-">&lt;@Localizer["AllFunctions"]&gt;</option>
@ -35,17 +35,17 @@ else
<option value="Security">@Localizer["Security"]</option> <option value="Security">@Localizer["Security"]</option>
<option value="Other">@Localizer["Other"]</option> <option value="Other">@Localizer["Other"]</option>
</select> </select>
</td> </div>
<td> <div class="col-sm-4">
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br /> <Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
<select id="rows" class="form-select" @onchange="(e => RowsChanged(e))"> <select id="rows" class="form-select" @onchange="(e => RowsChanged(e))">
<option value="10">10</option> <option value="10">10</option>
<option value="50">50</option> <option value="50">50</option>
<option value="100">100</option> <option value="100">100</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
@if (_logs.Any()) @if (_logs.Any())
{ {

View File

@ -10,51 +10,42 @@
@if (string.IsNullOrEmpty(_moduledefinitionname) && _templates != null) @if (string.IsNullOrEmpty(_moduledefinitionname) && _templates != null)
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label> <Label Class="col-sm-3" For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="owner" class="form-control" @bind="@_owner" required />
<input id="owner" class="form-control" @bind="@_owner" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
<td> <div class="col-sm-9">
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label> <input id="module" class="form-control" @bind="@_module" required />
</td> </div>
<td> </div>
<input id="module" class="form-control" @bind="@_module" /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
</tr> <div class="col-sm-9">
<tr>
<td>
<Label For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3" ></textarea> <textarea id="description" class="form-control" @bind="@_description" rows="3" ></textarea>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
<Label For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label> <div class="col-sm-9">
</td> <select id="template" class="form-select" @onchange="(e => TemplateChanged(e))" required>
<td>
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates) @foreach (Template template in _templates)
{ {
<option value="@template.Name">@template.Title</option> <option value="@template.Name">@template.Title</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label> <div class="col-sm-9">
</td> <select id="reference" class="form-select" @bind="@_reference" required>
<td>
<select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions) @foreach (string version in _versions)
{ {
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0) if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
@ -64,28 +55,30 @@
} }
<option value="local">@SharedLocalizer["LocalVersion"]</option> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</select> </select>
</td> </div>
</tr> </div>
@if (!string.IsNullOrEmpty(_location)) @if (!string.IsNullOrEmpty(_location))
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label> <div class="col-sm-9">
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly /> <input id="module" class="form-control" @bind="@_location" readonly />
</td> </div>
</tr> </div>
} }
</table> </div>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button> <button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button>
} }
else else
{ {
<button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button> <button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button>
</form>
} }
@code { @code {
private ElementReference form;
private bool validated = false;
private string _moduledefinitionname = string.Empty; private string _moduledefinitionname = string.Empty;
private string _owner = string.Empty; private string _owner = string.Empty;
private string _module = string.Empty; private string _module = string.Empty;
@ -124,9 +117,11 @@ else
private async Task CreateModule() private async Task CreateModule()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-") try
{ {
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference }; var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition); moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
@ -139,16 +134,16 @@ else
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
} }
else
{
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
}
}
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Creating Module"); await logger.LogError(ex, "Error Creating Module");
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task ActivateModule() private async Task ActivateModule()
{ {

View File

@ -9,19 +9,19 @@
<TabStrip> <TabStrip>
<TabPanel Name="Download" ResourceKey="Download"> <TabPanel Name="Download" ResourceKey="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more modules from the list below. Once you are ready click Install to complete the installation."></ModuleMessage> <div class="row justify-content-center mb-3">
<div class="col-sm-6">
<table class="table table-borderless" style="margin: auto; width: 50% !important;"> <div class="input-group">
<tr> <select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
<td> <option value="free">@SharedLocalizer["Free"]</option>
<option value="paid">@SharedLocalizer["Paid"]</option>
</select>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" /> <input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
</td> <button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<td>
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>&nbsp;
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button> <button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
</td> </div>
</tr> </div>
</table> </div>
@if (_packages != null) @if (_packages != null)
{ {
@ -32,10 +32,26 @@
<td> <td>
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;by:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br /> <h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;by:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br /> @(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Source"]: <strong>@context.PackageUrl</strong> <strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td> </td>
<td style="vertical-align: middle;"> <td style="width: 1px; vertical-align: middle;">
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadModule(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button> @if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
}
else
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td> </td>
</Row> </Row>
</Pager> </Pager>
@ -50,25 +66,61 @@
} }
</TabPanel> </TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload"> <TabPanel Name="Upload" ResourceKey="Upload">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label>
<Label HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="true" /> <FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="true" />
</td> </div>
</tr> </div>
</table> </div>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
@if (_productname != "")
{
<div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
</div>
<div class="modal-body">
<p style="height: 200px; overflow-y: scroll;">
<h3>@_productname</h3>
@if (!string.IsNullOrEmpty(_license))
{
@((MarkupString)_license)
}
else
{
@SharedLocalizer["License Not Specified"]
}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
<button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button> <button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {
private List<Package> _packages; private List<Package> _packages;
private string _price = "free";
private string _search = ""; private string _search = "";
private string _productname = "";
private string _license = "";
private string _packageid = "";
private string _version = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -88,7 +140,7 @@
private async Task LoadModuleDefinitions() private async Task LoadModuleDefinitions()
{ {
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_packages = await PackageService.GetPackagesAsync("module", _search); _packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
if (_packages != null) if (_packages != null)
{ {
@ -102,6 +154,21 @@
} }
} }
private async void PriceChanged(ChangeEventArgs e)
{
try
{
_price = (string)e.Value;
_search = "";
await LoadModuleDefinitions();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On PriceChanged");
}
}
private async Task Search() private async Task Search()
{ {
try try
@ -127,6 +194,55 @@
} }
} }
private void HideModal()
{
_productname = "";
_license = "";
StateHasChanged();
}
private async Task GetPackage(string packageid, string version)
{
try
{
var package = await PackageService.GetPackageAsync(packageid, version);
if (package != null)
{
_productname = package.Name;
if (!string.IsNullOrEmpty(package.License))
{
_license = package.License.Replace("\n", "<br />");
}
_packageid = package.PackageId;
_version = package.Version;
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
private async Task DownloadPackage()
{
try
{
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages");
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
_productname = "";
_license = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
private async Task InstallModules() private async Task InstallModules()
{ {
try try
@ -139,20 +255,4 @@
await logger.LogError(ex, "Error Installing Module"); await logger.LogError(ex, "Error Installing Module");
} }
} }
private async Task DownloadModule(string packageid, string version)
{
try
{
await PackageService.DownloadPackageAsync(packageid, version, "Packages");
await logger.LogInformation("Module {ModuleDefinitionName} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
}
}
} }

View File

@ -10,51 +10,42 @@
@if (_templates != null) @if (_templates != null)
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label> <Label Class="col-sm-3" For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="owner" class="form-control" @bind="@_owner" required />
<input id="owner" class="form-control" @bind="@_owner" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
<td> <div class="col-sm-9">
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label> <input id="module" class="form-control" @bind="@_module" required />
</td> </div>
<td> </div>
<input id="module" class="form-control" @bind="@_module" /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
</tr> <div class="col-sm-9">
<tr> <textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000" required></textarea>
<td> </div>
<Label For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label> </div>
</td> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea> <div class="col-sm-9">
</td> <select id="template" class="form-select" @onchange="(e => TemplateChanged(e))" required>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates) @foreach (Template template in _templates)
{ {
<option value="@template.Name">@template.Title</option> <option value="@template.Name">@template.Title</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label> <div class="col-sm-9">
</td> <select id="reference" class="form-select" @bind="@_reference" required>
<td>
<select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions) @foreach (string version in _versions)
{ {
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0) if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
@ -64,25 +55,26 @@
} }
<option value="local">@SharedLocalizer["LocalVersion"]</option> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</select> </select>
</td> </div>
</tr> </div>
@if (!string.IsNullOrEmpty(_location)) @if (!string.IsNullOrEmpty(_location))
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label> <div class="col-sm-9">
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly /> <input id="module" class="form-control" @bind="@_location" readonly />
</td> </div>
</tr> </div>
} }
</table> </div>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["CreateModule"]</button> <button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["CreateModule"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</form>
} }
@code { @code {
private ElementReference form;
private bool validated = false;
private string _owner = string.Empty; private string _owner = string.Empty;
private string _module = string.Empty; private string _module = string.Empty;
private string _description = string.Empty; private string _description = string.Empty;
@ -110,6 +102,10 @@
} }
private async Task CreateModule() private async Task CreateModule()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -130,6 +126,11 @@
await logger.LogError(ex, "Error Creating Module"); await logger.LogError(ex, "Error Creating Module");
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private bool IsValid(string name) private bool IsValid(string name)
{ {

View File

@ -7,101 +7,81 @@
<TabStrip> <TabStrip>
<TabPanel Name="Definition" ResourceKey="Definition"> <TabPanel Name="Definition" ResourceKey="Definition">
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="name" class="form-control" @bind="@_name" maxlength="200" required />
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="description" HelpText="The description of the module" ResourceKey="Description">Description: </Label>
<td> <div class="col-sm-9">
<Label For="description" HelpText="The description of the module" ResourceKey="Description">Description: </Label> <textarea id="description" class="form-control" @bind="@_description" rows="2" maxlength="2000" required></textarea>
</td> </div>
<td> </div>
<textarea id="description" class="form-control" @bind="@_description" rows="2"></textarea> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
</tr> <div class="col-sm-9">
<tr> <input id="categories" class="form-control" @bind="@_categories" maxlength="200" required />
<td> </div>
<Label For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label> </div>
</td> </div>
<td> </form>
<input id="categories" class="form-control" @bind="@_categories" />
</td>
</tr>
</table>
<Section Name="Information" ResourceKey="Information"> <Section Name="Information" ResourceKey="Information">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="moduledefinitionname" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
<Label For="moduledefinitionname" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label> <div class="col-sm-9">
</td>
<td>
<input id="moduledefinitionname" class="form-control" @bind="@_moduledefinitionname" disabled /> <input id="moduledefinitionname" class="form-control" @bind="@_moduledefinitionname" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="version" HelpText="The version of the module" ResourceKey="Version">Version: </Label>
<Label For="version" HelpText="The version of the module" ResourceKey="Version">Version: </Label> <div class="col-sm-9">
</td>
<td>
<input id="version" class="form-control" @bind="@_version" disabled /> <input id="version" class="form-control" @bind="@_version" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
<Label For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label> <div class="col-sm-9">
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" disabled /> <input id="owner" class="form-control" @bind="@_owner" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
<Label For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label> <div class="col-sm-9">
</td>
<td>
<input id="url" class="form-control" @bind="@_url" disabled /> <input id="url" class="form-control" @bind="@_url" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="contact" HelpText="The contact for the module" ResourceKey="Contact">Contact: </Label>
<Label For="contact" HelpText="The contact for the module" ResourceKey="Contact">Contact: </Label> <div class="col-sm-9">
</td>
<td>
<input id="contact" class="form-control" @bind="@_contact" disabled /> <input id="contact" class="form-control" @bind="@_contact" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="license" HelpText="The module license terms" ResourceKey="License">License: </Label>
<Label For="license" HelpText="The module license terms" ResourceKey="License">License: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea> <textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label>
<Label For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label> <div class="col-sm-9">
</td>
<td>
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled /> <input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</td> </div>
</tr> </div>
</table> </div>
</Section> </Section>
</TabPanel> </TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions"> <TabPanel Name="Permissions" ResourceKey="Permissions">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td>
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
</td> </div>
</tr> </div>
</table>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
@ -111,6 +91,8 @@
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
@code { @code {
private ElementReference form;
private bool validated = false;
private int _moduleDefinitionId; private int _moduleDefinitionId;
private string _name; private string _name;
private string _version; private string _version;
@ -167,6 +149,10 @@
} }
private async Task SaveModuleDefinition() private async Task SaveModuleDefinition()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -194,4 +180,9 @@
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -22,6 +22,7 @@ else
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
<th>@SharedLocalizer["Version"]</th> <th>@SharedLocalizer["Version"]</th>
<th>@SharedLocalizer["Expires"]</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
</Header> </Header>
<Row> <Row>
@ -34,6 +35,9 @@ else
</td> </td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.Version</td> <td>@context.Version</td>
<td>
@((MarkupString)PurchaseLink(context.PackageName))
</td>
<td> <td>
@if (UpgradeAvailable(context.PackageName, context.Version)) @if (UpgradeAvailable(context.PackageName, context.Version))
{ {
@ -67,10 +71,31 @@ else
} }
} }
private string PurchaseLink(string packagename)
{
string link = "";
if (!string.IsNullOrEmpty(packagename) && _packages != null)
{
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null)
{
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
if (!string.IsNullOrEmpty(package.PaymentUrl))
{
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
}
}
}
}
return link;
}
private bool UpgradeAvailable(string packagename, string version) private bool UpgradeAvailable(string packagename, string version)
{ {
var upgradeavailable = false; var upgradeavailable = false;
if (_packages != null) if (!string.IsNullOrEmpty(packagename) && _packages != null)
{ {
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault(); var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null) if (package != null)

View File

@ -5,31 +5,35 @@
@inject IStringLocalizer<Export> Localizer @inject IStringLocalizer<Export> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <div class="container">
<tbody> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="content" HelpText="The Exported Module Content" ResourceKey="Content">Content: </Label>
<td width="30%"> <div class="col-sm-9">
<Label For="content" HelpText="Enter the module content" ResourceKey="Content">Content: </Label> <textarea id="content" class="form-control" @bind="@_content" rows="5" readonly></textarea>
</td> </div>
<td> </div>
<textarea id="content" class="form-control" @bind="@_content" rows="5"></textarea> </div>
</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button> <button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {
private string _content = string.Empty; private string _content = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
public override string Title => "Export Content"; public override string Title => "Export Content";
private async Task ExportModule() private async Task ExportModule()
{
try
{ {
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId); _content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId);
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Exporting Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message);
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
}
} }
} }

View File

@ -5,29 +5,34 @@
@inject IStringLocalizer<Import> Localizer @inject IStringLocalizer<Import> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tbody> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="content" HelpText="Enter The Module Content To Import" ResourceKey="Content">Content: </Label>
<Label For="content" HelpText="Enter the module content" ResourceKey="Content">Content: </Label> <div class="col-sm-9">
</td> <textarea id="content" class="form-control" @bind="@_content" rows="5" required></textarea>
<td>
<textarea id="content" class="form-control" @bind="@_content" rows="5"></textarea> </div>
</td> </div>
</tr> </div>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button> <button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</form>
@code { @code {
private string _content = string.Empty; private string _content = string.Empty;
private ElementReference form;
private bool validated = false;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
public override string Title => "Import Content"; public override string Title => "Import Content";
private async Task ImportModule() private async Task ImportModule()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_content != string.Empty) if (_content != string.Empty)
{ {
@ -54,4 +59,9 @@
AddModuleMessage(Localizer["Message.Required.ImportContent"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.ImportContent"], MessageType.Warning);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -8,49 +8,42 @@
@inject IStringLocalizer<Settings> Localizer @inject IStringLocalizer<Settings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabStrip> <TabStrip>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings"> <TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
@if (_containers != null) @if (_containers != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
<Label For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label> <div class="col-sm-9">
</td> <input id="title" type="text" name="Title" class="form-control" @bind="@_title" required />
<td> </div>
<input id="title" type="text" name="Title" class="form-control" @bind="@_title" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
<tr> <div class="col-sm-9">
<td> <select id="container" class="form-select" @bind="@_containerType" required>
<Label For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
</td>
<td>
<select id="container" class="form-select" @bind="@_containerType">
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
<Label For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label> <div class="col-sm-9">
</td> <select id="allpages" class="form-select" @bind="@_allPages" required>
<td>
<select id="allpages" class="form-select" @bind="@_allPages">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
<Label For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label> <div class="col-sm-9">
</td> <select id="page" class="form-select" @bind="@_pageId" required>
<td>
<select id="page" class="form-select" @bind="@_pageId">
@foreach (Page p in PageState.Pages) @foreach (Page p in PageState.Pages)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
@ -59,21 +52,20 @@
} }
} }
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
} }
</TabPanel> </TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions"> <TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null) @if (_permissions != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td>
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" Permissions="@_permissions" @ref="_permissionGrid" />
</td> </div>
</tr> </div>
</table>
} }
</TabPanel> </TabPanel>
@if (_moduleSettingsType != null) @if (_moduleSettingsType != null)
@ -89,16 +81,20 @@
</TabPanel> </TabPanel>
} }
</TabStrip> </TabStrip>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br /> <br />
<br /> <br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo> <AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
</form>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Module Settings"; public override string Title => "Module Settings";
private ElementReference form;
private bool validated = false;
private List<Theme> _themes; private List<Theme> _themes;
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private string _title; private string _title;
@ -178,6 +174,10 @@
} }
private async Task SaveModule() private async Task SaveModule()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (!string.IsNullOrEmpty(_title)) if (!string.IsNullOrEmpty(_title))
{ {
@ -227,5 +227,10 @@
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -6,39 +6,35 @@
@inject IStringLocalizer<Add> Localizer @inject IStringLocalizer<Add> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabStrip Refresh="@_refresh"> <TabStrip Refresh="@_refresh">
<TabPanel Name="Settings" ResourceKey="Settings"> <TabPanel Name="Settings" ResourceKey="Settings">
@if (_themeList != null) @if (_themeList != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
<Label For="Name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label> <div class="col-sm-9">
</td> <input id="name" class="form-control" @bind="@_name" required />
<td> </div>
<input id="Name" class="form-control" @bind="@_name" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
<tr> <div class="col-sm-9">
<td> <select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
</td>
<td>
<select id="Parent" class="form-select" @onchange="(e => ParentChanged(e))">
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option> <option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
@foreach (Page page in _pageList) @foreach (Page page in _pageList)
{ {
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option> <option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages" ResourceKey="Insert">Insert: </Label>
<Label For="Insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages" ResourceKey="Insert">Insert: </Label> <div class="col-sm-9">
</td> <select id="insert" class="form-select" @bind="@_insert" required>
<td>
<select id="Insert" class="form-select" @bind="@_insert">
<option value="<<">@Localizer["AtBeginning"]</option> <option value="<<">@Localizer["AtBeginning"]</option>
@if (_children != null && _children.Count > 0) @if (_children != null && _children.Count > 0)
{ {
@ -57,115 +53,96 @@
} }
</select> </select>
} }
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
<Label For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label> <div class="col-sm-9">
</td> <select id="navigation" class="form-select" @bind="@_isnavigation" required>
<td>
<select id="navigation" class="form-select" @bind="@_isnavigation">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
<Label For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label> <div class="col-sm-9">
</td> <select id="clickable" class="form-select" @bind="@_isclickable" required>
<td>
<select id="clickable" class="form-select" @bind="@_isclickable">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label>
<Label For="Path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label> <div class="col-sm-9">
</td> <input id="path" class="form-control" @bind="@_path" />
<td> </div>
<input id="Path" class="form-control" @bind="@_path" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
<tr> <div class="col-sm-9">
<td> <input id="url" class="form-control" @bind="@_url" />
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label> </div>
</td> </div>
<td> </div>
<input id="Url" class="form-control" @bind="@_url" />
</td>
</tr>
</table>
<Section Name="Appearance" ResourceKey="Appearance"> <Section Name="Appearance" ResourceKey="Appearance">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
<Label For="Title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label> <div class="col-sm-9">
</td> <input id="title" class="form-control" @bind="@_title" />
<td> </div>
<input id="Title" class="form-control" @bind="@_title" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<tr> <div class="col-sm-9">
<td> <select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<Label For="Theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
</td>
<td>
<select id="Theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))">
@foreach (var theme in _themes) @foreach (var theme in _themes)
{ {
<option value="@theme.TypeName">@theme.Name</option> <option value="@theme.TypeName">@theme.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label> <div class="col-sm-9">
</td> <select id="container" class="form-select" @bind="@_containertype" required>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
<Label For="Icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label> <div class="col-sm-9">
</td> <input id="icon" class="form-control" @bind="@_icon" />
<td> </div>
<input id="Icon" class="form-control" @bind="@_icon" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
<tr> <div class="col-sm-9">
<td> <select id="personalizable" class="form-select" @bind="@_ispersonalizable" required>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
</td>
<td>
<select id="Personalizable" class="form-select" @bind="@_ispersonalizable">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
</Section> </Section>
} }
</TabPanel> </TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions"> <TabPanel Name="Permissions" ResourceKey="Permissions">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td>
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</td> </div>
</tr> </div>
</table>
</TabPanel> </TabPanel>
@if (_themeSettingsType != null) @if (_themeSettingsType != null)
{ {
@ -176,6 +153,7 @@
</TabStrip> </TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
</form>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -187,7 +165,7 @@
private string _name; private string _name;
private string _title; private string _title;
private string _path = string.Empty; private string _path = string.Empty;
private string _parentid; private string _parentid = "-1";
private string _insert = ">>"; private string _insert = ">>";
private List<Page> _children; private List<Page> _children;
private int _childid = -1; private int _childid = -1;
@ -204,6 +182,8 @@
private object _themeSettings; private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; } private RenderFragment ThemeSettingsComponent { get; set; }
private bool _refresh = false; private bool _refresh = false;
private ElementReference form;
private bool validated = false;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -299,17 +279,21 @@
} }
private async Task SavePage() private async Task SavePage()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
Page page = null; Page page = null;
try try
{ {
if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(_themetype) && _containertype != "-") if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
{ {
page = new Page(); page = new Page();
page.SiteId = PageState.Page.SiteId; page.SiteId = PageState.Page.SiteId;
page.Name = _name; page.Name = _name;
page.Title = _title; page.Title = _title;
if (_path == "") if (string.IsNullOrEmpty(_path))
{ {
_path = _name; _path = _name;
} }
@ -319,7 +303,7 @@
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);
} }
if (string.IsNullOrEmpty(_parentid)) if (_parentid == "-1")
{ {
page.ParentId = null; page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path); page.Path = Utilities.GetFriendlyUrl(_path);
@ -338,6 +322,12 @@
} }
} }
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
{
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
return;
}
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList)) if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{ {
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
@ -406,6 +396,11 @@
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private void Cancel() private void Cancel()
{ {
@ -423,4 +418,9 @@
{ {
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath); return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
} }
private static bool PagePathIsDeleted(string pagePath, int siteId, List<Page> existingPages)
{
return existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.IsDeleted == true);
}
} }

View File

@ -7,25 +7,22 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabStrip Refresh="@_refresh"> <TabStrip Refresh="@_refresh">
<TabPanel Name="Settings" ResourceKey="Settings"> <TabPanel Name="Settings" ResourceKey="Settings">
@if (_themeList != null) @if (_themeList != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
<Label For="Name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label> <div class="col-sm-9">
</td> <input id="name" class="form-control" @bind="@_name" maxlength="50" required />
<td> </div>
<input id="Name" class="form-control" @bind="@_name" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
<tr> <div class="col-sm-9">
<td> <select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
</td>
<td>
<select id="Parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))">
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option> <option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
@foreach (Page page in _pageList) @foreach (Page page in _pageList)
{ {
@ -35,14 +32,12 @@
} }
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="move" HelpText="Select the location where you would like the page to be moved in relation to other pages" ResourceKey="Move">Move: </Label>
<Label For="Move" HelpText="Select the location where you would like the page to be moved in relation to other pages" ResourceKey="Move">Move: </Label> <div class="col-sm-9">
</td> <select id="move" class="form-select" @bind="@_insert" required>
<td>
<select id="Move" class="form-select" @bind="@_insert">
@if (_parentid == _currentparentid) @if (_parentid == _currentparentid)
{ {
<option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option> <option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option>
@ -65,104 +60,86 @@
} }
</select> </select>
} }
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
<Label For="Navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label> <div class="col-sm-9">
</td> <select id="navigation" class="form-select" @bind="@_isnavigation" required>
<td>
<select id="Navigation" class="form-select" @bind="@_isnavigation">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
<Label For="Clickablen" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label> <div class="col-sm-9">
</td> <select id="clickable" class="form-select" @bind="@_isclickable" required>
<td>
<select id="Navigation" class="form-select" @bind="@_isclickable">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label>
<Label For="Path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label> <div class="col-sm-9">
</td> <input id="path" class="form-control" @bind="@_path" maxlength="256"/>
<td> </div>
<input id="Path" class="form-control" @bind="@_path" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
<tr> <div class="col-sm-9">
<td> <input id="url" class="form-control" @bind="@_url" maxlength="500"/>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label> </div>
</td> </div>
<td> </div>
<input id="Url" class="form-control" @bind="@_url" />
</td>
</tr>
</table>
<Section Name="Appearance" ResourceKey="Appearance"> <Section Name="Appearance" ResourceKey="Appearance">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
<Label For="Title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label> <div class="col-sm-9">
</td> <input id="title" class="form-control" @bind="@_title" maxlength="200"/>
<td> </div>
<input id="Title" class="form-control" @bind="@_title" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<tr> <div class="col-sm-9">
<td> <select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<Label For="Theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
</td>
<td>
<select id="Theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))">
@foreach (var theme in _themes) @foreach (var theme in _themes)
{ {
<option value="@theme.TypeName">@theme.Name</option> <option value="@theme.TypeName">@theme.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label> <div class="col-sm-9">
</td> <select id="container" class="form-select" @bind="@_containertype" required>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
<Label For="Icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label> <div class="col-sm-9">
</td> <input id="icon" class="form-control" @bind="@_icon" maxlength="50"/>
<td> </div>
<input id="Icon" class="form-control" @bind="@_icon" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
<tr> <div class="col-sm-9">
<td> <select id="personalizable" class="form-select" @bind="@_ispersonalizable" required>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
</td>
<td>
<select id="Personalizable" class="form-select" @bind="@_ispersonalizable">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
</Section> </Section>
<br /><br /> <br /><br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
@ -171,13 +148,13 @@
<TabPanel Name="Permissions" ResourceKey="Permissions"> <TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null) @if (_permissions != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td>
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</td>
</tr> </div>
</table> </div>
} }
</TabPanel> </TabPanel>
@if (_themeSettingsType != null) @if (_themeSettingsType != null)
@ -185,14 +162,18 @@
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings"> <TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent @ThemeSettingsComponent
</TabPanel> </TabPanel>
<br />
} }
</TabStrip> </TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
</form>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private ElementReference form;
private bool validated = false;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
@ -202,7 +183,7 @@
private string _title; private string _title;
private string _path; private string _path;
private string _currentparentid; private string _currentparentid;
private string _parentid; private string _parentid = "-1";
private string _insert = "="; private string _insert = "=";
private List<Page> _children; private List<Page> _children;
private int _childid = -1; private int _childid = -1;
@ -251,7 +232,7 @@
if (page.ParentId == null) if (page.ParentId == null)
{ {
_parentid = string.Empty; _parentid = "-1";
} }
else else
{ {
@ -374,19 +355,22 @@
} }
private async Task SavePage() private async Task SavePage()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
Page page = null; Page page = null;
try try
{ {
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && _containertype != "-") if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
{ {
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
string currentPath = page.Path; string currentPath = page.Path;
page.Name = _name; page.Name = _name;
page.Title = _title; page.Title = _title;
if (_path == "" && _name.ToLower() != "home") if (string.IsNullOrEmpty(_path) && _name.ToLower() != "home")
if (_path == string.Empty && _name.ToLower() != "home")
{ {
_path = _name; _path = _name;
} }
@ -394,7 +378,7 @@
{ {
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);
} }
if (string.IsNullOrEmpty(_parentid) || _parentid == "-1") if (_parentid == "-1")
{ {
page.ParentId = null; page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path); page.Path = Utilities.GetFriendlyUrl(_path);
@ -505,6 +489,11 @@
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private void Cancel() private void Cancel()
{ {

View File

@ -5,94 +5,76 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="name" HelpText="The name of this profile item" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="The name of this profile item" ResourceKey="Name">Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="name" class="form-control" @bind="@_name" maxlength="50" required />
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="title" HelpText="The title of the profile item to display to the user" ResourceKey="Title">Title: </Label>
<td> <div class="col-sm-9">
<Label For="title" HelpText="The title of the profile item to display to the user" ResourceKey="Title">Title: </Label> <input id="title" class="form-control" @bind="@_title" maxlength="50" required />
</td> </div>
<td> </div>
<input id="title" class="form-control" @bind="@_title" /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="description" HelpText="The help text displayed to the user for this profile item" ResourceKey="Description">Description: </Label>
</tr> <div class="col-sm-9">
<tr> <textarea id="description" class="form-control" @bind="@_description" rows="5" maxlength="256" required ></textarea>
<td> </div>
<Label For="description" HelpText="The help text displayed to the user for this profile item" ResourceKey="Description">Description: </Label> </div>
</td> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="category" HelpText="The category of this profile item (for grouping)" ResourceKey="Category">Category: </Label>
<textarea id="description" class="form-control" @bind="@_description" rows="5"></textarea> <div class="col-sm-9">
</td> <input id="category" class="form-control" @bind="@_category" maxlength="50" />
</tr> </div>
<tr> </div>
<td> <div class="row mb-1 align-items-center">
<Label For="category" HelpText="The category of this profile item (for grouping)" ResourceKey="Category">Category: </Label> <Label Class="col-sm-3" For="order" HelpText="The index order of where this profile item should be displayed" ResourceKey="Order">Order: </Label>
</td> <div class="col-sm-9">
<td> <input id="order" class="form-control" @bind="@_vieworder" maxlength="4" required />
<input id="category" class="form-control" @bind="@_category" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="length" HelpText="The max number of characters this profile item should accept (enter zero for unlimited)" ResourceKey="Length">Length: </Label>
<td> <div class="col-sm-9">
<Label For="order" HelpText="The index order of where this profile item should be displayed" ResourceKey="Order">Order: </Label> <input id="length" class="form-control" @bind="@_maxlength" maxlength="4" required />
</td> </div>
<td> </div>
<input id="order" class="form-control" @bind="@_vieworder" /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="defaultVal" HelpText="The default value for this profile item" ResourceKey="DefaultValue">Default Value: </Label>
</tr> <div class="col-sm-9">
<tr> <input id="defaultVal" class="form-control" @bind="@_defaultvalue" maxlength="2000"/>
<td> </div>
<Label For="length" HelpText="The max number of characters this profile item should accept (enter zero for unlimited)" ResourceKey="Length">Length: </Label> </div>
</td> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="options" HelpText="A comma delimited list of options the user can select from" ResourceKey="Options">Options: </Label>
<input id="length" class="form-control" @bind="@_maxlength" /> <div class="col-sm-9">
</td> <input id="options" class="form-control" @bind="@_options" maxlength="2000" />
</tr> </div>
<tr> </div>
<td> <div class="row mb-1 align-items-center">
<Label For="defaultVal" HelpText="The default value for this profile item" ResourceKey="DefaultValue">Default Value: </Label> <Label Class="col-sm-3" For="required" HelpText="Should a user be required to provide a value for this profile item?" ResourceKey="Required">Required? </Label>
</td> <div class="col-sm-9">
<td> <select id="required" class="form-select" @bind="@_isrequired" required>
<input id="defaultVal" class="form-control" @bind="@_defaultvalue" />
</td>
</tr>
<tr>
<td>
<Label For="options" HelpText="A comma delimited list of options the user can select from" ResourceKey="Options">Options: </Label>
</td>
<td>
<input id="options" class="form-control" @bind="@_options" />
</td>
</tr>
<tr>
<td>
<Label For="required" HelpText="Should a user be required to provide a value for this profile item?" ResourceKey="Required">Required? </Label>
</td>
<td>
<select id="required" class="form-select" @bind="@_isrequired">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
<Label For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label> <div class="col-sm-9">
</td> <select id="private" class="form-select" @bind="@_isprivate" required>
<td>
<select id="private" class="form-select" @bind="@_isprivate">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveProfile">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveProfile">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@if (PageState.QueryString.ContainsKey("id")) @if (PageState.QueryString.ContainsKey("id"))
@ -101,9 +83,12 @@
<br /> <br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo> <AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
} }
</form>
@code { @code {
private int _profileid = -1; private int _profileid = -1;
private ElementReference form;
private bool validated = false;
private string _name = string.Empty; private string _name = string.Empty;
private string _title = string.Empty; private string _title = string.Empty;
private string _description = string.Empty; private string _description = string.Empty;
@ -158,6 +143,10 @@
} }
private async Task SaveProfile() private async Task SaveProfile()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -200,4 +189,9 @@
AddModuleMessage(Localizer["Error.Profile.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Profile.Save"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -16,52 +16,43 @@
</Authorized> </Authorized>
<NotAuthorized> <NotAuthorized>
<ModuleMessage Message="@Localizer["Info.Registration.InvalidEmail"]" Type="MessageType.Info" /> <ModuleMessage Message="@Localizer["Info.Registration.InvalidEmail"]" Type="MessageType.Info" />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
<Label For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label> <div class="col-sm-9">
</td> <input id="username" class="form-control" @bind="@_username" maxlength="256" required />
<td> </div>
<input id="username" class="form-control" @bind="@_username" readonly /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
<tr> <div class="col-sm-9">
<td> <input id="password" type="password" class="form-control" @bind="@_password" autocomplete="new-password" required />
<Label For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label> </div>
</td> </div>
<td> <div class="row mb-1 align-items-center">
<input id="password" type="password" class="form-control" @bind="@_password" autocomplete="new-password" /> <Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
</td> <div class="col-sm-9">
</tr> <input id="confirm" type="password" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
<tr> </div>
<td> </div>
<Label For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
<td> <div class="col-sm-9">
<input id="confirm" type="password" class="form-control" @bind="@_confirm" autocomplete="new-password" /> <input id="email" class="form-control" @bind="@_email" maxlength="256" required />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
<Label For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label> <div class="col-sm-9">
</td> <input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
<td> </div>
<input id="email" class="form-control" @bind="@_email" /> </div>
</td> </div>
</tr> <br />
<tr>
<td>
<Label For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
</td>
<td>
<input id="displayname" class="form-control" @bind="@_displayname" />
</td>
</tr>
</table>
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button> <button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
</form>
</NotAuthorized> </NotAuthorized>
</AuthorizeView> </AuthorizeView>
} }
@ -72,6 +63,8 @@ else
@code { @code {
private string _username = string.Empty; private string _username = string.Empty;
private ElementReference form;
private bool validated = false;
private string _password = string.Empty; private string _password = string.Empty;
private string _confirm = string.Empty; private string _confirm = string.Empty;
private string _email = string.Empty; private string _email = string.Empty;
@ -80,12 +73,16 @@ else
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
private async Task Register() private async Task Register()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
bool _isEmailValid = Utilities.IsValidEmail(_email); bool _isEmailValid = Utilities.IsValidEmail(_email);
if (_username != "" && _password != "" && _confirm != "" && _isEmailValid) if (_isEmailValid)
{ {
if (_password == _confirm) if (_password == _confirm)
{ {
@ -126,6 +123,11 @@ else
AddModuleMessage(Localizer["Error.User.Add"], MessageType.Error); AddModuleMessage(Localizer["Error.User.Add"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private void Cancel() private void Cancel()
{ {

View File

@ -5,6 +5,7 @@
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container"> <div class="container">
<div class="form-group"> <div class="form-group">
<label for="Username" class="control-label">@SharedLocalizer["Username"] </label> <label for="Username" class="control-label">@SharedLocalizer["Username"] </label>
@ -12,17 +13,20 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label> <label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" /> <input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label> <label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" /> <input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" required />
</div> </div>
<button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Password.Reset"]</button> <button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Password.Reset"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
</div> </div>
</form>
@code { @code {
private ElementReference form;
private bool validated = false;
private string _username = string.Empty; private string _username = string.Empty;
private string _password = string.Empty; private string _password = string.Empty;
private string _confirm = string.Empty; private string _confirm = string.Empty;
@ -42,6 +46,10 @@
} }
private async Task Reset() private async Task Reset()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -84,6 +92,11 @@
AddModuleMessage(Localizer["Error.Password.Reset"], MessageType.Error); AddModuleMessage(Localizer["Error.Password.Reset"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private void Cancel() private void Cancel()
{ {

View File

@ -6,37 +6,32 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="name" HelpText="Name Of The Role" ResourceKey="Name">Name:</Label>
<Label For="name" HelpText="Name Of The Role" ResourceKey="Name">Name:</Label> <div class="col-sm-9">
</td>
<td>
<input id="name" class="form-control" @bind="@_name" maxlength="256" required /> <input id="name" class="form-control" @bind="@_name" maxlength="256" required />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose" ResourceKey="Description">Description:</Label>
<Label For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose" ResourceKey="Description">Description:</Label> <div class="col-sm-9">
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5" maxlength="256" required></textarea> <textarea id="description" class="form-control" @bind="@_description" rows="5" maxlength="256" required></textarea>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label>
<Label For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label> <div class="col-sm-9">
</td> <select id="isautoassigned" class="form-select" @bind="@_isautoassigned" required>
<td>
<select id="isautoassigned" class="form-select" @bind="@_isautoassigned">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> <br /><br />
<button type="button" class="btn btn-success" @onclick="SaveRole">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</div>
</form> </form>
@code { @code {
@ -77,7 +72,7 @@
} }
else else
{ {
AddModuleMessage(Localizer["Message.InfoRequired"], MessageType.Warning); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }

View File

@ -6,39 +6,34 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="name" HelpText="Name Of The Role" ResourceKey="Name">Name:</Label>
<Label For="name" HelpText="Name Of The Role" ResourceKey="Name">Name:</Label> <div class="col-sm-9">
</td>
<td>
<input id="name" class="form-control" @bind="@_name" maxlength="256" required /> <input id="name" class="form-control" @bind="@_name" maxlength="256" required />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose" ResourceKey="Description">Description:</Label>
<Label For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose" ResourceKey="Description">Description:</Label> <div class="col-sm-9">
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5" maxlength="256" required></textarea> <textarea id="description" class="form-control" @bind="@_description" rows="5" maxlength="256" required></textarea>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label>
<Label For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label> <div class="col-sm-9">
</td> <select id="isautoassigned" class="form-select" @bind="@_isautoassigned" required>
<td>
<select id="isautoassigned" class="form-select" @bind="@_isautoassigned">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> <br /><br />
<button type="button" class="btn btn-success" @onclick="SaveRole">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br /><br /> <br /><br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
</div>
</form> </form>
@code { @code {
@ -106,7 +101,7 @@
} }
else else
{ {
AddModuleMessage(Localizer["Message.InfoRequired"], MessageType.Warning); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }
} }

View File

@ -11,50 +11,44 @@
} }
else else
{ {
<table class="table table-borderless">
<tr> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<td width="30%"> <div class="container">
<Label For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label>
<td> <div class="col-sm-9">
<input id="role" class="form-control" @bind="@name" disabled /> <input id="role" class="form-control" @bind="@name" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
<Label For="user" HelpText="Select a user" ResourceKey="User">User: </Label> <div class="col-sm-9">
</td> <select id="user" class="form-select" @bind="@userid" required>
<td>
<select id="user" class="form-select" @bind="@userid">
<option value="-1">&lt;@Localizer["User.Select"]&gt;</option> <option value="-1">&lt;@Localizer["User.Select"]&gt;</option>
@foreach (UserRole userrole in users) @foreach (UserRole userrole in users)
{ {
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option> <option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<Label For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label> <div class="col-sm-9">
</td> <input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" required />
<td> </div>
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<tr> <div class="col-sm-9">
<td> <input type="date" id="expiryDate" class="form-control" @bind="@expirydate" required />
<Label For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label> </div>
</td> </div>
<td> <br /><br />
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<hr class="app-rule" /> <hr class="app-rule" />
<div class="row mb-1 align-items-center">
<p align="center"> <p align="center">
<Pager Items="@userroles"> <Pager Items="@userroles">
<Header> <Header>
@ -73,9 +67,15 @@ else
</Row> </Row>
</Pager> </Pager>
</p> </p>
</div>
</div>
</form>
} }
@code { @code {
private ElementReference form;
private bool validated = false;
private int roleid; private int roleid;
private string name = string.Empty; private string name = string.Empty;
private List<UserRole> users; private List<UserRole> users;
@ -122,6 +122,10 @@ else
} }
private async Task SaveUserRole() private async Task SaveUserRole()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -162,7 +166,17 @@ else
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task DeleteUserRole(int UserRoleId) private async Task DeleteUserRole(int UserRoleId)
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -178,4 +192,9 @@ else
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error); AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -13,106 +13,88 @@
@if (_initialized) @if (_initialized)
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="name" HelpText="Enter the site name" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="Enter the site name" ResourceKey="Name">Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="name" class="form-control" @bind="@_name" maxlength="200" required />
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
<td> <div class="col-sm-9">
<Label For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
</td>
<td>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea> <textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
} }
else else
{ {
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea> <textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea>
} }
</td> </div>
</tr> </div>
<div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
<td> <div class="col-sm-9">
<Label For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label> <select id="allowRegister" class="form-select" @bind="@_allowregistration" required>
</td>
<td>
<select id="allowRegister" class="form-select" @bind="@_allowregistration">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
<Label For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label> <div class="col-sm-9">
</td> <select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
<td>
<select id="isDeleted" class="form-select" @bind="@_isdeleted">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance"> <Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
<Label For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" /> <FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
<Label For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" /> <FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
<Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label> <div class="col-sm-9">
</td> <select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<td>
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option> <option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes) @foreach (var theme in _themes)
{ {
<option value="@theme.TypeName">@theme.Name</option> <option value="@theme.TypeName">@theme.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label> <div class="col-sm-9">
</td> <select id="defaultContainer" class="form-select" @bind="@_containertype" required>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label> <div class="col-sm-9">
</td> <select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
<td>
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="@Constants.DefaultAdminContainer">&lt;@Localizer["DefaultAdminContainer"]&gt;</option> <option value="@Constants.DefaultAdminContainer">&lt;@Localizer["DefaultAdminContainer"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
@ -120,133 +102,114 @@
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
</table>
</div>
</Section> </Section>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings"> <Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%">&nbsp;</td> <div class="col-sm-3">
<td> </div>
<div class="col-sm-9">
<strong>@Localizer["Smtp.Required.EnableNotificationJob"]</strong><br /> <strong>@Localizer["Smtp.Required.EnableNotificationJob"]</strong><br />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="host" HelpText="Enter the host name of the SMTP server" ResourceKey="Host">Host: </Label>
<Label For="host" HelpText="Enter the host name of the SMTP server" ResourceKey="Host">Host: </Label> <div class="col-sm-9">
</td>
<td>
<input id="host" class="form-control" @bind="@_smtphost" /> <input id="host" class="form-control" @bind="@_smtphost" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="port" HelpText="Enter the port number for the SMTP server. Please note this field is required if you provide a host name." ResourceKey="Port">Port: </Label>
<Label For="port" HelpText="Enter the port number for the SMTP server. Please note this field is required if you provide a host name." ResourceKey="Port">Port: </Label> <div class="col-sm-9">
</td>
<td>
<input id="port" class="form-control" @bind="@_smtpport" /> <input id="port" class="form-control" @bind="@_smtpport" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
<Label For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label> <div class="col-sm-9">
</td>
<td>
<select id="enabledSSl" class="form-select" @bind="@_smtpssl" > <select id="enabledSSl" class="form-select" @bind="@_smtpssl" >
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
<Label For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label> <div class="col-sm-9">
</td>
<td>
<input id="username" class="form-control" @bind="@_smtpusername" /> <input id="username" class="form-control" @bind="@_smtpusername" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
<Label For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label> <div class="col-sm-9">
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@_smtppassword" /> <input id="password" type="password" class="form-control" @bind="@_smtppassword" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmptSender">Email Sender: </Label>
<Label For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmptSender">Email Sender: </Label> <div class="col-sm-9">
</td>
<td>
<input id="sender" class="form-control" @bind="@_smtpsender" /> <input id="sender" class="form-control" @bind="@_smtpsender" />
</td> </div>
</tr> </div>
</table>
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button> <button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
<br /><br /> <br /><br />
</div>
</Section> </Section>
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings"> <Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="isEnabled" HelpText="Select whether you would like this site to be available as a Progressive Web Application (PWA)" ResourceKey="EnablePWA">Is Enabled? </Label>
<Label For="isEnabled" HelpText="Select whether you would like this site to be available as a Progressive Web Application (PWA)" ResourceKey="EnablePWA">Is Enabled? </Label> <div class="col-sm-9">
</td> <select id="isEnabled" class="form-select" @bind="@_pwaisenabled" required>
<td>
<select id="isEnabled" class="form-select" @bind="@_pwaisenabled">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension." ResourceKey="PwaApplicationIcon">App Icon: </Label>
<Label For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension." ResourceKey="PwaApplicationIcon">App Icon: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" /> <FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension." ResourceKey="PwaSplashIcon">Splash Icon: </Label>
<Label For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension." ResourceKey="PwaSplashIcon">Splash Icon: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" /> <FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
</td> </div>
</tr> </div>
</table> </div>
</Section> </Section>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation"> <Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<Label For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label> <div class="col-sm-9">
</td>
<td>
<input id="tenant" class="form-control" @bind="@_tenant" readonly /> <input id="tenant" class="form-control" @bind="@_tenant" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
<Label For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label> <div class="col-sm-9">
</td>
<td>
<input id="database" class="form-control" @bind="@_database" readonly /> <input id="database" class="form-control" @bind="@_database" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<Label For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea> <textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
</td> </div>
</tr> </div>
</table> </div>
</Section> </Section>
} }
<br /> <br />
@ -255,9 +218,12 @@
<br /> <br />
<br /> <br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
</form>
} }
@code { @code {
private ElementReference form;
private bool validated = false;
private bool _initialized = false; private bool _initialized = false;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
@ -421,6 +387,10 @@
} }
private async Task SaveSite() private async Task SaveSite()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try try
{ {
@ -539,6 +509,11 @@
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error); AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task DeleteSite() private async Task DeleteSite()
{ {

View File

@ -19,57 +19,48 @@
} }
else else
{ {
<table class="table table-borderless"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="name" HelpText="Enter the name of the site" ResourceKey="Name">Site Name: </Label> <Label Class="col-sm-3" For="name" HelpText="Enter the name of the site" ResourceKey="Name">Site Name: </Label>
</td> <div class="col-sm-9">
<td> <input id="name" class="form-control" @bind="@_name" maxlength="200" required />
<input id="name" class="form-control" @bind="@_name" /> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
<td> <div class="col-sm-9">
<Label For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label> <textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
</td> </div>
<td> </div>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</tr> <div class="col-sm-9">
<tr> <select id="defaultTheme" class="form-select" @onchange="(e => ThemeChanged(e))" required>
<td>
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-select" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option> <option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes) @foreach (var theme in _themes)
{ {
<option value="@theme.TypeName">@theme.Name</option> <option value="@theme.TypeName">@theme.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label> <div class="col-sm-9">
</td> <select id="defaultContainer" class="form-select" @bind="@_containertype" required>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
<Label For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label> <div class="col-sm-9">
</td> <select id="adminContainer" class="form-select" @bind="@_admincontainertype" required>
<td>
<select id="adminContainer" class="form-select" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="">&lt;@Localizer["DefaultContainer.Admin"]&gt;</option> <option value="">&lt;@Localizer["DefaultContainer.Admin"]&gt;</option>
@foreach (var container in _containers) @foreach (var container in _containers)
@ -77,28 +68,24 @@ else
<option value="@container.TypeName">@container.Name</option> <option value="@container.TypeName">@container.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
<Label For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label> <div class="col-sm-9">
</td> <select id="siteTemplate" class="form-select" @bind="@_sitetemplatetype" required>
<td>
<select id="siteTemplate" class="form-select" @bind="@_sitetemplatetype">
<option value="-">&lt;@Localizer["SiteTemplate.Select"]&gt;</option> <option value="-">&lt;@Localizer["SiteTemplate.Select"]&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates) @foreach (SiteTemplate siteTemplate in _siteTemplates)
{ {
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option> <option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<Label For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label> <div class="col-sm-9">
</td> <select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
<td>
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))">
<option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option> <option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option>
<option value="+">&lt;@Localizer["Tenant.Add"]&gt;</option> <option value="+">&lt;@Localizer["Tenant.Add"]&gt;</option>
@foreach (Tenant tenant in _tenants) @foreach (Tenant tenant in _tenants)
@ -106,29 +93,23 @@ else
<option value="@tenant.TenantId">@tenant.Name</option> <option value="@tenant.TenantId">@tenant.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
@if (_tenantid == "+") @if (_tenantid == "+")
{ {
<tr> <div class="row mb-1 align-items-center">
<td colspan="2">
<hr class="app-rule" /> <hr class="app-rule" />
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
<td> <div class="col-sm-9">
<Label For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label> <input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
</td> </div>
<td> </div>
<input id="name" class="form-control" @bind="@_tenantName" /> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
</tr> <div class="col-sm-9">
<tr> <select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
<td>
<Label For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
</td>
<td>
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
@foreach (var database in _databases) @foreach (var database in _databases)
{ {
if (database.IsDefault) if (database.IsDefault)
@ -141,36 +122,37 @@ else
} }
} }
</select> </select>
</td> </div>
</tr> </div>
if (_databaseConfigType != null) if (_databaseConfigType != null)
{ {
@DatabaseConfigComponent; @DatabaseConfigComponent;
} }
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label>
<Label For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label> <div class="col-sm-9">
</td>
<td>
<input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly /> <input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label>
<Label For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label> <div class="col-sm-9">
</td> <input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" required />
<td> </div>
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" /> </div>
</td>
</tr>
} }
</table> </div>
<br />
<br />
<button type="button" class="btn btn-success" @onclick="SaveSite">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveSite">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</form>
} }
@code { @code {
private List<Database> _databases; private List<Database> _databases;
private ElementReference form;
private bool validated = false;
private string _databaseName = "LocalDB"; private string _databaseName = "LocalDB";
private Type _databaseConfigType; private Type _databaseConfigType;
private object _databaseConfig; private object _databaseConfig;
@ -273,6 +255,10 @@ else
} }
private async Task SaveSite() private async Task SaveSite()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-") if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
{ {
@ -381,4 +367,9 @@ else
AddModuleMessage(Localizer["Message.Required.Tenant"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.Tenant"], MessageType.Warning);
} }
} }
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
} }

View File

@ -13,12 +13,11 @@
} }
else else
{ {
<table class="table table-borderless"> <div class="container">
<tr>
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="tenant" HelpText="Select the tenant for the SQL server" ResourceKey="Tenant">Tenant: </Label> <Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the SQL server" ResourceKey="Tenant">Tenant: </Label>
</td> <div class="col-sm-9">
<td>
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))"> <select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))">
<option value="-1">&lt;@Localizer["Tenant.Select"]&gt;</option> <option value="-1">&lt;@Localizer["Tenant.Select"]&gt;</option>
@foreach (Tenant tenant in _tenants) @foreach (Tenant tenant in _tenants)
@ -26,42 +25,61 @@ else
<option value="@tenant.TenantId">@tenant.Name</option> <option value="@tenant.TenantId">@tenant.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
@if (_tenantid != "-1") @if (_tenantid != "-1")
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
<Label For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label> <div class="col-sm-9">
</td>
<td>
<input id="database" class="form-control" @bind="@_database" readonly /> <input id="database" class="form-control" @bind="@_database" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<Label For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea> <textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="sqlQeury" HelpText="Enter the query for the SQL server" ResourceKey="SqlQuery">SQL Query: </Label>
<Label For="sqlQeury" HelpText="Enter the query for the SQL server" ResourceKey="SqlQuery">SQL Query: </Label> <div class="col-sm-9">
</td> <textarea id="sqlQeury" class="form-control" @bind="@_sql" rows="3"></textarea>
<td> </div>
<textarea id="sqlQeury" class="form-control" @bind="@_sql" rows="5"></textarea> </div>
</td>
</tr>
} }
</table> </div>
<br />
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button> <button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button>
<br /> <br />
<br /> <br />
@if (!string.IsNullOrEmpty(_results)) @if (_results != null)
{ {
@((MarkupString)_results) @if (_results.Count > 0)
{
<div class="table-responsive">
<Pager Class="table table-bordered" Items="@_results">
<Header>
@foreach (KeyValuePair<string, string> kvp in _results.First())
{
<th>@kvp.Key</th>
}
</Header>
<Row>
@foreach (KeyValuePair<string, string> kvp in context)
{
<td>@kvp.Value</td>
}
</Row>
</Pager>
</div>
}
else
{
@Localizer["Return.NoResult"]
}
<br />
<br />
} }
} }
@ -71,7 +89,7 @@ else
private string _database = string.Empty; private string _database = string.Empty;
private string _connectionstring = string.Empty; private string _connectionstring = string.Empty;
private string _sql = string.Empty; private string _sql = string.Empty;
private string _results = string.Empty; private List<Dictionary<string, string>> _results;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -118,7 +136,7 @@ else
{ {
var sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql }; var sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql };
sqlquery = await SqlService.ExecuteQueryAsync(sqlquery); sqlquery = await SqlService.ExecuteQueryAsync(sqlquery);
_results = DisplayResults(sqlquery.Results); _results = sqlquery.Results;
AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success); AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success);
} }
else else
@ -132,44 +150,4 @@ else
AddModuleMessage(ex.Message, MessageType.Error); AddModuleMessage(ex.Message, MessageType.Error);
} }
} }
private string DisplayResults(List<Dictionary<string, string>> results)
{
var table = string.Empty;
foreach (Dictionary<string, string> item in results)
{
if (table == string.Empty)
{
table = "<div class=\"table-responsive\">";
table += "<table class=\"table table-bordered\"><thead><tr>";
foreach (KeyValuePair<string, string> kvp in item)
{
table += "<th scope=\"col\">" + kvp.Key + "</th>";
}
table += "</tr></thead><tbody>";
}
table += "<tr>";
foreach (KeyValuePair<string, string> kvp in item)
{
table += "<td>" + kvp.Value + "</td>";
}
table += "</tr>";
}
if (table != string.Empty)
{
table += "</tbody></table></div>";
}
else
{
table = Localizer["Return.NoResult"];
}
return table;
}
} }

View File

@ -7,105 +7,79 @@
<TabStrip> <TabStrip>
<TabPanel Name="Info" Heading="Info" ResourceKey="Info"> <TabPanel Name="Info" Heading="Info" ResourceKey="Info">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="version" HelpText="Framework Version" ResourceKey="FrameworkVersion">Framework Version: </Label>
<Label For="version" HelpText="Framework Version" ResourceKey="FrameworkVersion">Framework Version: </Label> <div class="col-sm-9">
</td>
<td>
<input id="version" class="form-control" @bind="@_version" readonly /> <input id="version" class="form-control" @bind="@_version" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="clrversion" HelpText="Common Language Runtime Version" ResourceKey="CLRVersion">CLR Version: </Label>
<Label For="clrversion" HelpText="Common Language Runtime Version" ResourceKey="CLRVersion">CLR Version: </Label> <div class="col-sm-9">
</td>
<td>
<input id="clrversion" class="form-control" @bind="@_clrversion" readonly /> <input id="clrversion" class="form-control" @bind="@_clrversion" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="osversion" HelpText="Operating System Version" ResourceKey="OSVersion">OS Version: </Label>
<Label For="osversion" HelpText="Operating System Version" ResourceKey="OSVersion">OS Version: </Label> <div class="col-sm-9">
</td>
<td>
<input id="osversion" class="form-control" @bind="@_osversion" readonly /> <input id="osversion" class="form-control" @bind="@_osversion" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="serverpath" HelpText="Server Path" ResourceKey="ServerPath">Server Path: </Label>
<Label For="serverpath" HelpText="Server Path" ResourceKey="ServerPath">Server Path: </Label> <div class="col-sm-9">
</td>
<td>
<input id="serverpath" class="form-control" @bind="@_serverpath" readonly /> <input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="servertime" HelpText="Server Time" ResourceKey="ServerTime">Server Time: </Label>
<Label For="servertime" HelpText="Server Time" ResourceKey="ServerTime">Server Time: </Label> <div class="col-sm-9">
</td>
<td>
<input id="servertime" class="form-control" @bind="@_servertime" readonly /> <input id="servertime" class="form-control" @bind="@_servertime" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="installationid" HelpText="The Unique Identifier For Your Installation" ResourceKey="InstallationId">Installation ID: </Label>
<Label For="installationid" HelpText="The Unique Identifier For Your Installation" ResourceKey="InstallationId">Installation ID: </Label> <div class="col-sm-9">
</td>
<td>
<input id="installationid" class="form-control" @bind="@_installationid" readonly /> <input id="installationid" class="form-control" @bind="@_installationid" readonly />
</td> </div>
</tr> </div>
<tr> </div>
<td>&nbsp;</td>
<td>
<br /><input type="checkbox" @onchange="(e => RegisterChecked(e))" /> @Localizer["Register"]
</td>
</tr>
</table>
<br /><br /> <br /><br />
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" /> <ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
</TabPanel> </TabPanel>
<TabPanel Name="Options" Heading="Options" ResourceKey="Options"> <TabPanel Name="Options" Heading="Options" ResourceKey="Options">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)" ResourceKey="BlazorRuntime">Blazor Runtime: </Label>
<Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)" ResourceKey="BlazorRuntime">Blazor Runtime: </Label> <div class="col-sm-9">
</td>
<td>
<select id="runtime" class="form-select" @bind="@_runtime"> <select id="runtime" class="form-select" @bind="@_runtime">
<option value="Server">@Localizer["Server"]</option> <option value="Server">@Localizer["Server"]</option>
<option value="WebAssembly">@Localizer["WebAssembly"]</option> <option value="WebAssembly">@Localizer["WebAssembly"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="rendermode" HelpText="Blazor Server Render Mode" ResourceKey="RenderMode">Render Mode: </Label>
<Label For="rendermode" HelpText="Blazor Server Render Mode" ResourceKey="RenderMode">Render Mode: </Label> <div class="col-sm-9">
</td>
<td>
<select id="rendermode" class="form-select" @bind="@_rendermode"> <select id="rendermode" class="form-select" @bind="@_rendermode">
<option value="Server">@Localizer["Server"]</option> <option value="Server">@Localizer["Server"]</option>
<option value="ServerPrerendered">@Localizer["ServerPrerendered"]</option> <option value="ServerPrerendered">@Localizer["ServerPrerendered"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="detailederrors" HelpText="Specify If Detailed Errors Are Enabled For Blazor. This Option Should Not Not Be Enabled In Production." ResourceKey="DetailedErrors">Detailed Errors? </Label>
<Label For="detailederrors" HelpText="Specify If Detailed Errors Are Enabled For Blazor. This Option Should Not Not Be Enabled In Production." ResourceKey="DetailedErrors">Detailed Errors? </Label> <div class="col-sm-9">
</td>
<td>
<select id="detailederrors" class="form-select" @bind="@_detailederrors"> <select id="detailederrors" class="form-select" @bind="@_detailederrors">
<option value="true">@SharedLocalizer["True"]</option> <option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option> <option value="false">@SharedLocalizer["False"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="logginglevel" HelpText="The Minimum Logging Level For The Event Log. This Option Can Be Used To Control The Volume Of Items Stored In Your Event Log." ResourceKey="LoggingLevel">Logging Level: </Label>
<Label For="logginglevel" HelpText="The Minimum Logging Level For The Event Log. This Option Can Be Used To Control The Volume Of Items Stored In Your Event Log." ResourceKey="LoggingLevel">Logging Level: </Label> <div class="col-sm-9">
</td>
<td>
<select id="logginglevel" class="form-select" @bind="@_logginglevel"> <select id="logginglevel" class="form-select" @bind="@_logginglevel">
<option value="Trace">@Localizer["Trace"]</option> <option value="Trace">@Localizer["Trace"]</option>
<option value="Debug">@Localizer["Debug"]</option> <option value="Debug">@Localizer["Debug"]</option>
@ -114,31 +88,27 @@
<option value="Error">@Localizer["Error"]</option> <option value="Error">@Localizer["Error"]</option>
<option value="Critical">@Localizer["Critical"]</option> <option value="Critical">@Localizer["Critical"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="swagger" HelpText="Specify If Swagger Is Enabled For Your Server API" ResourceKey="Swagger">Swagger Enabled? </Label>
<Label For="swagger" HelpText="Specify If Swagger Is Enabled For Your Server API" ResourceKey="Swagger">Swagger Enabled? </Label> <div class="col-sm-9">
</td>
<td>
<select id="swagger" class="form-select" @bind="@_swagger"> <select id="swagger" class="form-select" @bind="@_swagger">
<option value="true">@SharedLocalizer["True"]</option> <option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option> <option value="false">@SharedLocalizer["False"]</option>
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="packageservice" HelpText="Specify If The Package Service Is Enabled For Installing Modules, Themes, And Translations" ResourceKey="PackageService">Enable Package Service? </Label>
<Label For="packageservice" HelpText="Specify If The Package Service Is Enabled For Installing Modules, Themes, And Translations" ResourceKey="PackageService">Enable Package Service? </Label> <div class="col-sm-9">
</td>
<td>
<select id="packageservice" class="form-select" @bind="@_packageservice"> <select id="packageservice" class="form-select" @bind="@_packageservice">
<option value="true">@SharedLocalizer["True"]</option> <option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option> <option value="false">@SharedLocalizer["False"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
<br /><br /> <br /><br />
<button type="button" class="btn btn-success" @onclick="SaveConfig">@SharedLocalizer["Save"]</button>&nbsp; <button type="button" class="btn btn-success" @onclick="SaveConfig">@SharedLocalizer["Save"]</button>&nbsp;
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</a>&nbsp; <a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</a>&nbsp;
@ -220,20 +190,4 @@
await logger.LogError(ex, "Error Restarting Application"); await logger.LogError(ex, "Error Restarting Application");
} }
} }
private async Task RegisterChecked(ChangeEventArgs e)
{
try
{
if ((bool)e.Value)
{
await InstallationService.RegisterAsync(PageState.User.Email);
AddModuleMessage(Localizer["Success.Register"], MessageType.Success);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On Register");
}
}
} }

View File

@ -9,19 +9,19 @@
<TabStrip> <TabStrip>
<TabPanel Name="Download" ResourceKey="Download"> <TabPanel Name="Download" ResourceKey="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more themes from the list below. Once you are ready click Install to complete the installation."></ModuleMessage> <div class="row justify-content-center mb-3">
<div class="col-sm-6">
<table class="table table-borderless" style=" margin: auto; width: 50% !important;"> <div class="input-group">
<tr> <select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
<td> <option value="free">@SharedLocalizer["Free"]</option>
<option value="paid">@SharedLocalizer["Paid"]</option>
</select>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" /> <input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
</td> <button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<td>
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>&nbsp;
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button> <button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
</td> </div>
</tr> </div>
</table> </div>
@if (_packages != null) @if (_packages != null)
{ {
@ -32,10 +32,26 @@
<td> <td>
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;@SharedLocalizer["Search.By"]:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br /> <h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3>&nbsp;&nbsp;@SharedLocalizer["Search.By"]:&nbsp;&nbsp;<strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br /> @(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>&nbsp;&nbsp;|&nbsp;&nbsp;@SharedLocalizer["Search.Source"]: <strong>@context.PackageUrl</strong> <strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td> </td>
<td style="vertical-align: middle;"> <td style="width: 1px; vertical-align: middle;">
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadTheme(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button> @if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
}
else
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td> </td>
</Row> </Row>
</Pager> </Pager>
@ -50,25 +66,62 @@
} }
</TabPanel> </TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload"> <TabPanel Name="Upload" ResourceKey="Upload">
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label>
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="@true" /> <FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="@true" />
</td>
</tr> </div>
</table> </div>
</div>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
@if (_productname != "")
{
<div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
</div>
<div class="modal-body">
<p style="height: 200px; overflow-y: scroll;">
<h3>@_productname</h3>
@if (!string.IsNullOrEmpty(_license))
{
@((MarkupString)_license)
}
else
{
@SharedLocalizer["License Not Specified"]
}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
<button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button> <button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {
private List<Package> _packages; private List<Package> _packages;
private string _price = "free";
private string _search = ""; private string _search = "";
private string _productname = "";
private string _license = "";
private string _packageid = "";
private string _version = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -88,7 +141,7 @@
private async Task LoadThemes() private async Task LoadThemes()
{ {
var themes = await ThemeService.GetThemesAsync(); var themes = await ThemeService.GetThemesAsync();
_packages = await PackageService.GetPackagesAsync("theme", _search); _packages = await PackageService.GetPackagesAsync("theme", _search, _price, "");
if (_packages != null) if (_packages != null)
{ {
@ -102,6 +155,21 @@
} }
} }
private async void PriceChanged(ChangeEventArgs e)
{
try
{
_price = (string)e.Value;
_search = "";
await LoadThemes();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On PriceChanged");
}
}
private async Task Search() private async Task Search()
{ {
try try
@ -127,6 +195,55 @@
} }
} }
private void HideModal()
{
_productname = "";
_license = "";
StateHasChanged();
}
private async Task GetPackage(string packageid, string version)
{
try
{
var package = await PackageService.GetPackageAsync(packageid, version);
if (package != null)
{
_productname = package.Name;
if (!string.IsNullOrEmpty(package.License))
{
_license = package.License.Replace("\n", "<br />");
}
_packageid = package.PackageId;
_version = package.Version;
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Theme.Download"], MessageType.Error);
}
}
private async Task DownloadPackage()
{
try
{
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages");
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Theme.Download"], MessageType.Success);
_productname = "";
_license = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version);
AddModuleMessage(Localizer["Error.Theme.Download"], MessageType.Error);
}
}
private async Task InstallThemes() private async Task InstallThemes()
{ {
try try
@ -139,20 +256,4 @@
await logger.LogError(ex, "Error Installing Theme"); await logger.LogError(ex, "Error Installing Theme");
} }
} }
private async Task DownloadTheme(string packageid, string version)
{
try
{
await PackageService.DownloadPackageAsync(packageid, version, "Packages");
await logger.LogInformation("Theme {ThemeName} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage(Localizer["Success.Theme.Download"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ThemeName} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Theme.Download"], MessageType.Error);
}
}
} }

View File

@ -11,28 +11,22 @@
@if (_templates != null) @if (_templates != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="owner" HelpText="Enter the name of the organization who is developing this theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
<Label For="owner" HelpText="Enter the name of the organization who is developing this theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label> <div class="col-sm-9">
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" /> <input id="owner" class="form-control" @bind="@_owner" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label>
<Label For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label> <div class="col-sm-9">
</td>
<td>
<input id="module" class="form-control" @bind="@_theme" /> <input id="module" class="form-control" @bind="@_theme" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label>
<Label For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label> <div class="col-sm-9">
</td>
<td>
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))"> <select id="template" class="form-select" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates) @foreach (Template template in _templates)
@ -40,13 +34,11 @@
<option value="@template.Name">@template.Title</option> <option value="@template.Name">@template.Title</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label> <div class="col-sm-9">
</td>
<td>
<select id="reference" class="form-select" @bind="@_reference"> <select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions) @foreach (string version in _versions)
{ {
@ -57,20 +49,18 @@
} }
<option value="local">@SharedLocalizer["LocalVersion"]</option> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</select> </select>
</td> </div>
</tr> </div>
@if (!string.IsNullOrEmpty(_location)) @if (!string.IsNullOrEmpty(_location)) {
{ <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
<td> <div class="col-sm-9">
<Label For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly /> <input id="module" class="form-control" @bind="@_location" readonly />
</td> </div>
</tr> </div>
} }
</table> </div>
<br />
<button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Theme.Create"]</button> <button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Theme.Create"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
} }

View File

@ -21,8 +21,9 @@ else
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th scope="col">@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
<th scope="col">@SharedLocalizer["Version"]</th> <th>@SharedLocalizer["Version"]</th>
<th>@SharedLocalizer["Expires"]</th>
<th>&nbsp;</th> <th>&nbsp;</th>
</Header> </Header>
<Row> <Row>
@ -35,6 +36,9 @@ else
</td> </td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.Version</td> <td>@context.Version</td>
<td>
@((MarkupString)PurchaseLink(context.PackageName))
</td>
<td> <td>
@if (UpgradeAvailable(context.PackageName, context.Version)) @if (UpgradeAvailable(context.PackageName, context.Version))
{ {
@ -69,10 +73,31 @@ else
} }
} }
private string PurchaseLink(string packagename)
{
string link = "";
if (!string.IsNullOrEmpty(packagename) && _packages != null)
{
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null)
{
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
if (!string.IsNullOrEmpty(package.PaymentUrl))
{
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
}
}
}
}
return link;
}
private bool UpgradeAvailable(string packagename, string version) private bool UpgradeAvailable(string packagename, string version)
{ {
var upgradeavailable = false; var upgradeavailable = false;
if (_packages != null) if (!string.IsNullOrEmpty(packagename) && _packages != null)
{ {
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault(); var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null) if (package != null)

View File

@ -6,64 +6,50 @@
@inject IStringLocalizer<View> Localizer @inject IStringLocalizer<View> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="name" HelpText="The name of the theme" ResourceKey="Name">Name: </Label>
<Label For="name" HelpText="The name of the theme" ResourceKey="Name">Name: </Label> <div class="col-sm-9">
</td>
<td>
<input id="name" class="form-control" @bind="@_name" disabled /> <input id="name" class="form-control" @bind="@_name" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="themename" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
<Label For="themename" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label> <div class="col-sm-9">
</td>
<td>
<input id="themename" class="form-control" @bind="@_themeName" disabled /> <input id="themename" class="form-control" @bind="@_themeName" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="version" HelpText="The version of the theme" ResourceKey="Version">Version: </Label>
<Label For="version" HelpText="The version of the theme" ResourceKey="Version">Version: </Label> <div class="col-sm-9">
</td>
<td>
<input id="version" class="form-control" @bind="@_version" disabled /> <input id="version" class="form-control" @bind="@_version" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label>
<Label For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label> <div class="col-sm-9">
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" disabled /> <input id="owner" class="form-control" @bind="@_owner" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="url" HelpText="The reference url of the theme" ResourceKey="ReferenceUrl">Reference Url: </Label>
<Label For="url" HelpText="The reference url of the theme" ResourceKey="ReferenceUrl">Reference Url: </Label> <div class="col-sm-9">
</td>
<td>
<input id="url" class="form-control" @bind="@_url" disabled /> <input id="url" class="form-control" @bind="@_url" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="contact" HelpText="The contact for the theme" ResourceKey="Contact">Contact: </Label>
<Label For="contact" HelpText="The contact for the theme" ResourceKey="Contact">Contact: </Label> <div class="col-sm-9">
</td>
<td>
<input id="contact" class="form-control" @bind="@_contact" disabled /> <input id="contact" class="form-control" @bind="@_contact" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="license" HelpText="The license of the theme" ResourceKey="License">License: </Label>
<Label For="license" HelpText="The license of the theme" ResourceKey="License">License: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea> <textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td> </div>
</tr> </div>
</table> </div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {

View File

@ -22,16 +22,14 @@
</TabPanel> </TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload"> <TabPanel Name="Upload" ResourceKey="Upload">
<ModuleMessage Type="MessageType.Info" Message="Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade"></ModuleMessage> <ModuleMessage Type="MessageType.Info" Message="Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade"></ModuleMessage>
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label>
<Label HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label> <div class="col-sm-9">
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" /> <FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" />
</td> </div>
</tr> </div>
</table> </div>
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button> <button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
@ -46,7 +44,7 @@
{ {
try try
{ {
List<Package> packages = await PackageService.GetPackagesAsync("framework"); List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", "");
if (packages != null) if (packages != null)
{ {
_package = packages.Where(item => item.PackageId.StartsWith(Constants.PackageId)).FirstOrDefault(); _package = packages.Where(item => item.PackageId.StartsWith(Constants.PackageId)).FirstOrDefault();
@ -73,7 +71,7 @@
AddModuleMessage(Localizer["Info.Upgrade.Wait"], MessageType.Info); AddModuleMessage(Localizer["Info.Upgrade.Wait"], MessageType.Info);
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 20); await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade(); await InstallationService.Upgrade();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -8,32 +8,27 @@
@if (PageState.User != null) @if (PageState.User != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
<Label For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label> <div class="col-sm-9">
</td>
<td>
<input id="to" class="form-control" @bind="@username" /> <input id="to" class="form-control" @bind="@username" />
</td> </div> >
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
<Label For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label> <div class="col-sm-9">
</td>
<td>
<input id="subject" class="form-control" @bind="@subject" /> <input id="subject" class="form-control" @bind="@subject" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="message" HelpText="Enter the message" ResourceKey="Message">Message: </Label>
<Label For="message" HelpText="Enter the message" ResourceKey="Message">Message: </Label> <div class="col-sm-9">
</td>
<td>
<textarea id="message" class="form-control" @bind="@body" rows="5" /> <textarea id="message" class="form-control" @bind="@body" rows="5" />
</td> </div>
</tr> </div>
</table> </div>
<br/>
<button type="button" class="btn btn-primary" @onclick="Send">@SharedLocalizer["Send"]</button> <button type="button" class="btn btn-primary" @onclick="Send">@SharedLocalizer["Send"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
} }

View File

@ -6,12 +6,13 @@
@inject ISettingService SettingService @inject ISettingService SettingService
@inject INotificationService NotificationService @inject INotificationService NotificationService
@inject IFileService FileService @inject IFileService FileService
@inject IFolderService FolderService
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (PageState.User != null && photo != null) @if (PageState.User != null && photo != null)
{ {
<img src="@photo.Url" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block"> <img src="@ImageUrl(photofileid, 400, 400, "crop")" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
} }
else else
{ {
@ -19,58 +20,47 @@ else
} }
<TabStrip> <TabStrip>
<TabPanel Name="Identity" ResourceKey="Identity"> <TabPanel Name="Identity" ResourceKey="Identity">
@if (PageState.User != null) @if (profiles != null && settings != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified." ResourceKey="Username"></Label>
<Label For="username" HelpText="Your username. Note that this field can not be modified." ResourceKey="Username"></Label> <div class="col-sm-9">
</td>
<td>
<input id="username" class="form-control" @bind="@username" readonly /> <input id="username" class="form-control" @bind="@username" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label>
<Label For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label> <div class="col-sm-9">
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@password" autocomplete="new-password" /> <input id="password" type="password" class="form-control" @bind="@password" autocomplete="new-password" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
<Label For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label> <div class="col-sm-9">
</td>
<td>
<input id="confirm" type="password" class="form-control" @bind="@confirm" autocomplete="new-password" /> <input id="confirm" type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
<Label For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label> <div class="col-sm-9">
</td>
<td>
<input id="email" class="form-control" @bind="@email" /> <input id="email" class="form-control" @bind="@email" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
<Label For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label> <div class="col-sm-9">
</td>
<td>
<input id="displayname" class="form-control" @bind="@displayname" /> <input id="displayname" class="form-control" @bind="@displayname" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
<Label For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label> <div class="col-sm-9">
</td> <FileManager FileId="@photofileid" Filter="@Constants.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
<td> </div>
<FileManager FileId="@photofileid" @ref="filemanager" /> </div>
</td> </div>
</tr> <br />
</table>
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
} }
@ -78,7 +68,8 @@ else
<TabPanel Name="Profile" ResourceKey="Profile"> <TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null && settings != null) @if (profiles != null && settings != null)
{ {
<table class="table table-borderless"> <div class="container">
<div class="row mb-1 align-items-center">
@foreach (Profile profile in profiles) @foreach (Profile profile in profiles)
{ {
var p = profile; var p = profile;
@ -86,18 +77,14 @@ else
{ {
if (p.Category != category) if (p.Category != category)
{ {
<tr> <div class="col text-center pb-2">
<th colspan="2" style="text-align: center;">
@p.Category @p.Category
</th> </div>
</tr>
category = p.Category; category = p.Category;
} }
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label> <div class="col-sm-9">
</td>
<td>
@if (!string.IsNullOrEmpty(p.Options)) @if (!string.IsNullOrEmpty(p.Options))
{ {
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))"> <select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
@ -125,11 +112,12 @@ else
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" /> <input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
} }
} }
</td> </div>
</tr> </div>
} }
} }
</table> </div>
</div>
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
} }
@ -221,6 +209,7 @@ else
private string email = string.Empty; private string email = string.Empty;
private string displayname = string.Empty; private string displayname = string.Empty;
private FileManager filemanager; private FileManager filemanager;
private int folderid = -1;
private int photofileid = -1; private int photofileid = -1;
private File photo = null; private File photo = null;
private List<Profile> profiles; private List<Profile> profiles;
@ -241,6 +230,13 @@ else
email = PageState.User.Email; email = PageState.User.Email;
displayname = PageState.User.DisplayName; displayname = PageState.User.DisplayName;
// get user folder
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
if (folder != null)
{
folderid = folder.FolderId;
}
if (PageState.User.PhotoFileId != null) if (PageState.User.PhotoFileId != null)
{ {
photofileid = PageState.User.PhotoFileId.Value; photofileid = PageState.User.PhotoFileId.Value;

View File

@ -8,75 +8,71 @@
@if (PageState.User != null) @if (PageState.User != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <label Class="col-sm-3">@Localizer["Title"] </label>
<label class="control-label">@Localizer["Title"] </label>
</td>
@if (title == "From") @if (title == "From")
{ {
<td> <div class="col-sm-3">
<input class="form-control" @bind="@username" readonly /> <input class="form-control" @bind="@username" readonly />
</td> </div>
} }
@if (title == "To") @if (title == "To")
{ {
<td> <div class="col-sm-3">
<input class="form-control" @bind="@username" /> <input class="form-control" @bind="@username" />
</td> </div>
} }
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <label Class="col-sm-3">@Localizer["Subject"] </label>
<label class="control-label">@Localizer["Subject"] </label>
</td>
@if (title == "From") @if (title == "From")
{ {
<td> <div class="col-sm-3">
<input class="form-control" @bind="@subject" readonly /> <input class="form-control" @bind="@subject" readonly />
</td> </div>
} }
@if (title == "To") @if (title == "To")
{ {
<td> <div class="col-sm-3">
<input class="form-control" @bind="@subject" /> <input class="form-control" @bind="@subject" />
</td> </div>
} }
</tr> </div>
</div>
<div class="container">
@if (title == "From") @if (title == "From")
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <label class="col-sm-3">@Localizer["Date"] </label>
<label class="control-label">@Localizer["Date"] </label> <div class="col-sm-9">
</td>
<td>
<input class="form-control" @bind="@createdon" readonly /> <input class="form-control" @bind="@createdon" readonly />
</td> </div>
</tr> </div>
} }
@if (title == "From") @if (title == "From")
{ {
<tr> <div class="row mb-1 align-items-center">
<td> <label class="col-sm-3">@Localizer["Message"] </label>
<label class="control-label">@Localizer["Message"] </label> <div class="col-sm-9">
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" readonly /> <textarea class="form-control" @bind="@body" rows="5" readonly />
</td> </div>
</tr> </div>
} }
@if (title == "To") @if (title == "To")
{ {
<tr>
<td> <div class="row mb-1 align-items-center">
<label class="control-label">@Localizer["Message"] </label> <label class="col-sm-3">@Localizer["Message"] </label>
</td> <div class="col-sm-9">
<td> <textarea class="form-control" @bind="@body" rows="5" readonly />
<textarea class="form-control" @bind="@body" rows="5" /> </div>
</td> </div>
</tr>
} }
</table>
</div>
@if (reply != string.Empty) @if (reply != string.Empty)

View File

@ -11,71 +11,59 @@
<TabPanel Name="Identity" ResourceKey="Identity"> <TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null) @if (profiles != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
<Label For="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label> <div class="col-sm-9">
</td>
<td>
<input id="username" class="form-control" @bind="@username" /> <input id="username" class="form-control" @bind="@username" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
<Label For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label> <div class="col-sm-9">
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@password" /> <input id="password" type="password" class="form-control" @bind="@password" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
<Label For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label> <div class="col-sm-9">
</td>
<td>
<input id="confirm" type="password" class="form-control" @bind="@confirm" /> <input id="confirm" type="password" class="form-control" @bind="@confirm" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label>
<Label For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label> <div class="col-sm-9">
</td>
<td>
<input id="email" class="form-control" @bind="@email" /> <input id="email" class="form-control" @bind="@email" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label>
<Label For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label> <div class="col-sm-9">
</td>
<td>
<input id="displayname" class="form-control" @bind="@displayname" /> <input id="displayname" class="form-control" @bind="@displayname" />
</td> </div>
</tr> </div>
</table> </div>
} }
</TabPanel> </TabPanel>
<TabPanel Name="Profile" ResourceKey="Profile"> <TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null) @if (profiles != null)
{ {
<table class="table table-borderless"> <div class="container">
<div class="row mb-1 align-items-center">
@foreach (Profile profile in profiles) @foreach (Profile profile in profiles)
{ {
var p = profile; var p = profile;
if (p.Category != category) if (p.Category != category)
{ {
<tr> <div class="col text-center pb-2">
<th colspan="2" style="text-align: center;"> <strong>@p.Category</strong>
@p.Category </div>
</th>
</tr>
category = p.Category; category = p.Category;
} }
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label> <div class="col-sm-9">
</td>
<td>
@if (p.IsRequired) @if (p.IsRequired)
{ {
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" /> <input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
@ -84,14 +72,17 @@
{ {
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" /> <input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
} }
</td> </div>
</tr> </div>
} }
</table>
</div>
</div>
} }
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
<br />
<br />
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>

View File

@ -20,90 +20,92 @@ else
<TabPanel Name="Identity" ResourceKey="Identity"> <TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null) @if (profiles != null)
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username"></Label>
<Label For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username"></Label> <div class="col-sm-9">
</td>
<td>
<input id="username" class="form-control" @bind="@username" readonly /> <input id="username" class="form-control" @bind="@username" readonly />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
<Label For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label> <div class="col-sm-9">
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@password" /> <input id="password" type="password" class="form-control" @bind="@password" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
<Label For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label> <div class="col-sm-9">
</td>
<td>
<input id="confirm" type="password" class="form-control" @bind="@confirm" /> <input id="confirm" type="password" class="form-control" @bind="@confirm" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label>
<Label For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label> <div class="col-sm-9">
</td>
<td>
<input id="email" class="form-control" @bind="@email" /> <input id="email" class="form-control" @bind="@email" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label>
<Label For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label> <div class="col-sm-9">
</td>
<td>
<input id="displayname" class="form-control" @bind="@displayname" /> <input id="displayname" class="form-control" @bind="@displayname" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of the user" ResourceKey="Photo"></Label>
<Label For="@photofileid.ToString()" HelpText="A photo of the user" ResourceKey="Photo"></Label> <div class="col-sm-9">
</td>
<td>
<FileManager FileId="@photofileid" @ref="filemanager" /> <FileManager FileId="@photofileid" @ref="filemanager" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="isdeleted" HelpText="Indicate if the user is active" ResourceKey="IsDeleted"></Label>
<Label For="isdeleted" HelpText="Indicate if the user is active" ResourceKey="IsDeleted"></Label> <div class="col-sm-9">
</td>
<td>
<select id="isdeleted" class="form-select" @bind="@isdeleted"> <select id="isdeleted" class="form-select" @bind="@isdeleted">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
} }
</TabPanel> </TabPanel>
<TabPanel Name="Profile" ResourceKey="Profile"> <TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null) @if (profiles != null)
{ {
<table class="table table-borderless"> <div class="container">
<div class="row mb-1 align-items-center">
@foreach (Profile profile in profiles) @foreach (Profile profile in profiles)
{ {
var p = profile; var p = profile;
if (p.Category != category) if (p.Category != category)
{ {
<tr> <div class="col text-center pb-2">
<th colspan="2" style="text-align: center;"> <strong>@p.Category</strong>
@p.Category </div>
</th>
</tr>
category = p.Category; category = p.Category;
} }
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label> <div class="col-sm-9">
</td> @if (!string.IsNullOrEmpty(p.Options))
<td> {
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
@if (GetProfileValue(p.Name, "") == option || (GetProfileValue(p.Name, "") == "" && p.DefaultValue == option))
{
<option value="@option" selected>@option</option>
}
else
{
<option value="@option">@option</option>
}
}
</select>
}
else
{
@if (p.IsRequired) @if (p.IsRequired)
{ {
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" /> <input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
@ -112,10 +114,12 @@ else
{ {
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" /> <input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
} }
</td>
</tr>
} }
</table> </div>
</div>
}
</div>
</div>
} }
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>

View File

@ -14,20 +14,19 @@
} }
else else
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td> <div class="col-sm-4">
<div><ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" /></div> <ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" />
</td> </div>
<td> <div class="col-sm-4">
<input class="form-control" @bind="@_search" /> <input class="form-control" @bind="@_search" />
</td> </div>
<td> <div class="col-sm-4">
<button class="btn btn-secondary" @onclick="OnSearch">@SharedLocalizer["Search"]</button> <button class="btn btn-secondary" @onclick="OnSearch">@SharedLocalizer["Search"]</button>
</td> </div>
</tr> </div>
</table> </div>
<Pager Items="@userroles"> <Pager Items="@userroles">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
@ -40,7 +39,7 @@ else
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" /> <ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" />
</td> </td>
<td> <td>
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.Role.Name == RoleNames.Host)" ResourceKey="DeleteUser" /> <ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
</td> </td>
<td> <td>
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" /> <ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
@ -96,6 +95,8 @@ else
{ {
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId); await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
await logger.LogInformation("User Deleted {User}", UserRole.User); await logger.LogInformation("User Deleted {User}", UserRole.User);
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
userroles = Search(_search);
StateHasChanged(); StateHasChanged();
} }
} }

View File

@ -12,20 +12,16 @@
} }
else else
{ {
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="user" HelpText="The user you are assigning roles to" ResourceKey="User">User: </Label>
<Label For="user" HelpText="The user you are assigning roles to" ResourceKey="User">User: </Label> <div class="col-sm-9">
</td>
<td>
<input id="user" class="form-control" @bind="@name" disabled /> <input id="user" class="form-control" @bind="@name" disabled />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="role" HelpText="Select a role" ResourceKey="Role">Role: </Label>
<Label For="role" HelpText="Select a role" ResourceKey="Role">Role: </Label> <div class="col-sm-9">
</td>
<td>
<select id="role" class="form-select" @bind="@roleid"> <select id="role" class="form-select" @bind="@roleid">
<option value="-1">&lt;@Localizer["Role.Select"]&gt;</option> <option value="-1">&lt;@Localizer["Role.Select"]&gt;</option>
@foreach (Role role in roles) @foreach (Role role in roles)
@ -33,28 +29,26 @@ else
<option value="@(role.RoleId)">@role.Name</option> <option value="@(role.RoleId)">@role.Name</option>
} }
</select> </select>
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<Label For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label> <div class="col-sm-9">
</td>
<td>
<input id="effectiveDate" class="form-control" @bind="@effectivedate" /> <input id="effectiveDate" class="form-control" @bind="@effectivedate" />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<Label For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label> <div class="col-sm-9">
</td>
<td>
<input id="expiryDate" class="form-control" @bind="@expirydate" /> <input id="expiryDate" class="form-control" @bind="@expirydate" />
</td> </div>
</tr> </div>
</table>
</div>
<br />
<br />
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<hr class="app-rule" /> <hr class="app-rule" />
<p align="center"> <p align="center">
<Pager Items="@userroles"> <Pager Items="@userroles">

View File

@ -58,7 +58,6 @@
</span> </span>
</div> </div>
} }
<ModuleMessage Message="@_message" Type="@_messagetype"></ModuleMessage>
</div> </div>
@if (_image != string.Empty) @if (_image != string.Empty)
{ {
@ -67,6 +66,14 @@
</div> </div>
} }
</div> </div>
@if (!string.IsNullOrEmpty(_message))
{
<div class="row">
<div class="col mt-2">
<ModuleMessage Message="@_message" Type="@_messagetype" />
</div>
</div>
}
</div> </div>
} }
@ -89,7 +96,7 @@
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
[Parameter] [Parameter]
public string Folder { get; set; } // optional - for setting a specific folder by default public string Folder { get; set; } // optional - for setting a specific folder by default ( only relevant for host functions )
[Parameter] [Parameter]
public int FolderId { get; set; } = -1; // optional - for setting a specific folderid by default public int FolderId { get; set; } = -1; // optional - for setting a specific folderid by default
@ -104,7 +111,10 @@
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
[Parameter] [Parameter]
public int FileId { get; set; } = -1; // optional - for setting a specific file by default public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
[Parameter]
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default
[Parameter] [Parameter]
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif" public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
@ -112,6 +122,15 @@
[Parameter] [Parameter]
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
[Parameter]
public EventCallback<int> OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded
[Parameter]
public EventCallback<int> OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected
[Parameter]
public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
if (!string.IsNullOrEmpty(Id)) if (!string.IsNullOrEmpty(Id))
@ -119,6 +138,11 @@
_id = Id; _id = Id;
} }
if (!ShowFiles)
{
ShowImage = false;
}
if (!string.IsNullOrEmpty(Folder)) if (!string.IsNullOrEmpty(Folder))
{ {
_folders = new List<Folder> { new Folder { FolderId = -1, Name = Folder } }; _folders = new List<Folder> { new Folder { FolderId = -1, Name = Folder } };
@ -135,12 +159,14 @@
if (file != null) if (file != null)
{ {
FolderId = file.FolderId; FolderId = file.FolderId;
await OnSelect.InvokeAsync(FileId);
} }
else else
{ {
FileId = -1; // file does not exist FileId = -1; // file does not exist
} }
} }
await SetImage(); await SetImage();
if (!string.IsNullOrEmpty(Filter)) if (!string.IsNullOrEmpty(Filter))
@ -218,6 +244,10 @@
{ {
_message = string.Empty; _message = string.Empty;
FileId = int.Parse((string)e.Value); FileId = int.Parse((string)e.Value);
if (FileId != -1)
{
await OnSelect.InvokeAsync(FileId);
}
await SetImage(); await SetImage();
StateHasChanged(); StateHasChanged();
@ -230,7 +260,7 @@
if (FileId != -1) if (FileId != -1)
{ {
_file = await FileService.GetFileAsync(FileId); _file = await FileService.GetFileAsync(FileId);
if (_file != null && _file.ImageHeight != 0 && _file.ImageWidth != 0) if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0)
{ {
var maxwidth = 200; var maxwidth = 200;
var maxheight = 200; var maxheight = 200;
@ -272,16 +302,14 @@
_message = Localizer["Success.File.Upload"]; _message = Localizer["Success.File.Upload"];
_messagetype = MessageType.Success; _messagetype = MessageType.Success;
// set FileId to first file in upload collection
await GetFiles(); await GetFiles();
if (upload.Length == 1)
{
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
if (file != null) if (file != null)
{ {
FileId = file.FileId; FileId = file.FileId;
await SetImage(); await SetImage();
} await OnUpload.InvokeAsync(FileId);
} }
StateHasChanged(); StateHasChanged();
} }
@ -315,6 +343,7 @@
{ {
await FileService.DeleteFileAsync(FileId); await FileService.DeleteFileAsync(FileId);
await logger.LogInformation("File Deleted {File}", FileId); await logger.LogInformation("File Deleted {File}", FileId);
await OnDelete.InvokeAsync(FileId);
_message = Localizer["Success.File.Delete"]; _message = Localizer["Success.File.Delete"];
_messagetype = MessageType.Success; _messagetype = MessageType.Success;

View File

@ -3,17 +3,16 @@
@if (!string.IsNullOrEmpty(HelpText)) @if (!string.IsNullOrEmpty(HelpText))
{ {
<span class="app-tooltip" data-tip="@((MarkupString)HelpText)">@((MarkupString)_openLabel)@ChildContent@((MarkupString)_closeLabel) <img src="images/help.png" /></span> <span class="@_spanclass" data-tip="@((MarkupString)@_helptext)">
<label for="@For" class="@_labelclass">@ChildContent</label> <img src="images/help.png" />
</span>
} }
else else
{ {
@((MarkupString)_openLabel)@ChildContent@((MarkupString)_closeLabel) <label for="@For" class="@_labelclass">@ChildContent</label>
} }
@code { @code {
private string _openLabel = string.Empty;
private string _closeLabel = "</label>";
[Parameter] [Parameter]
public RenderFragment ChildContent { get; set; } public RenderFragment ChildContent { get; set; }
@ -21,39 +20,33 @@ else
public string For { get; set; } // optional - the id of the associated input control for accessibility public string For { get; set; } // optional - the id of the associated input control for accessibility
[Parameter] [Parameter]
public string Class { get; set; } // optional - the class for the label ( ie. control-label ) public string Class { get; set; } // optional - CSS classes
[Parameter] [Parameter]
public string HelpText { get; set; } // optional - tooltip for this label public string HelpText { get; set; } // optional - tooltip for this label
private string _spanclass = "app-tooltip";
private string _labelclass = "form-label";
private string _helptext = string.Empty;
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
base.OnParametersSet(); base.OnParametersSet();
if (string.IsNullOrEmpty(Class)) if (!string.IsNullOrEmpty(HelpText))
{ {
Class = "form-label"; _helptext = Localize(nameof(HelpText), HelpText);
_spanclass += (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
} }
else
_openLabel = "<label";
if (!string.IsNullOrEmpty(For))
{ {
_openLabel += " for=\"" + For + "\""; _labelclass += (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
} }
if (!string.IsNullOrEmpty(Class))
{
_openLabel += " class=\"" + Class + "\"";
}
_openLabel += ">";
var text = Localize("Text", String.Empty); var text = Localize("Text", String.Empty);
if (text != String.Empty) if (!string.IsNullOrEmpty(text))
{ {
ChildContent =@<text>@text</text>; ChildContent =@<text>@text</text>;
} }
HelpText = Localize(nameof(HelpText), HelpText);
} }
} }

View File

@ -4,7 +4,7 @@
@if (!string.IsNullOrEmpty(_message)) @if (!string.IsNullOrEmpty(_message))
{ {
<div class="@_classname alert-dismissible fade show" role="alert"> <div class="@_classname alert-dismissible fade show mb-3" role="alert">
@((MarkupString)_message) @((MarkupString)_message)
@if (Type == MessageType.Error && PageState != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @if (Type == MessageType.Error && PageState != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
@ -12,7 +12,6 @@
} }
<button type="button" class="btn-close" aria-label="Close" @onclick="DismissModal"></button> <button type="button" class="btn-close" aria-label="Close" @onclick="DismissModal"></button>
</div> </div>
<br />
} }
@code { @code {

View File

@ -2,45 +2,57 @@
@inherits ModuleControlBase @inherits ModuleControlBase
@typeparam TableItem @typeparam TableItem
<p> @if (ItemList != null)
@if (Toolbar == "Top")
{ {
<div class="mx-auto text-center"> @if (Toolbar == "Top" && _pages > 0 && Items.Count() > _maxItems)
@if (_endPage > 1)
{ {
<button class="btn btn-secondary m-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button> <ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
</li>
} }
@if (_page > _maxPages) <li class="page-item@((_page > 1) ? "" : " disabled")">
{ <a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
<button class="btn btn-secondary m-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button> </li>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary m-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++) @for (int i = _startPage; i <= _endPage; i++)
{ {
var pager = i; var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))> if (pager == _page)
@pager
</button>
}
<button class="btn btn-secondary m-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
{ {
<button class="btn btn-secondary m-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button> <li class="page-item active">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
} }
@if (_endPage > 1) else
{ {
<button class="btn btn-secondary m-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button> <li class="page-item">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
} }
@if (_endPage > 1) }
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{ {
<span class="btn btn-link disabled">Page @_page of @_pages</span> <li class="page-item@((_endPage < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
</li>
} }
</div> <li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">
<a class="page-link">Page @_page of @_pages</a>
</li>
</ul>
} }
@if (Format == "Table") @if (Format == "Table" && Row != null)
{ {
<table class="@Class"> <table class="@Class">
<thead> <thead>
@ -58,90 +70,125 @@
</tbody> </tbody>
</table> </table>
} }
@if (Format == "Grid") @if (Format == "Grid" && Row != null)
{ {
int count = 0;
if (ItemList != null)
{
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
}
<div class="@Class"> <div class="@Class">
<div class="row">@Header</div> @if (Header != null)
@foreach (var item in ItemList)
{ {
<div class="row">@Row(item)</div> <div class="row"><div class="col">@Header</div></div>
@if (Detail != null) }
@for (int row = 0; row < (count / _columns); row++)
{ {
<div class="row">@Detail(item)</div> <div class="row">
@for (int col = 0; col < _columns; col++)
{
int index = (row * _columns) + col;
if (index < ItemList.Count())
{
<div class="col">@Row(ItemList.ElementAt(index))</div>
}
else
{
<div class="col">&nbsp;</div>
} }
} }
</div> </div>
} }
@if (Toolbar == "Bottom") </div>
{
<div class="mx-auto text-center">
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
} }
@if (_page > _maxPages) @if (Toolbar == "Bottom" && _pages > 0 && Items.Count() > _maxItems)
{ {
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button> <ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
</li>
} }
@if (_endPage > 1) <li class="page-item@((_page > 1) ? "" : " disabled")">
{ <a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button> </li>
@for (int i = _startPage; i <= _endPage; i++) @for (int i = _startPage; i <= _endPage; i++)
{ {
var pager = i; var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))> if (pager == _page)
@pager
</button>
}
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
{ {
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button> <li class="page-item active">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
} }
@if (_endPage > 1) else
{ {
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button> <li class="page-item">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
} }
@if (_endPage > 1) }
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{ {
<span class="btn btn-link disabled">Page @_page of @_pages</span> <li class="page-item@((_endPage < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
</li>
}
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">
<a class="page-link">Page @_page of @_pages</a>
</li>
</ul>
} }
</div>
} }
</p>
@code { @code {
private int _pages = 0; private int _pages = 0;
private int _page = 1; private int _page = 1;
private int _maxItems = 10; private int _maxItems = 10;
private int _maxPages = 5; private int _displayPages = 5;
private int _startPage = 0; private int _startPage = 0;
private int _endPage = 0; private int _endPage = 0;
private int _columns = 1;
[Parameter] [Parameter]
public string Format { get; set; } public string Format { get; set; } // Table or Grid
[Parameter] [Parameter]
public string Toolbar { get; set; } public string Toolbar { get; set; } // Top or Bottom
[Parameter] [Parameter]
public RenderFragment Header { get; set; } public RenderFragment Header { get; set; } = null;
[Parameter] [Parameter]
public RenderFragment<TableItem> Row { get; set; } public RenderFragment<TableItem> Row { get; set; } = null;
[Parameter] [Parameter]
public RenderFragment<TableItem> Detail { get; set; } public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
[Parameter] [Parameter]
public IEnumerable<TableItem> Items { get; set; } public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
[Parameter] [Parameter]
public string PageSize { get; set; } public string PageSize { get; set; } // number of items to display on a page
[Parameter] [Parameter]
public string DisplayPages { get; set; } public string Columns { get; set; } // only applicable to Grid layouts
[Parameter]
public string CurrentPage { get; set; } // optional property to set the initial page to display
[Parameter]
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
[Parameter] [Parameter]
public string Class { get; set; } public string Class { get; set; }
@ -177,86 +224,89 @@
_maxItems = int.Parse(PageSize); _maxItems = int.Parse(PageSize);
} }
if (!string.IsNullOrEmpty(DisplayPages)) if (!string.IsNullOrEmpty(Columns))
{ {
_maxPages = int.Parse(DisplayPages); _columns = int.Parse(Columns);
} }
if (!string.IsNullOrEmpty(DisplayPages))
{
_displayPages = int.Parse(DisplayPages);
}
if (!string.IsNullOrEmpty(CurrentPage))
{
_page = int.Parse(CurrentPage);
}
else
{
_page = 1; _page = 1;
}
_startPage = 0; _startPage = 0;
_endPage = 0; _endPage = 0;
if (Items != null) if (Items != null)
{ {
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
_pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems); _pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems);
if (_page > _pages)
{
_page = _pages;
}
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
SetPagerSize();
}
} }
SetPagerSize("forward"); public void SetPagerSize()
}
public void UpdateList(int currentPage)
{ {
ItemList = Items.Skip((currentPage - 1) * _maxItems).Take(_maxItems); _startPage = ((_page - 1) / _displayPages) * _displayPages + 1;
_page = currentPage; _endPage = _startPage + _displayPages - 1;
if (_endPage > _pages)
StateHasChanged();
}
public void SetPagerSize(string direction)
{
if (direction == "forward")
{
if (_endPage + 1 < _pages)
{
_startPage = _endPage + 1;
}
else
{
_startPage = 1;
}
if (_endPage + _maxPages < _pages)
{
_endPage = _startPage + _maxPages - 1;
}
else
{ {
_endPage = _pages; _endPage = _pages;
} }
StateHasChanged(); StateHasChanged();
} }
else if (direction == "back")
public void UpdateList(int page)
{ {
_endPage = _startPage - 1; ItemList = Items.Skip((page - 1) * _maxItems).Take(_maxItems);
_startPage = _startPage - _maxPages; _page = page;
SetPagerSize();
} }
public void SkipPages(string direction)
{
switch (direction)
{
case "forward":
_page = _endPage + 1;
break;
case "back":
_page = _startPage - 1;
break;
}
SetPagerSize();
} }
public void NavigateToPage(string direction) public void NavigateToPage(string direction)
{ {
if (direction == "next") switch (direction)
{ {
case "next":
if (_page < _pages) if (_page < _pages)
{ {
if (_page == _endPage)
{
SetPagerSize("forward");
}
_page += 1; _page += 1;
} }
} break;
else if (direction == "previous") case "previous":
{
if (_page > 1) if (_page > 1)
{ {
if (_page == _startPage)
{
SetPagerSize("back");
}
_page -= 1; _page -= 1;
} }
break;
} }
UpdateList(_page); UpdateList(_page);

View File

@ -7,8 +7,10 @@
@if (_permissions != null) @if (_permissions != null)
{ {
<br /> <div class="container">
<table class="table table-borderless" style="width: 50%; min-width: 250px;"> <div class="row">
<div class="col">
<table class="table table-borderless">
<tbody> <tbody>
<tr> <tr>
<th scope="col">@Localizer["Role"]</th> <th scope="col">@Localizer["Role"]</th>
@ -32,9 +34,18 @@
} }
</tbody> </tbody>
</table> </table>
<br />
</div>
</div>
<div class="row">
<div class="col">
@if (_users.Count != 0) @if (_users.Count != 0)
{ {
<table class="table table-borderless" style="width: 50%; min-width: 250px;"> <div class="row">
<div class="col">
</div>
</div>
<table class="table table-borderless">
<thead> <thead>
<tr> <tr>
<th scope="col">@Localizer["User"]</th> <th scope="col">@Localizer["User"]</th>
@ -61,8 +72,13 @@
} }
</tbody> </tbody>
</table> </table>
<br />
} }
<table class="table table-borderless" style="width: 50%; min-width: 250px;"> </div>
</div>
<div class="row">
<div class="col">
<table class="table table-borderless">
<tbody> <tbody>
<tr> <tr>
<td class="input-group"> <td class="input-group">
@ -73,7 +89,14 @@
</tbody> </tbody>
</table> </table>
<br /> <br />
</div>
</div>
<div class="row">
<div class="col">
<ModuleMessage Type="MessageType.Error" Message="@_message" /> <ModuleMessage Type="MessageType.Error" Message="@_message" />
</div>
</div>
</div>
} }
@code { @code {

View File

@ -2,7 +2,7 @@
@namespace Oqtane.Modules.HtmlText @namespace Oqtane.Modules.HtmlText
@inherits ModuleBase @inherits ModuleBase
@inject IHtmlTextService HtmlTextService @inject IHtmlTextService HtmlTextService
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Index> Localizer
@((MarkupString)content) @((MarkupString)content)

View File

@ -5,19 +5,17 @@
@inject IStringLocalizer<Settings> Localizer @inject IStringLocalizer<Settings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<table class="table table-borderless"> <div class="container">
<tr> <div class="row mb-1 align-items-center">
<td width="30%"> <Label Class="col-sm-3" For="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
<Label For="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label> <div class="col-sm-9">
</td>
<td>
<select id="files" class="form-select" @bind="@_allowfilemanagement"> <select id="files" class="form-select" @bind="@_allowfilemanagement">
<option value="true">@SharedLocalizer["Yes"]</option> <option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option> <option value="false">@SharedLocalizer["No"]</option>
</select> </select>
</td> </div>
</tr> </div>
</table> </div>
@code { @code {
private string _allowfilemanagement; private string _allowfilemanagement;

View File

@ -134,6 +134,11 @@ namespace Oqtane.Modules
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
} }
public string ImageUrl(int fileid, int width, int height, string mode)
{
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode);
}
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "") public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
{ {
var urlParameters = new Dictionary<string, string>(); var urlParameters = new Dictionary<string, string>();
@ -205,6 +210,38 @@ namespace Oqtane.Modules
// logging methods // logging methods
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args) public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
{
LogFunction logFunction;
if (string.IsNullOrEmpty(function))
{
// try to infer from page action
function = PageState.Action;
}
if (!Enum.TryParse(function, out logFunction))
{
switch (function.ToLower())
{
case "add":
logFunction = LogFunction.Create;
break;
case "edit":
logFunction = LogFunction.Update;
break;
case "delete":
logFunction = LogFunction.Delete;
break;
case "":
logFunction = LogFunction.Read;
break;
default:
logFunction = LogFunction.Other;
break;
}
}
await Log(alias, level, logFunction, exception, message, args);
}
public async Task Log(Alias alias, LogLevel level, LogFunction function, Exception exception, string message, params object[] args)
{ {
int pageId = ModuleState.PageId; int pageId = ModuleState.PageId;
int moduleId = ModuleState.ModuleId; int moduleId = ModuleState.ModuleId;
@ -215,34 +252,8 @@ namespace Oqtane.Modules
} }
string category = GetType().AssemblyQualifiedName; string category = GetType().AssemblyQualifiedName;
string feature = Utilities.GetTypeNameLastSegment(category, 1); string feature = Utilities.GetTypeNameLastSegment(category, 1);
LogFunction logFunction;
if (string.IsNullOrEmpty(function))
{
function = PageState.Action;
}
switch (function.ToLower())
{
case "add":
logFunction = LogFunction.Create;
break;
case "edit": await LoggingService.Log(alias, pageId, moduleId, userId, category, feature, function, level, exception, message, args);
logFunction = LogFunction.Update;
break;
case "delete":
logFunction = LogFunction.Delete;
break;
default:
logFunction = LogFunction.Read;
break;
}
if (feature == "Login")
{
logFunction = LogFunction.Security;
}
await LoggingService.Log(alias, pageId, moduleId, userId, category, feature, logFunction, level, exception, message, args);
} }
public class Logger public class Logger
@ -259,6 +270,11 @@ namespace Oqtane.Modules
await _moduleBase.Log(null, LogLevel.Trace, "", null, message, args); await _moduleBase.Log(null, LogLevel.Trace, "", null, message, args);
} }
public async Task LogTrace(LogFunction function, string message, params object[] args)
{
await _moduleBase.Log(null, LogLevel.Trace, function, null, message, args);
}
public async Task LogTrace(Exception exception, string message, params object[] args) public async Task LogTrace(Exception exception, string message, params object[] args)
{ {
await _moduleBase.Log(null, LogLevel.Trace, "", exception, message, args); await _moduleBase.Log(null, LogLevel.Trace, "", exception, message, args);
@ -269,6 +285,11 @@ namespace Oqtane.Modules
await _moduleBase.Log(null, LogLevel.Debug, "", null, message, args); await _moduleBase.Log(null, LogLevel.Debug, "", null, message, args);
} }
public async Task LogDebug(LogFunction function, string message, params object[] args)
{
await _moduleBase.Log(null, LogLevel.Debug, function, null, message, args);
}
public async Task LogDebug(Exception exception, string message, params object[] args) public async Task LogDebug(Exception exception, string message, params object[] args)
{ {
await _moduleBase.Log(null, LogLevel.Debug, "", exception, message, args); await _moduleBase.Log(null, LogLevel.Debug, "", exception, message, args);
@ -279,6 +300,11 @@ namespace Oqtane.Modules
await _moduleBase.Log(null, LogLevel.Information, "", null, message, args); await _moduleBase.Log(null, LogLevel.Information, "", null, message, args);
} }
public async Task LogInformation(LogFunction function, string message, params object[] args)
{
await _moduleBase.Log(null, LogLevel.Information, function, null, message, args);
}
public async Task LogInformation(Exception exception, string message, params object[] args) public async Task LogInformation(Exception exception, string message, params object[] args)
{ {
await _moduleBase.Log(null, LogLevel.Information, "", exception, message, args); await _moduleBase.Log(null, LogLevel.Information, "", exception, message, args);
@ -289,6 +315,11 @@ namespace Oqtane.Modules
await _moduleBase.Log(null, LogLevel.Warning, "", null, message, args); await _moduleBase.Log(null, LogLevel.Warning, "", null, message, args);
} }
public async Task LogWarning(LogFunction function, string message, params object[] args)
{
await _moduleBase.Log(null, LogLevel.Warning, function, null, message, args);
}
public async Task LogWarning(Exception exception, string message, params object[] args) public async Task LogWarning(Exception exception, string message, params object[] args)
{ {
await _moduleBase.Log(null, LogLevel.Warning, "", exception, message, args); await _moduleBase.Log(null, LogLevel.Warning, "", exception, message, args);
@ -299,6 +330,11 @@ namespace Oqtane.Modules
await _moduleBase.Log(null, LogLevel.Error, "", null, message, args); await _moduleBase.Log(null, LogLevel.Error, "", null, message, args);
} }
public async Task LogError(LogFunction function, string message, params object[] args)
{
await _moduleBase.Log(null, LogLevel.Error, function, null, message, args);
}
public async Task LogError(Exception exception, string message, params object[] args) public async Task LogError(Exception exception, string message, params object[] args)
{ {
await _moduleBase.Log(null, LogLevel.Error, "", exception, message, args); await _moduleBase.Log(null, LogLevel.Error, "", exception, message, args);
@ -309,6 +345,11 @@ namespace Oqtane.Modules
await _moduleBase.Log(null, LogLevel.Critical, "", null, message, args); await _moduleBase.Log(null, LogLevel.Critical, "", null, message, args);
} }
public async Task LogCritical(LogFunction function, string message, params object[] args)
{
await _moduleBase.Log(null, LogLevel.Critical, function, null, message, args);
}
public async Task LogCritical(Exception exception, string message, params object[] args) public async Task LogCritical(Exception exception, string message, params object[] args)
{ {
await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args); await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args);

View File

@ -5,7 +5,7 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RazorLangVersion>3.0</RazorLangVersion> <RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>2.2.0</Version> <Version>2.3.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.2.0</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.3.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
@ -38,4 +38,15 @@
<Folder Include="Resources\" /> <Folder Include="Resources\" />
<Folder Include="Resources\Themes\Controls\Theme\" /> <Folder Include="Resources\Themes\Controls\Theme\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<TrimmerRootAssembly Include="System.Runtime" />
<TrimmerRootAssembly Include="System.Linq.Parallel" />
<TrimmerRootAssembly Include="System.Runtime.CompilerServices.VisualC" />
</ItemGroup>
<PropertyGroup>
<BlazorEnableCompression>false</BlazorEnableCompression>
</PropertyGroup>
</Project> </Project>

View File

@ -153,4 +153,10 @@
<data name="Pwd.HelpText" xml:space="preserve"> <data name="Pwd.HelpText" xml:space="preserve">
<value>Enter the password to use for the database</value> <value>Enter the password to use for the database</value>
</data> </data>
<data name="Custom" xml:space="preserve">
<value>Custom</value>
</data>
<data name="Integrated" xml:space="preserve">
<value>Integrated</value>
</data>
</root> </root>

View File

@ -147,4 +147,10 @@
<data name="Pwd.HelpText" xml:space="preserve"> <data name="Pwd.HelpText" xml:space="preserve">
<value>Enter the password to use for the database</value> <value>Enter the password to use for the database</value>
</data> </data>
<data name="Custom" xml:space="preserve">
<value>Custom</value>
</data>
<data name="Integrated" xml:space="preserve">
<value>Integrated</value>
</data>
</root> </root>

View File

@ -120,8 +120,8 @@
<data name="DatabaseConfig" xml:space="preserve"> <data name="DatabaseConfig" xml:space="preserve">
<value>Database Configuration</value> <value>Database Configuration</value>
</data> </data>
<data name="DatabaseType" xml:space="preserve"> <data name="DatabaseType.Text" xml:space="preserve">
<value>Database Type:</value> <value>Database:</value>
</data> </data>
<data name="ApplicationAdmin" xml:space="preserve"> <data name="ApplicationAdmin" xml:space="preserve">
<value>Application Administrator</value> <value>Application Administrator</value>
@ -138,4 +138,31 @@
<data name="Register" xml:space="preserve"> <data name="Register" xml:space="preserve">
<value>Please Register Me For Major Product Updates And Security Bulletins</value> <value>Please Register Me For Major Product Updates And Security Bulletins</value>
</data> </data>
<data name="Confirm.HelpText" xml:space="preserve">
<value>Please confirm the password entered above by entering it again</value>
</data>
<data name="Confirm.Text" xml:space="preserve">
<value>Confirm:</value>
</data>
<data name="DatabaseType.HelpText" xml:space="preserve">
<value>Select the type of database you wish to use</value>
</data>
<data name="Email.HelpText" xml:space="preserve">
<value>Provide the email address for the host user account</value>
</data>
<data name="Email.Text" xml:space="preserve">
<value>Email:</value>
</data>
<data name="Password.HelpText" xml:space="preserve">
<value>Provide a password for the primary user account</value>
</data>
<data name="Password.Text" xml:space="preserve">
<value>Password:</value>
</data>
<data name="Username.HelpText" xml:space="preserve">
<value>Provide a username for the primary user account</value>
</data>
<data name="Username.Text" xml:space="preserve">
<value>Username:</value>
</data>
</root> </root>

View File

@ -156,4 +156,10 @@
<data name="UploadFiles.Heading" xml:space="preserve"> <data name="UploadFiles.Heading" xml:space="preserve">
<value>Upload Files</value> <value>Upload Files</value>
</data> </data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name of the file being downloaded</value>
</data>
<data name="Name.Text" xml:space="preserve">
<value>Name:</value>
</data>
</root> </root>

View File

@ -144,4 +144,10 @@
<data name="Size.Text" xml:space="preserve"> <data name="Size.Text" xml:space="preserve">
<value>Size: </value> <value>Size: </value>
</data> </data>
<data name="Description.HelpText" xml:space="preserve">
<value>A description of the file. This can be used as a caption for image files.</value>
</data>
<data name="Description.Text" xml:space="preserve">
<value>Description:</value>
</data>
</root> </root>

View File

@ -165,4 +165,22 @@
<data name="DeleteFolder.Message" xml:space="preserve"> <data name="DeleteFolder.Message" xml:space="preserve">
<value>Are You Sure You Wish To Delete This Folder?</value> <value>Are You Sure You Wish To Delete This Folder?</value>
</data> </data>
<data name="Type.HelpText" xml:space="preserve">
<value>Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users</value>
</data>
<data name="Type.Text" xml:space="preserve">
<value>Type:</value>
</data>
<data name="Capacity.HelpText" xml:space="preserve">
<value>Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited.</value>
</data>
<data name="Capacity.Text" xml:space="preserve">
<value>Capacity:</value>
</data>
<data name="ImageSizes.HelpText" xml:space="preserve">
<value>Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)</value>
</data>
<data name="ImageSizes.Text" xml:space="preserve">
<value>Image Sizes:</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -154,16 +154,16 @@
<value>Select how often you want the job to run</value> <value>Select how often you want the job to run</value>
</data> </data>
<data name="Starting.HelpText" xml:space="preserve"> <data name="Starting.HelpText" xml:space="preserve">
<value>What time do you want the job to start</value> <value>Optionally enter the date and time when this job should start executing</value>
</data> </data>
<data name="Ending.HelpText" xml:space="preserve"> <data name="Ending.HelpText" xml:space="preserve">
<value>When do you want the job to end</value> <value>Optionally enter the date and time when this job should stop executing</value>
</data> </data>
<data name="RetentionLog.HelpText" xml:space="preserve"> <data name="RetentionLog.HelpText" xml:space="preserve">
<value>Number of log entries to retain for this job</value> <value>Number of log entries to retain for this job</value>
</data> </data>
<data name="NextExecution.HelpText" xml:space="preserve"> <data name="NextExecution.HelpText" xml:space="preserve">
<value>Next execution for this job.</value> <value>Optionally modify the date and time when this job should execute next</value>
</data> </data>
<data name="Type.Text" xml:space="preserve"> <data name="Type.Text" xml:space="preserve">
<value>Type: </value> <value>Type: </value>

View File

@ -121,9 +121,15 @@
<value>Export</value> <value>Export</value>
</data> </data>
<data name="Content.HelpText" xml:space="preserve"> <data name="Content.HelpText" xml:space="preserve">
<value>Enter the module content</value> <value>The Exported Module Content</value>
</data> </data>
<data name="Content.Text" xml:space="preserve"> <data name="Content.Text" xml:space="preserve">
<value>Content: </value> <value>Content: </value>
</data> </data>
<data name="Error.Module.Export" xml:space="preserve">
<value>Error Exporting Module Content</value>
</data>
<data name="Success.Content.Export" xml:space="preserve">
<value>Content Exported Successfully</value>
</data>
</root> </root>

View File

@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Content.HelpText" xml:space="preserve"> <data name="Content.HelpText" xml:space="preserve">
<value>Enter the module content</value> <value>Enter The Module Content To Import</value>
</data> </data>
<data name="Content.Text" xml:space="preserve"> <data name="Content.Text" xml:space="preserve">
<value>Content: </value> <value>Content: </value>
@ -133,7 +133,7 @@
<value>A Problem Was Encountered Importing Content. Please Ensure The Content Is Formatted Correctly For The Module.</value> <value>A Problem Was Encountered Importing Content. Please Ensure The Content Is Formatted Correctly For The Module.</value>
</data> </data>
<data name="Error.Module.Import" xml:space="preserve"> <data name="Error.Module.Import" xml:space="preserve">
<value>Error Importing Module</value> <value>Error Importing Module Content</value>
</data> </data>
<data name="Message.Required.ImportContent" xml:space="preserve"> <data name="Message.Required.ImportContent" xml:space="preserve">
<value>You Must Enter Some Content To Import</value> <value>You Must Enter Some Content To Import</value>

View File

@ -228,4 +228,7 @@
<data name="Appearance.Name" xml:space="preserve"> <data name="Appearance.Name" xml:space="preserve">
<value>Appearance</value> <value>Appearance</value>
</data> </data>
<data name="Message.Page.Deleted" xml:space="preserve">
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
</data>
</root> </root>

View File

@ -148,7 +148,7 @@
<value>Error Adding User</value> <value>Error Adding User</value>
</data> </data>
<data name="Confirm.HelpText" xml:space="preserve"> <data name="Confirm.HelpText" xml:space="preserve">
<value>If you are changing your password you must enter it again to confirm it matches</value> <value>Enter your password again to confirm it matches the value entered above</value>
</data> </data>
<data name="Confirm.Text" xml:space="preserve"> <data name="Confirm.Text" xml:space="preserve">
<value>Confirm Password:</value> <value>Confirm Password:</value>
@ -166,7 +166,7 @@
<value>Email:</value> <value>Email:</value>
</data> </data>
<data name="Password.HelpText" xml:space="preserve"> <data name="Password.HelpText" xml:space="preserve">
<value>If you wish to change your password you can enter it here. Please choose a sufficiently secure password.</value> <value>Please choose a sufficiently secure password and enter it here</value>
</data> </data>
<data name="Password.Text" xml:space="preserve"> <data name="Password.Text" xml:space="preserve">
<value>Password:</value> <value>Password:</value>

View File

@ -217,10 +217,10 @@
<value>Enter the password for the integrated security</value> <value>Enter the password for the integrated security</value>
</data> </data>
<data name="HostUsername.HelpText" xml:space="preserve"> <data name="HostUsername.HelpText" xml:space="preserve">
<value>Enter the username of the host for this site</value> <value>Enter a valid host username</value>
</data> </data>
<data name="HostPassword.HelpText" xml:space="preserve"> <data name="HostPassword.HelpText" xml:space="preserve">
<value>Enter the password for the host of this site</value> <value>Enter a valid host password</value>
</data> </data>
<data name="Name.Text" xml:space="preserve"> <data name="Name.Text" xml:space="preserve">
<value>Site Name: </value> <value>Site Name: </value>

View File

@ -279,4 +279,37 @@
<data name="Search.Source" xml:space="preserve"> <data name="Search.Source" xml:space="preserve">
<value>source</value> <value>source</value>
</data> </data>
<data name="Message.InfoRequired" xml:space="preserve">
<value>Please Provide All Required Information</value>
</data>
<data name="Free" xml:space="preserve">
<value>Free</value>
</data>
<data name="Paid" xml:space="preserve">
<value>Paid</value>
</data>
<data name="Search.Price" xml:space="preserve">
<value>price</value>
</data>
<data name="Accept" xml:space="preserve">
<value>Accept</value>
</data>
<data name="License Not Specified" xml:space="preserve">
<value>License Not Specified</value>
</data>
<data name="Review License Terms" xml:space="preserve">
<value>Review License Terms</value>
</data>
<data name="Trial" xml:space="preserve">
<value>Day Trial</value>
</data>
<data name="Expires" xml:space="preserve">
<value>Expires</value>
</data>
<data name="Extend" xml:space="preserve">
<value>Extend</value>
</data>
<data name="Not Specified" xml:space="preserve">
<value>Not Specified</value>
</data>
</root> </root>

View File

@ -59,7 +59,7 @@
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true"> <xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType> <xsd:complexType>
@ -117,10 +117,28 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Title.HelpText" xml:space="preserve"> <data name="Footer.HelpText" xml:space="preserve">
<value>Specify If The Page Footer Should Be Displayed</value> <value>Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page.</value>
</data> </data>
<data name="Title.Text" xml:space="preserve"> <data name="Footer.Text" xml:space="preserve">
<value>Display Footer?</value> <value>Display Footer?</value>
</data> </data>
<data name="Login.HelpText" xml:space="preserve">
<value>Specify if a Login option should be displayed, Note that this option does not prevent the login page from being accessible via a direct url.</value>
</data>
<data name="Login.Text" xml:space="preserve">
<value>Show Login?</value>
</data>
<data name="Page" xml:space="preserve">
<value>Page</value>
</data>
<data name="Register.HelpText" xml:space="preserve">
<value>Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.</value>
</data>
<data name="Register.Text" xml:space="preserve">
<value>Show Register?</value>
</data>
<data name="Site" xml:space="preserve">
<value>Site</value>
</data>
</root> </root>

View File

@ -67,9 +67,9 @@ namespace Oqtane.Services
await DeleteAsync($"{Apiurl}/{fileId}"); await DeleteAsync($"{Apiurl}/{fileId}");
} }
public async Task<File> UploadFileAsync(string url, int folderId) public async Task<File> UploadFileAsync(string url, int folderId, string name)
{ {
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}"); return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}");
} }
public async Task<string> UploadFilesAsync(int folderId, string[] files, string id) public async Task<string> UploadFilesAsync(int folderId, string[] files, string id)

View File

@ -4,8 +4,15 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to retrieve <see cref="Database"/> information.
/// </summary>
public interface IDatabaseService public interface IDatabaseService
{ {
/// <summary>
/// Returns a list of databases
/// </summary>
/// <returns></returns>
Task<List<Database>> GetDatabasesAsync(); Task<List<Database>> GetDatabasesAsync();
} }
} }

View File

@ -62,8 +62,9 @@ namespace Oqtane.Services
/// </summary> /// </summary>
/// <param name="url"></param> /// <param name="url"></param>
/// <param name="folderId"></param> /// <param name="folderId"></param>
/// <param name="name"></param>
/// <returns></returns> /// <returns></returns>
Task<File> UploadFileAsync(string url, int folderId); Task<File> UploadFileAsync(string url, int folderId, string name);
/// <summary> /// <summary>
/// Upload one or more files. /// Upload one or more files.

View File

@ -4,12 +4,42 @@ using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage (install master database / upgrade version / etc.) the installation
/// </summary>
public interface IInstallationService public interface IInstallationService
{ {
/// <summary>
/// Returns a status/message object with the current installation state
/// </summary>
/// <returns></returns>
Task<Installation> IsInstalled(); Task<Installation> IsInstalled();
/// <summary>
/// Starts the installation process
/// </summary>
/// <param name="config">connectionString, database type, alias etc.</param>
/// <returns>internal status/message object</returns>
Task<Installation> Install(InstallConfig config); Task<Installation> Install(InstallConfig config);
/// <summary>
/// Starts the upgrade process
/// </summary>
/// <returns>internal status/message object</returns>
Task<Installation> Upgrade(); Task<Installation> Upgrade();
/// <summary>
/// Restarts the installation
/// </summary>
/// <returns>internal status/message object</returns>
Task RestartAsync(); Task RestartAsync();
/// <summary>
/// Registers a new <see cref="User"/>
/// </summary>
/// <param name="email">Email of the user to be registered</param>
/// <returns></returns>
Task RegisterAsync(string email); Task RegisterAsync(string email);
} }
} }

View File

@ -4,10 +4,22 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to read the job schedule log
/// </summary>
public interface IJobLogService public interface IJobLogService
{ {
/// <summary>
/// Return a list of all <see cref="JobLog"/> entries
/// </summary>
/// <returns></returns>
Task<List<JobLog>> GetJobLogsAsync(); Task<List<JobLog>> GetJobLogsAsync();
/// <summary>
/// Return a <see cref="JobLog"/> entry for the given Id
/// </summary>
/// <param name="jobLogId"></param>
/// <returns></returns>
Task<JobLog> GetJobLogAsync(int jobLogId); Task<JobLog> GetJobLogAsync(int jobLogId);
} }
} }

View File

@ -1,23 +1,61 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage jobs (<see cref="Job"/>)
/// </summary>
public interface IJobService public interface IJobService
{ {
/// <summary>
/// Returns a list of all jobs
/// </summary>
/// <returns></returns>
Task<List<Job>> GetJobsAsync(); Task<List<Job>> GetJobsAsync();
/// <summary>
/// Return a specific job
/// </summary>
/// <param name="jobId"></param>
/// <returns></returns>
Task<Job> GetJobAsync(int jobId); Task<Job> GetJobAsync(int jobId);
/// <summary>
/// Adds a new job
/// </summary>
/// <param name="job"></param>
/// <returns></returns>
Task<Job> AddJobAsync(Job job); Task<Job> AddJobAsync(Job job);
/// <summary>
/// Updates an existing job
/// </summary>
/// <param name="job"></param>
/// <returns></returns>
Task<Job> UpdateJobAsync(Job job); Task<Job> UpdateJobAsync(Job job);
/// <summary>
/// Delete an existing job
/// </summary>
/// <param name="jobId"></param>
/// <returns></returns>
Task DeleteJobAsync(int jobId); Task DeleteJobAsync(int jobId);
/// <summary>
/// Starts the given job
/// </summary>
/// <param name="jobId"></param>
/// <returns></returns>
Task StartJobAsync(int jobId); Task StartJobAsync(int jobId);
/// <summary>
/// Stops the given job
/// </summary>
/// <param name="jobId"></param>
/// <returns></returns>
Task StopJobAsync(int jobId); Task StopJobAsync(int jobId);
} }
} }

View File

@ -4,14 +4,38 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage <see cref="Language"/> entries
/// </summary>
public interface ILanguageService public interface ILanguageService
{ {
/// <summary>
/// Returns a list of all available languages for the given <see cref="Site"/>
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<List<Language>> GetLanguagesAsync(int siteId); Task<List<Language>> GetLanguagesAsync(int siteId);
/// <summary>
/// Returns the given language
/// </summary>
/// <param name="languageId"></param>
/// <returns></returns>
Task<Language> GetLanguageAsync(int languageId); Task<Language> GetLanguageAsync(int languageId);
/// <summary>
/// Adds the given language
/// </summary>
/// <param name="language"></param>
/// <returns></returns>
Task<Language> AddLanguageAsync(Language language); Task<Language> AddLanguageAsync(Language language);
/// <summary>
/// Deletes the given language
/// </summary>
/// <param name="languageId"></param>
/// <returns></returns>
Task DeleteLanguageAsync(int languageId); Task DeleteLanguageAsync(int languageId);
} }
} }

View File

@ -4,8 +4,15 @@ using Oqtane.Models;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to retrieve localizations (<see cref="Culture"/>)
/// </summary>
public interface ILocalizationService public interface ILocalizationService
{ {
/// <summary>
/// Returns a collection of supported cultures
/// </summary>
/// <returns></returns>
Task<IEnumerable<Culture>> GetCulturesAsync(); Task<IEnumerable<Culture>> GetCulturesAsync();
} }
} }

View File

@ -1,4 +1,4 @@
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -7,11 +7,59 @@ using Oqtane.Enums;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to retrieve and store <see cref="Log"/> entries
/// </summary>
public interface ILogService public interface ILogService
{ {
/// <summary>
/// Returns a list of log entires for the given params
/// </summary>
/// <param name="siteId"></param>
/// <param name="level"></param>
/// <param name="function"></param>
/// <param name="rows"></param>
/// <returns></returns>
Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows); Task<List<Log>> GetLogsAsync(int siteId, string level, string function, int rows);
/// <summary>
/// Returns a specific log entry for the given id
/// </summary>
/// <param name="logId"></param>
/// <returns></returns>
Task<Log> GetLogAsync(int logId); Task<Log> GetLogAsync(int logId);
/// <summary>
/// Creates a new log entry
/// </summary>
/// <param name="pageId"></param>
/// <param name="moduleId"></param>
/// <param name="userId"></param>
/// <param name="category"></param>
/// <param name="feature"></param>
/// <param name="function"></param>
/// <param name="level"></param>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
/// <returns></returns>
Task Log(int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args); Task Log(int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args);
/// <summary>
/// Creates a new log entry
/// </summary>
/// <param name="alias"></param>
/// <param name="pageId"></param>
/// <param name="moduleId"></param>
/// <param name="userId"></param>
/// <param name="category"></param>
/// <param name="feature"></param>
/// <param name="function"></param>
/// <param name="level"></param>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
/// <returns></returns>
Task Log(Alias alias, int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args); Task Log(Alias alias, int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args);
} }
} }

View File

@ -5,14 +5,60 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage a <see cref="ModuleDefinition"/>
/// </summary>
public interface IModuleDefinitionService public interface IModuleDefinitionService
{ {
/// <summary>
/// Returns a list of module definitions for the given site
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId); Task<List<ModuleDefinition>> GetModuleDefinitionsAsync(int siteId);
/// <summary>
/// Returns a specific module definition
/// </summary>
/// <param name="moduleDefinitionId"></param>
/// <param name="siteId"></param>
/// <returns></returns>
Task<ModuleDefinition> GetModuleDefinitionAsync(int moduleDefinitionId, int siteId); Task<ModuleDefinition> GetModuleDefinitionAsync(int moduleDefinitionId, int siteId);
/// <summary>
/// Updates a existing module definition
/// </summary>
/// <param name="moduleDefinition"></param>
/// <returns></returns>
Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition); Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
/// <summary>
/// Installs all module definitions located in //TODO: 2dm where?
/// </summary>
/// <returns></returns>
Task InstallModuleDefinitionsAsync(); Task InstallModuleDefinitionsAsync();
/// <summary>
/// Deletes a module definition
/// </summary>
/// <param name="moduleDefinitionId"></param>
/// <param name="siteId"></param>
/// <returns></returns>
Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId); Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId);
/// <summary>
/// Creates a new module definition
/// </summary>
/// <param name="moduleDefinition"></param>
/// <returns></returns>
Task<ModuleDefinition> CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition); Task<ModuleDefinition> CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
/// <summary>
/// Returns a list of module definition templates
/// </summary>
/// <returns></returns>
Task<List<Template>> GetModuleDefinitionTemplatesAsync(); Task<List<Template>> GetModuleDefinitionTemplatesAsync();
} }
} }

View File

@ -1,17 +1,62 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to retreive and store modules (<see cref="Module"/>)
/// </summary>
public interface IModuleService public interface IModuleService
{ {
/// <summary>
/// Returns a list of modules for the given site
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<List<Module>> GetModulesAsync(int siteId); Task<List<Module>> GetModulesAsync(int siteId);
/// <summary>
/// Returns a specific module
/// </summary>
/// <param name="moduleId"></param>
/// <returns></returns>
Task<Module> GetModuleAsync(int moduleId); Task<Module> GetModuleAsync(int moduleId);
/// <summary>
/// Adds a new module
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
Task<Module> AddModuleAsync(Module module); Task<Module> AddModuleAsync(Module module);
/// <summary>
/// Updates an existing module
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
Task<Module> UpdateModuleAsync(Module module); Task<Module> UpdateModuleAsync(Module module);
/// <summary>
/// Deletes a module
/// </summary>
/// <param name="moduleId"></param>
/// <returns></returns>
Task DeleteModuleAsync(int moduleId); Task DeleteModuleAsync(int moduleId);
/// <summary>
/// Imports a module
/// </summary>
/// <param name="moduleId"></param>
/// <param name="content">module in JSON format</param>
/// <returns></returns>
Task<bool> ImportModuleAsync(int moduleId, string content); Task<bool> ImportModuleAsync(int moduleId, string content);
/// <summary>
/// Exports a given module
/// </summary>
/// <param name="moduleId"></param>
/// <returns>module in JSON</returns>
Task<string> ExportModuleAsync(int moduleId); Task<string> ExportModuleAsync(int moduleId);
} }
} }

View File

@ -1,19 +1,49 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to store and retreive notifications (<see cref="Notification"/>)
/// </summary>
public interface INotificationService public interface INotificationService
{ {
/// <summary>
/// Return a list of notifications
/// </summary>
/// <param name="siteId"></param>
/// <param name="direction"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId); Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId);
/// <summary>
/// Returns a specific notifications
/// </summary>
/// <param name="notificationId"></param>
/// <returns></returns>
Task<Notification> GetNotificationAsync(int notificationId); Task<Notification> GetNotificationAsync(int notificationId);
/// <summary>
/// Creates a new notification
/// </summary>
/// <param name="notification"></param>
/// <returns></returns>
Task<Notification> AddNotificationAsync(Notification notification); Task<Notification> AddNotificationAsync(Notification notification);
/// <summary>
/// Updates a existing notification
/// </summary>
/// <param name="notification"></param>
/// <returns></returns>
Task<Notification> UpdateNotificationAsync(Notification notification); Task<Notification> UpdateNotificationAsync(Notification notification);
/// <summary>
/// Deletes a notification
/// </summary>
/// <param name="notificationId"></param>
/// <returns></returns>
Task DeleteNotificationAsync(int notificationId); Task DeleteNotificationAsync(int notificationId);
} }
} }

View File

@ -4,11 +4,50 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage packages (<see cref="Package"/>)
/// </summary>
public interface IPackageService public interface IPackageService
{ {
/// <summary>
/// Returns a list of packages matching the given parameters
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
Task<List<Package>> GetPackagesAsync(string type); Task<List<Package>> GetPackagesAsync(string type);
Task<List<Package>> GetPackagesAsync(string type, string search);
/// <summary>
/// Returns a list of packages matching the given parameters
/// </summary>
/// <param name="type"></param>
/// <param name="search"></param>
/// <param name="price"></param>
/// <param name="package"></param>
/// <returns></returns>
Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package);
/// <summary>
/// Returns a specific package
/// </summary>
/// <param name="packageId"></param>
/// <param name="version"></param>
/// <returns></returns>
Task<Package> GetPackageAsync(string packageId, string version);
/// <summary>
/// Downloads a specific package as .nupkg file
/// </summary>
/// <param name="packageId"></param>
/// <param name="version"></param>
/// <param name="folder"></param>
/// <returns></returns>
Task DownloadPackageAsync(string packageId, string version, string folder); Task DownloadPackageAsync(string packageId, string version, string folder);
/// <summary>
/// Installs all packages located in //TODO: 2dm where?
/// </summary>
/// <returns></returns>
Task InstallPackagesAsync(); Task InstallPackagesAsync();
} }
} }

View File

@ -1,15 +1,56 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to store and retreive a <see cref="PageModule"/>
/// </summary>
public interface IPageModuleService public interface IPageModuleService
{ {
/// <summary>
/// Returns a specific page module
/// </summary>
/// <param name="pageModuleId"></param>
/// <returns></returns>
Task<PageModule> GetPageModuleAsync(int pageModuleId); Task<PageModule> GetPageModuleAsync(int pageModuleId);
/// <summary>
/// Return a specific page module
/// </summary>
/// <param name="pageId"></param>
/// <param name="moduleId"></param>
/// <returns></returns>
Task<PageModule> GetPageModuleAsync(int pageId, int moduleId); Task<PageModule> GetPageModuleAsync(int pageId, int moduleId);
/// <summary>
/// Creates a new page module
/// </summary>
/// <param name="pageModule"></param>
/// <returns></returns>
Task<PageModule> AddPageModuleAsync(PageModule pageModule); Task<PageModule> AddPageModuleAsync(PageModule pageModule);
/// <summary>
/// Updates a existing page module
/// </summary>
/// <param name="pageModule"></param>
/// <returns></returns>
Task<PageModule> UpdatePageModuleAsync(PageModule pageModule); Task<PageModule> UpdatePageModuleAsync(PageModule pageModule);
/// <summary>
/// Updates order of all page modules in the given pane
/// </summary>
/// <param name="pageId"></param>
/// <param name="pane"></param>
/// <returns></returns>
Task UpdatePageModuleOrderAsync(int pageId, string pane); Task UpdatePageModuleOrderAsync(int pageId, string pane);
/// <summary>
/// Deletes a page module
/// </summary>
/// <param name="pageModuleId"></param>
/// <returns></returns>
Task DeletePageModuleAsync(int pageModuleId); Task DeletePageModuleAsync(int pageModuleId);
} }
} }

View File

@ -1,19 +1,79 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Services to store and retrieve a <see cref="Page"/>
/// </summary>
public interface IPageService public interface IPageService
{ {
/// <summary>
/// Retuns a list of pages
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<List<Page>> GetPagesAsync(int siteId); Task<List<Page>> GetPagesAsync(int siteId);
/// <summary>
/// Returns a specific page
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Page> GetPageAsync(int pageId); Task<Page> GetPageAsync(int pageId);
/// <summary>
/// Returns a specific page personalized for the given user
/// </summary>
/// <param name="pageId"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<Page> GetPageAsync(int pageId, int userId); Task<Page> GetPageAsync(int pageId, int userId);
/// <summary>
/// Returns a specific page by its defined path
/// </summary>
/// <param name="path"></param>
/// <param name="siteId"></param>
/// <returns></returns>
Task<Page> GetPageAsync(string path, int siteId); Task<Page> GetPageAsync(string path, int siteId);
/// <summary>
/// Adds a new page
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
Task<Page> AddPageAsync(Page page); Task<Page> AddPageAsync(Page page);
/// <summary>
/// Adds a new page
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
Task<Page> AddPageAsync(int pageId, int userId); Task<Page> AddPageAsync(int pageId, int userId);
/// <summary>
/// Updates a existing page
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
Task<Page> UpdatePageAsync(Page page); Task<Page> UpdatePageAsync(Page page);
/// <summary>
/// Updates order of all page modules in the given parent
/// </summary>
/// <param name="siteId"></param>
/// <param name="pageId"></param>
/// <param name="parentId"></param>
/// <returns></returns>
Task UpdatePageOrderAsync(int siteId, int pageId, int? parentId); Task UpdatePageOrderAsync(int siteId, int pageId, int? parentId);
/// <summary>
/// Deletes a page
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task DeletePageAsync(int pageId); Task DeletePageAsync(int pageId);
} }
} }

View File

@ -1,19 +1,48 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to store and retreive <see cref="Profile"/> entries
/// </summary>
public interface IProfileService public interface IProfileService
{ {
/// <summary>
/// Returns a list of profile entries
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<List<Profile>> GetProfilesAsync(int siteId); Task<List<Profile>> GetProfilesAsync(int siteId);
/// <summary>
/// Returns a specific profile entry
/// </summary>
/// <param name="profileId"></param>
/// <returns></returns>
Task<Profile> GetProfileAsync(int profileId); Task<Profile> GetProfileAsync(int profileId);
/// <summary>
/// Creates a new profile entry
/// </summary>
/// <param name="profile"></param>
/// <returns></returns>
Task<Profile> AddProfileAsync(Profile profile); Task<Profile> AddProfileAsync(Profile profile);
/// <summary>
/// Updates an existing profile entry
/// </summary>
/// <param name="profile"></param>
/// <returns></returns>
Task<Profile> UpdateProfileAsync(Profile profile); Task<Profile> UpdateProfileAsync(Profile profile);
/// <summary>
/// Deletes a profile entry
/// </summary>
/// <param name="profileId"></param>
/// <returns></returns>
Task DeleteProfileAsync(int profileId); Task DeleteProfileAsync(int profileId);
} }
} }

View File

@ -1,55 +1,184 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage <see cref="Setting"/>s
/// </summary>
public interface ISettingService public interface ISettingService
{ {
/// <summary>
/// Returns a key-value dictionary of all tenant settings
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetTenantSettingsAsync(); Task<Dictionary<string, string>> GetTenantSettingsAsync();
/// <summary>
/// Updates a tenant setting
/// </summary>
/// <param name="tenantSettings"></param>
/// <returns></returns>
Task UpdateTenantSettingsAsync(Dictionary<string, string> tenantSettings); Task UpdateTenantSettingsAsync(Dictionary<string, string> tenantSettings);
/// <summary>
/// Returns a key-value dictionary of all site settings for the given site
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetSiteSettingsAsync(int siteId); Task<Dictionary<string, string>> GetSiteSettingsAsync(int siteId);
/// <summary>
/// Updates a site setting
/// </summary>
/// <param name="siteSettings"></param>
/// <param name="siteId"></param>
/// <returns></returns>
Task UpdateSiteSettingsAsync(Dictionary<string, string> siteSettings, int siteId); Task UpdateSiteSettingsAsync(Dictionary<string, string> siteSettings, int siteId);
/// <summary>
/// Returns a key-value dictionary of all page settings for the given page
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetPageSettingsAsync(int pageId); Task<Dictionary<string, string>> GetPageSettingsAsync(int pageId);
/// <summary>
/// Updates a page setting
/// </summary>
/// <param name="pageSettings"></param>
/// <param name="pageId"></param>
/// <returns></returns>
Task UpdatePageSettingsAsync(Dictionary<string, string> pageSettings, int pageId); Task UpdatePageSettingsAsync(Dictionary<string, string> pageSettings, int pageId);
/// <summary>
/// Returns a key-value dictionary of all page module settings for the given page module
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetPageModuleSettingsAsync(int pageModuleId); Task<Dictionary<string, string>> GetPageModuleSettingsAsync(int pageModuleId);
/// <summary>
/// Updates a page module setting
/// </summary>
/// <param name="pageModuleSettings"></param>
/// <param name="pageModuleId"></param>
/// <returns></returns>
Task UpdatePageModuleSettingsAsync(Dictionary<string, string> pageModuleSettings, int pageModuleId); Task UpdatePageModuleSettingsAsync(Dictionary<string, string> pageModuleSettings, int pageModuleId);
/// <summary>
/// Returns a key-value dictionary of all module settings for the given module
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetModuleSettingsAsync(int moduleId); Task<Dictionary<string, string>> GetModuleSettingsAsync(int moduleId);
/// <summary>
/// Updates a module setting
/// </summary>
/// <param name="moduleSettings"></param>
/// <param name="moduleId"></param>
/// <returns></returns>
Task UpdateModuleSettingsAsync(Dictionary<string, string> moduleSettings, int moduleId); Task UpdateModuleSettingsAsync(Dictionary<string, string> moduleSettings, int moduleId);
/// <summary>
/// Returns a key-value dictionary of all user settings for the given user
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetUserSettingsAsync(int userId); Task<Dictionary<string, string>> GetUserSettingsAsync(int userId);
/// <summary>
/// Updates a user setting
/// </summary>
/// <param name="userSettings"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task UpdateUserSettingsAsync(Dictionary<string, string> userSettings, int userId); Task UpdateUserSettingsAsync(Dictionary<string, string> userSettings, int userId);
/// <summary>
/// Returns a key-value dictionary of all folder settings for the given folder
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetFolderSettingsAsync(int folderId); Task<Dictionary<string, string>> GetFolderSettingsAsync(int folderId);
/// <summary>
/// Updates a folder setting
/// </summary>
/// <param name="folderSettings"></param>
/// <param name="folderId"></param>
/// <returns></returns>
Task UpdateFolderSettingsAsync(Dictionary<string, string> folderSettings, int folderId); Task UpdateFolderSettingsAsync(Dictionary<string, string> folderSettings, int folderId);
/// <summary>
/// Returns a key-value dictionary of all settings for the given entityName
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId); Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId);
/// <summary>
/// Updates settings for a given entityName and Id
/// </summary>
/// <param name="settings"></param>
/// <param name="entityName"></param>
/// <param name="entityId"></param>
/// <returns></returns>
Task UpdateSettingsAsync(Dictionary<string, string> settings, string entityName, int entityId); Task UpdateSettingsAsync(Dictionary<string, string> settings, string entityName, int entityId);
/// <summary>
/// Returns a specific setting
/// </summary>
/// <param name="settingId"></param>
/// <returns></returns>
Task<Setting> GetSettingAsync(int settingId); Task<Setting> GetSettingAsync(int settingId);
/// <summary>
/// Creates a new setting
/// </summary>
/// <param name="setting"></param>
/// <returns></returns>
Task<Setting> AddSettingAsync(Setting setting); Task<Setting> AddSettingAsync(Setting setting);
/// <summary>
/// Updates a existing setting
/// </summary>
/// <param name="setting"></param>
/// <returns></returns>
Task<Setting> UpdateSettingAsync(Setting setting); Task<Setting> UpdateSettingAsync(Setting setting);
/// <summary>
/// Deletes a setting
/// </summary>
/// <param name="settingId"></param>
/// <returns></returns>
Task DeleteSettingAsync(int settingId); Task DeleteSettingAsync(int settingId);
/// <summary>
/// Gets the value of the given settingName (key) from the given key-value dictionary
/// </summary>
/// <param name="settings"></param>
/// <param name="settingName"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
string GetSetting(Dictionary<string, string> settings, string settingName, string defaultValue); string GetSetting(Dictionary<string, string> settings, string settingName, string defaultValue);
/// <summary>
/// Sets the value of the given settingName (key) in the given key-value dictionary
/// </summary>
/// <param name="settings"></param>
/// <param name="settingName"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue); Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue);
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic);
Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2);
} }
} }

View File

@ -1,3 +1,4 @@
using Oqtane.Documentation;
using Oqtane.Models; using Oqtane.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -5,18 +6,47 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to store and retreive <see cref="Site"/> entries
/// </summary>
public interface ISiteService public interface ISiteService
{ {
/// <summary>
/// Returns a list of sites
/// </summary>
/// <returns></returns>
Task<List<Site>> GetSitesAsync(); Task<List<Site>> GetSitesAsync();
/// <summary>
/// Returns a specific site
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task<Site> GetSiteAsync(int siteId); Task<Site> GetSiteAsync(int siteId);
/// <summary>
/// Creates a new site
/// </summary>
/// <param name="site"></param>
/// <returns></returns>
Task<Site> AddSiteAsync(Site site); Task<Site> AddSiteAsync(Site site);
/// <summary>
/// Updates an existing site
/// </summary>
/// <param name="site"></param>
/// <returns></returns>
Task<Site> UpdateSiteAsync(Site site); Task<Site> UpdateSiteAsync(Site site);
/// <summary>
/// Deletes a site
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task DeleteSiteAsync(int siteId); Task DeleteSiteAsync(int siteId);
[PrivateApi]
[Obsolete("This method is deprecated.", false)] [Obsolete("This method is deprecated.", false)]
void SetAlias(Alias alias); void SetAlias(Alias alias);
} }

View File

@ -1,11 +1,18 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to retreive <see cref="SiteTemplate"/> entries
/// </summary>
public interface ISiteTemplateService public interface ISiteTemplateService
{ {
/// <summary>
/// Returns a list of site templates
/// </summary>
/// <returns></returns>
Task<List<SiteTemplate>> GetSiteTemplatesAsync(); Task<List<SiteTemplate>> GetSiteTemplatesAsync();
} }
} }

View File

@ -1,10 +1,18 @@
using Oqtane.Models; using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to execute a <see cref="SqlQuery"/> against the backend database
/// </summary>
public interface ISqlService public interface ISqlService
{ {
/// <summary>
/// Executes a sql query and returns its result
/// </summary>
/// <param name="sqlquery"></param>
/// <returns></returns>
Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery); Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery);
} }
} }

View File

@ -3,10 +3,22 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to retrieve and update system information.
/// </summary>
public interface ISystemService public interface ISystemService
{ {
/// <summary>
/// returns a key-value directory with the current system information (os-version, clr-version, etc.)
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetSystemInfoAsync(); Task<Dictionary<string, string>> GetSystemInfoAsync();
/// <summary>
/// Updates system information
/// </summary>
/// <param name="settings"></param>
/// <returns></returns>
Task UpdateSystemInfoAsync(Dictionary<string, string> settings); Task UpdateSystemInfoAsync(Dictionary<string, string> settings);
} }
} }

View File

@ -4,15 +4,65 @@ using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
{ {
/// <summary>
/// Service to manage <see cref="Theme"/> entries
/// </summary>
public interface IThemeService public interface IThemeService
{ {
/// <summary>
/// Returns a list of available themes
/// </summary>
/// <returns></returns>
Task<List<Theme>> GetThemesAsync(); Task<List<Theme>> GetThemesAsync();
/// <summary>
/// Returns a list of <see cref="ThemeControl"/>s from the given themes
/// </summary>
/// <param name="themes"></param>
/// <returns></returns>
List<ThemeControl> GetThemeControls(List<Theme> themes); List<ThemeControl> GetThemeControls(List<Theme> themes);
/// <summary>
/// Returns a list of layouts (<see cref="ThemeControl"/>) from the given themes with a matching theme name
/// </summary>
/// <param name="themes"></param>
/// <param name="themeName"></param>
/// <returns></returns>
List<ThemeControl> GetLayoutControls(List<Theme> themes, string themeName); List<ThemeControl> GetLayoutControls(List<Theme> themes, string themeName);
/// <summary>
/// Returns a list of containers (<see cref="ThemeControl"/>) from the given themes with a matching theme name
/// </summary>
/// <param name="themes"></param>
/// <param name="themeName"></param>
/// <returns></returns>
List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName); List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName);
/// <summary>
/// Installs all themes located in //TODO: 2dm where?
/// </summary>
/// <returns></returns>
Task InstallThemesAsync(); Task InstallThemesAsync();
/// <summary>
/// Deletes a theme
/// </summary>
/// <param name="themeName"></param>
/// <returns></returns>
Task DeleteThemeAsync(string themeName); Task DeleteThemeAsync(string themeName);
/// <summary>
/// Creates a new theme
/// </summary>
/// <param name="theme"></param>
/// <returns></returns>
Task<Theme> CreateThemeAsync(Theme theme); Task<Theme> CreateThemeAsync(Theme theme);
/// <summary>
/// Returns a list of theme templates (<see cref="Template"/>)
/// </summary>
/// <returns></returns>
Task<List<Template>> GetThemeTemplatesAsync(); Task<List<Template>> GetThemeTemplatesAsync();
} }
} }

View File

@ -22,12 +22,17 @@ namespace Oqtane.Services
public async Task<List<Package>> GetPackagesAsync(string type) public async Task<List<Package>> GetPackagesAsync(string type)
{ {
return await GetPackagesAsync(type, ""); return await GetPackagesAsync(type, "", "", "");
} }
public async Task<List<Package>> GetPackagesAsync(string type, string search) public async Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package)
{ {
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}"); return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}");
}
public async Task<Package> GetPackageAsync(string packageId, string version)
{
return await PostJsonAsync<Package>($"{Apiurl}?packageid={packageId}&version={version}", null);
} }
public async Task DownloadPackageAsync(string packageId, string version, string folder) public async Task DownloadPackageAsync(string packageId, string version, string folder)

View File

@ -47,6 +47,7 @@ namespace Oqtane.Services
{ {
return await PutJsonAsync<Role>($"{Apiurl}/{role.RoleId}", role); return await PutJsonAsync<Role>($"{Apiurl}/{role.RoleId}", role);
} }
public async Task DeleteRoleAsync(int roleId) public async Task DeleteRoleAsync(int roleId)
{ {
await DeleteAsync($"{Apiurl}/{roleId}"); await DeleteAsync($"{Apiurl}/{roleId}");

View File

@ -109,6 +109,17 @@ namespace Oqtane.Services
foreach (KeyValuePair<string, string> kvp in settings) foreach (KeyValuePair<string, string> kvp in settings)
{ {
string value = kvp.Value;
bool ispublic = false;
if (value.StartsWith("[Public]"))
{
if (entityName == EntityNames.Site)
{
ispublic = true;
}
value = value.Substring(8); // remove [Public]
}
Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase)); Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase));
if (setting == null) if (setting == null)
{ {
@ -116,14 +127,16 @@ namespace Oqtane.Services
setting.EntityName = entityName; setting.EntityName = entityName;
setting.EntityId = entityId; setting.EntityId = entityId;
setting.SettingName = kvp.Key; setting.SettingName = kvp.Key;
setting.SettingValue = kvp.Value; setting.SettingValue = value;
setting.IsPublic = ispublic;
setting = await AddSettingAsync(setting); setting = await AddSettingAsync(setting);
} }
else else
{ {
if (setting.SettingValue != kvp.Value) if (setting.SettingValue != kvp.Value)
{ {
setting.SettingValue = kvp.Value; setting.SettingValue = value;
setting.IsPublic = ispublic;
setting = await UpdateSettingAsync(setting); setting = await UpdateSettingAsync(setting);
} }
} }
@ -163,11 +176,17 @@ namespace Oqtane.Services
} }
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue) public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue)
{
return SetSetting(settings, settingName, settingValue, false);
}
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic)
{ {
if (settings == null) if (settings == null)
{ {
settings = new Dictionary<string, string>(); settings = new Dictionary<string, string>();
} }
settingValue = (isPublic) ? "[Public]" + settingValue : settingValue;
if (settings.ContainsKey(settingName)) if (settings.ContainsKey(settingName))
{ {
settings[settingName] = settingValue; settings[settingName] = settingValue;
@ -178,5 +197,28 @@ namespace Oqtane.Services
} }
return settings; return settings;
} }
public Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2)
{
if (settings1 == null)
{
settings1 = new Dictionary<string, string>();
}
if (settings2 != null)
{
foreach (var setting in settings2)
{
if (settings1.ContainsKey(setting.Key))
{
settings1[setting.Key] = setting.Value;
}
else
{
settings1.Add(setting.Key, setting.Value);
}
}
}
return settings1;
}
} }
} }

View File

@ -96,7 +96,7 @@ namespace Oqtane.Themes.Controls
{ {
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
string url = NavigateUrl(); string url = NavigateUrl(true);
if (action.Action != null) if (action.Action != null)
{ {
@ -115,7 +115,7 @@ namespace Oqtane.Themes.Controls
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
return NavigateUrl(url, true); return url;
} }
private async Task<string> DeleteModule(string url, PageModule pagemodule) private async Task<string> DeleteModule(string url, PageModule pagemodule)
@ -123,7 +123,7 @@ namespace Oqtane.Themes.Controls
pagemodule.IsDeleted = true; pagemodule.IsDeleted = true;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(url, true); return url;
} }
private async Task<string> Settings(string url, PageModule pagemodule) private async Task<string> Settings(string url, PageModule pagemodule)
@ -133,7 +133,7 @@ namespace Oqtane.Themes.Controls
return url; return url;
} }
private async Task<string> Publish(string s, PageModule pagemodule) private async Task<string> Publish(string url, PageModule pagemodule)
{ {
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions); var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions) foreach (var permissionstring in permissions)
@ -148,10 +148,10 @@ namespace Oqtane.Themes.Controls
} }
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions); pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module); await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, true); return url;
} }
private async Task<string> Unpublish(string s, PageModule pagemodule) private async Task<string> Unpublish(string url, PageModule pagemodule)
{ {
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions); var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions) foreach (var permissionstring in permissions)
@ -166,39 +166,39 @@ namespace Oqtane.Themes.Controls
} }
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions); pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module); await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, true); return url;
} }
private async Task<string> MoveTop(string s, PageModule pagemodule) private async Task<string> MoveTop(string url, PageModule pagemodule)
{ {
pagemodule.Order = 0; pagemodule.Order = 0;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true); return url;
} }
private async Task<string> MoveBottom(string s, PageModule pagemodule) private async Task<string> MoveBottom(string url, PageModule pagemodule)
{ {
pagemodule.Order = int.MaxValue; pagemodule.Order = int.MaxValue;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true); return url;
} }
private async Task<string> MoveUp(string s, PageModule pagemodule) private async Task<string> MoveUp(string url, PageModule pagemodule)
{ {
pagemodule.Order -= 3; pagemodule.Order -= 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true); return url;
} }
private async Task<string> MoveDown(string s, PageModule pagemodule) private async Task<string> MoveDown(string url, PageModule pagemodule)
{ {
pagemodule.Order += 3; pagemodule.Order += 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true); return url;
} }
public class ActionViewModel public class ActionViewModel

View File

@ -14,21 +14,17 @@
@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) @if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{ {
<div class="app-controlpanel" style="@_display"> <div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel">
<div class="@CardClass"> <div class="@HeaderClass">
<div class="@HeaderClass d-flex"> <h5 id="offcanvasScrollingLabel">@Localizer["ControlPanel"]</h5>
<span class="font-weight-bold">@Localizer["ControlPanel"]</span> <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
<div class="ms-auto">
<button type="button" class="btn-close" aria-label="Close" @onclick="HideControlPanel"></button>
</div>
</div> </div>
<div class="@BodyClass"> <div class="@BodyClass">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{ {
<div class="row d-flex"> <div class="row d-flex">
<div class="col"> <div class="col">
<button type="button" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button> <button data-bs-dismiss="offcanvas" type="button" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
</div> </div>
</div> </div>
@ -41,8 +37,8 @@
</div> </div>
<div class="row d-flex"> <div class="row d-flex">
<div class="col d-flex justify-content-between"> <div class="col d-flex justify-content-between">
<button type="button" class="btn btn-secondary col-3" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button> <button type="button" class="btn btn-secondary col-3" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
<button type="button" class="btn btn-secondary col-3" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button> <button type="button" class="btn btn-secondary col-3" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
<button type="button" class="btn btn-danger col-3" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button> <button type="button" class="btn btn-danger col-3" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
</div> </div>
</div> </div>
@ -187,11 +183,10 @@
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
<hr class="app-rule" /> <hr class="app-rule" />
<NavLink class="btn btn-info col-12" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink> <NavLink class="btn btn-info col-12" data-bs-dismiss="offcanvas" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink>
} }
</div> </div>
</div> </div>
</div>
} }
@if (ShowLanguageSwitcher) @if (ShowLanguageSwitcher)
@ -217,7 +212,8 @@
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) @if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{ {
<button type="button" class="btn @ButtonClass" @onclick="ShowControlPanel">
<button class="btn @ButtonClass" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
<span class="oi oi-cog"></span> <span class="oi oi-cog"></span>
</button> </button>
} }
@ -231,7 +227,6 @@
private List<Page> _pages = new List<Page>(); private List<Page> _pages = new List<Page>();
private List<Module> _modules = new List<Module>(); private List<Module> _modules = new List<Module>();
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private string _display = "display: none;";
private string _category = "Common"; private string _category = "Common";
protected string PageId { get; private set; } = "-"; protected string PageId { get; private set; } = "-";
@ -280,13 +275,13 @@
public string ButtonClass { get; set; } = "btn-outline-secondary"; public string ButtonClass { get; set; } = "btn-outline-secondary";
[Parameter] [Parameter]
public string CardClass { get; set; } = "card border-secondary mb-3"; public string ContainerClass { get; set; } = "offcanvas offcanvas-end";
[Parameter] [Parameter]
public string HeaderClass { get; set; } = "card-header"; public string HeaderClass { get; set; } = "offcanvas-header";
[Parameter] [Parameter]
public string BodyClass { get; set; } = "card-body"; public string BodyClass { get; set; } = "offcanvas-body overflow-auto";
[Parameter] [Parameter]
public bool ShowLanguageSwitcher { get; set; } = true; public bool ShowLanguageSwitcher { get; set; } = true;
@ -405,17 +400,17 @@
await PageModuleService.AddPageModuleAsync(pageModule); await PageModuleService.AddPageModuleAsync(pageModule);
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
Message = $"<br /><div class=\"alert alert-success\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>"; Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
} }
else else
{ {
Message = $"<br /><div class=\"alert alert-warning\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>"; Message = $"<div class=\"alert alert-warning mt-2 text-center\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
} }
} }
else else
{ {
Message = $"<br /><div class=\"alert alert-error\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>"; Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
} }
} }
@ -445,23 +440,9 @@
} }
} }
private void ShowControlPanel()
{
Message = "";
_display = "width: 25%; min-width: 375px;";
StateHasChanged();
}
private void HideControlPanel()
{
Message = "";
_display = "width: 0%;";
StateHasChanged();
}
private void Navigate(string location) private void Navigate(string location)
{ {
HideControlPanel(); //HideControlPanel();
Module module; Module module;
switch (location) switch (location)
{ {

View File

@ -12,7 +12,16 @@
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button> <button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
</Authorized> </Authorized>
<NotAuthorized> <NotAuthorized>
@if (ShowLogin)
{
<button type="button" class="btn btn-primary" @onclick="LoginUser">@SharedLocalizer["Login"]</button> <button type="button" class="btn btn-primary" @onclick="LoginUser">@SharedLocalizer["Login"]</button>
}
</NotAuthorized> </NotAuthorized>
</AuthorizeView> </AuthorizeView>
</span> </span>
@code
{
[Parameter]
public bool ShowLogin { get; set; }
}

Some files were not shown because too many files have changed in this diff Show More