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))
{ {
<CascadingAuthenticationState> <div style="@_display">
<CascadingValue Value="@PageState"> <CascadingAuthenticationState>
<SiteRouter OnStateChange="@ChangeState" /> <CascadingValue Value="@PageState">
</CascadingValue> <SiteRouter Runtime="@Runtime" RenderMode="@RenderMode" OnStateChange="@ChangeState" />
</CascadingAuthenticationState> </CascadingValue>
</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 OnParametersSetAsync()
{
_installation = await InstallationService.IsInstalled();
if (_installation.Alias != null)
{
SiteState.Alias = _installation.Alias;
SiteState.AntiForgeryToken = AntiForgeryToken;
}
else
{
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
}
_initialized = true;
}
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender && !_initialized) if (firstRender)
{ {
var interop = new Interop(JSRuntime); if (string.IsNullOrEmpty(AntiForgeryToken))
SiteState.AntiForgeryToken = await interop.GetElementByName(Constants.RequestVerificationToken);
_installation = await InstallationService.IsInstalled();
if (_installation.Alias != null)
{ {
SiteState.Alias = _installation.Alias; // 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");
} }
else _display = "";
{
_installation.Message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
}
_initialized = true;
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" /> </select>
</td> </div>
</tr> </div>
} @if (_security == "custom")
else {
{ <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
<td> <div class="col-sm-9">
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label> <input id="uid" type="text" class="form-control" @bind="@_uid" />
</td> </div>
<td> </div>
<select id="@fieldId" class="custom-select" @bind="@field.Value"> <div class="row mb-1 align-items-center">
<option value="true" selected>@SharedLocalizer["True"]</option> <Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<option value="false">@SharedLocalizer["False"]</option> <div class="col-sm-9">
</select> <input id="pwd" type="password" class="form-control" @bind="@_pwd" />
</td> </div>
</tr> </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>
</select>
<tr style="@isVisible"> </div>
<td> </div>
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label> @if (_security == "custom")
</td> {
<td> <div class="row mb-1 align-items-center">
<input id="@fieldId" type="@fieldType" class="form-control" @bind="@field.Value" /> <Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
</td> <div class="col-sm-9">
</tr> <input id="uid" type="text" class="form-control" @bind="@_uid" />
} </div>
else </div>
{ <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<td> <div class="col-sm-9">
<Label For="@fieldId" HelpText="@field.HelpText" ResourceKey="@field.Name">@Localizer[$"{field.FriendlyName}:"]</Label> <input id="pwd" type="password" class="form-control" @bind="@_pwd" />
</td> </div>
<td> </div>
<select id="@fieldId" class="custom-select" @bind="@field.Value">
<option value="true" selected>@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select>
</td>
</tr>
}
}
} }
@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,78 +20,64 @@
<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> @if (_databases != null)
<td> {
<select id="databasetype" class="custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))"> foreach (var database in _databases)
@if (_databases != null)
{ {
foreach (var database in _databases) if (database.IsDefault)
{ {
if (database.IsDefault) <option value="@database.Name" selected>@Localizer[@database.Name]</option>
{ }
<option value="@database.Name" selected>@Localizer[@database.Name]</option> else
} {
else <option value="@database.Name">@Localizer[@database.Name]</option>
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
} }
} }
</select> }
</td> </select>
</tr> </div>
@{ </div>
if (_databaseConfigType != null) @{
{ if (_databaseConfigType != null)
@DatabaseConfigComponent; {
} @DatabaseConfigComponent;
} }
</tbody> }
</table> </div>
</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> <input id="password" type="password" class="form-control" @bind="@_hostPassword" />
<td> </div>
<Label For="password" HelpText="Provide the password for the host user account" ResourceKey="Password">Password:</Label> </div>
</td> <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>
<input id="password" type="password" class="form-control" @bind="@_hostPassword" /> <div class="col-sm-9">
</td> <input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" />
</tr> </div>
<tr> </div>
<td> <div class="row mb-1 align-items-center">
<Label For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label> <Label Class="col-sm-3" For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label>
</td> <div class="col-sm-9">
<td> <input type="text" class="form-control" @bind="@_hostEmail" />
<input id="confirm" type="password" class="form-control" @bind="@_confirmPassword" /> </div>
</td> </div>
</tr> </div>
<tr>
<td>
<Label For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label>
</td>
<td>
<input type="text" class="form-control" @bind="@_hostEmail" />
</td>
</tr>
</tbody>
</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> <option value="-1">&lt;@Localizer["Folder.Select"]&gt;</option>
<td> @foreach (Folder folder in _folders)
<select id="folder" class="form-select" @bind="@_folderId"> {
<option value="-1">&lt;@Localizer["Folder.Select"]&gt;</option> <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
@foreach (Folder folder in _folders) }
{ </select>
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> </div>
} </div>
</select> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="name" HelpText="Enter the name of the file being downloaded" ResourceKey="Name">Name: </Label>
</tr> <div class="col-sm-9">
</table> <input id="name" class="form-control" @bind="@_name" />
<button type="button" class="btn btn-success" @onclick="Download">@SharedLocalizer["Download"]</button> </div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> </div>
</div>
<button type="button" class="btn btn-success" @onclick="Download">@SharedLocalizer["Download"]</button>
<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,37 +78,48 @@
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))
{ {
AddModuleMessage(Localizer["Message.Required.UrlFolder"], MessageType.Warning); if (_url == string.Empty || _folderId == -1)
return; {
} AddModuleMessage(Localizer["Message.Required.UrlFolder"], MessageType.Warning);
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;
} }
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)
{
await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", _url, ex.Message);
AddModuleMessage(Localizer["Error.Download.InvalidUrl"], MessageType.Error);
}
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Download.InvalidUrl"], MessageType.Error);
} }
} }
} }

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> @foreach (Folder folder in _folders)
<td> {
<select id="parent" class="form-select" @bind="@_folderId"> <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
@foreach (Folder folder in _folders) }
{ </select>
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> </div>
} </div>
</select> <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>
</tr> <div class="col-sm-9">
<tr> <input id="description" class="form-control" @bind="@_description" />
<td> </div>
<Label for="size" HelpText="The size of the file (in bytes)" ResourceKey="Size">Size: </Label> </div>
</td> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="size" HelpText="The size of the file (in bytes)" ResourceKey="Size">Size: </Label>
<input id="size" class="form-control" @bind="@_size" readonly /> <div class="col-sm-9">
</td> <input id="size" class="form-control" @bind="@_size" readonly />
</tr> </div>
</table> </div>
<button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button> </div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button>
<br /> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br /> <br />
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo> <br />
<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;
@ -88,26 +94,36 @@
private async Task SaveFile() private async Task SaveFile()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_name.IsPathOrFileValid()) try
{ {
File file = await FileService.GetFileAsync(_fileId); if (_name.IsPathOrFileValid())
file.Name = _name; {
file.FolderId = _folderId; File file = await FileService.GetFileAsync(_fileId);
file = await FileService.UpdateFileAsync(file); file.Name = _name;
await logger.LogInformation("File Saved {File}", file); file.FolderId = _folderId;
NavigationManager.NavigateTo(NavigateUrl()); file.Description = _description;
file = await FileService.UpdateFileAsync(file);
await logger.LogInformation("File Saved {File}", file);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["Message.File.InvalidName"], MessageType.Warning);
}
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.File.InvalidName"], MessageType.Warning); await logger.LogError(ex, "Error Saving File {FileId} {Error}", _fileId, ex.Message);
AddModuleMessage(Localizer["Error.File.Save"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving File {FileId} {Error}", _fileId, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.File.Save"], MessageType.Error);
} }
} }
} }

View File

@ -8,57 +8,67 @@
@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"))
{
<option value="-1">&lt;@Localizer["NoParent"]&gt;</option>
}
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
@if (PageState.QueryString.ContainsKey("id")) @if (PageState.QueryString.ContainsKey("id"))
{ {
<option value="-1">&lt;@Localizer["NoParent"]&gt;</option> <input id="type" class="form-control" readonly @bind="@_type" />
} }
@foreach (Folder folder in _folders) else
{ {
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> <select id="type" class="form-select" @bind="@_type" required>
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
</select>
} }
</select> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <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>
<td> <div class="col-sm-9">
<Label for="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label> <input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</td> </div>
<td> </div>
<input id="name" class="form-control" @bind="@_name" /> <div class="row mb-1 align-items-center">
</td> <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>
</tr> <div class="col-sm-9">
<tr> <input id="capacity" class="form-control" @bind="@_capacity" required />
<td> </div>
<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> </div>
</td> <div class="row mb-1 align-items-center">
<td> <div class="col-sm-12">
@if (PageState.QueryString.ContainsKey("id")) <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" />
<input id="type" class="form-control" readonly @bind="@_type" />
} </div>
else </div>
{ </div>
<select id="type" class="form-select" @bind="@_type"> </form>
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
</select>
}
</td>
</tr>
<tr>
<td colspan="2" align="center">
<Label 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" />
</td>
</tr>
</table>
@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)
@ -137,70 +152,81 @@
private async Task SaveFolder() private async Task SaveFolder()
{ {
if (_name == string.Empty || _parentId == -1) validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
AddModuleMessage(Localizer["Message.Required.FolderParent"], MessageType.Warning); if (_name == string.Empty || _parentId == -1)
return;
}
if (!_name.IsPathOrFileValid())
{
AddModuleMessage(Localizer["Message.Folder.InvalidName"], MessageType.Warning);
return;
}
try
{
Folder folder;
if (_folderId != -1)
{ {
folder = await FolderService.GetFolderAsync(_folderId); AddModuleMessage(Localizer["Message.Required.FolderParent"], MessageType.Warning);
} return;
else
{
folder = new Folder();
} }
folder.SiteId = PageState.Site.SiteId; if (!_name.IsPathOrFileValid())
if (_parentId == -1)
{ {
folder.ParentId = null; AddModuleMessage(Localizer["Message.Folder.InvalidName"], MessageType.Warning);
} return;
else
{
folder.ParentId = _parentId;
} }
folder.Name = _name; try
folder.Type = _type; {
folder.IsSystem = _isSystem; Folder folder;
folder.Permissions = _permissionGrid.GetPermissions(); if (_folderId != -1)
{
folder = await FolderService.GetFolderAsync(_folderId);
}
else
{
folder = new Folder();
}
if (_folderId != -1) folder.SiteId = PageState.Site.SiteId;
{
folder = await FolderService.UpdateFolderAsync(folder);
}
else
{
folder = await FolderService.AddFolderAsync(folder);
}
if (folder != null) if (_parentId == -1)
{ {
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); folder.ParentId = null;
await logger.LogInformation("Folder Saved {Folder}", folder); }
NavigationManager.NavigateTo(NavigateUrl()); else
{
folder.ParentId = _parentId;
}
folder.Name = _name;
folder.Type = _type;
folder.ImageSizes = _imagesizes;
folder.Capacity = int.Parse(_capacity);
folder.IsSystem = _isSystem;
folder.Permissions = _permissionGrid.GetPermissions();
if (_folderId != -1)
{
folder = await FolderService.UpdateFolderAsync(folder);
}
else
{
folder = await FolderService.AddFolderAsync(folder);
}
if (folder != null)
{
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId);
await logger.LogInformation("Folder Saved {Folder}", folder);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["Error.Folder.Save"], MessageType.Error);
}
} }
else catch (Exception ex)
{ {
await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message);
AddModuleMessage(Localizer["Error.Folder.Save"], MessageType.Error); AddModuleMessage(Localizer["Error.Folder.Save"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Folder.Save"], MessageType.Error);
} }
} }
@ -242,4 +268,4 @@
AddModuleMessage(Localizer["Error.Folder.Delete"], MessageType.Error); AddModuleMessage(Localizer["Error.Folder.Delete"], MessageType.Error);
} }
} }
} }

View File

@ -8,34 +8,34 @@
@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>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
<th>@Localizer["Modified"]</th> <th>@Localizer["Modified"]</th>
<th>@Localizer["Type"]</th> <th>@Localizer["Type"]</th>
<th>@Localizer["Size"]</th> <th>@Localizer["Size"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td> <td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>

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> <input id="type" class="form-control" @bind="@_jobType" readonly />
</td> </div>
<td> </div>
<input id="type" class="form-control" @bind="@_jobType" readonly /> <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>
</tr> <div class="col-sm-9">
<tr> <select id="enabled" class="form-select" @bind="@_isEnabled" required>
<td> <option value="True">@SharedLocalizer["Yes"]</option>
<Label For="enabled" HelpText="Select whether you want the job enabled or not" ResourceKey="Enabled">Enabled? </Label> <option value="False">@SharedLocalizer["No"]</option>
</td> </select>
<td> </div>
<select id="enabled" class="form-select" @bind="@_isEnabled"> </div>
<option value="True">@SharedLocalizer["Yes"]</option> <div class="row mb-1 align-items-center">
<option value="False">@SharedLocalizer["No"]</option> <Label Class="col-sm-3" For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label>
</select> <div class="col-sm-9">
</td> <input id="runs-every" class="form-control" @bind="@_interval" maxlength="4" required />
</tr> <select id="runs-every" class="form-select" @bind="@_frequency" required>
<tr> <option value="m">@Localizer["Minute(s)"]</option>
<td> <option value="H">@Localizer["Hour(s)"]</option>
<Label For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label> <option value="d">@Localizer["Day(s)"]</option>
</td> <option value="M">@Localizer["Month(s)"]</option>
<td> </select>
<input id="runs-every" class="form-control" @bind="@_interval" /> </div>
<select id="runs-every" class="form-select" @bind="@_frequency"> </div>
<option value="m">@Localizer["Minute(s)"]</option> <div class="row mb-1 align-items-center">
<option value="H">@Localizer["Hour(s)"]</option> <Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
<option value="d">@Localizer["Day(s)"]</option> <div class="col-sm-9">
<option value="M">@Localizer["Month(s)"]</option> <input id="retention" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
</select> </div>
</td> </div>
</tr> <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>
<td> <div class="col-sm-9">
<Label For="starting" HelpText="What time do you want the job to start" ResourceKey="Starting">Starting: </Label> <div class="row">
</td> <div class="col">
<td> <input id="starting" type="date" class="form-control" @bind="@_startDate" />
<input id="starting" class="form-control" @bind="@_startDate" /> </div>
</td> <div class="col">
</tr> <input id="starting" type="text" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
<tr> </div>
<td> </div>
<Label For="ending" HelpText="When do you want the job to end" ResourceKey="Ending">Ending: </Label> </div>
</td> </div>
<td> <div class="row mb-1 align-items-center">
<input id="ending" class="form-control" @bind="@_endDate" /> <Label Class="col-sm-3" For="ending" HelpText="Optionally enter the date and time when this job should stop executing" ResourceKey="Ending">Ending: </Label>
</td> <div class="col-sm-9">
</tr> <div class="row">
<tr> <div class="col">
<td> <input id="ending" type="date" class="form-control" @bind="@_endDate" />
<Label For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label> </div>
</td> <div class="col">
<td> <input id="ending" type="text" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
<input id="retention" class="form-control" @bind="@_retentionHistory" /> </div>
</td> </div>
</tr> </div>
<tr> </div>
<td> <div class="row mb-1 align-items-center">
<Label For="next" HelpText="Next execution for this job." ResourceKey="NextExecution">Next Execution: </Label> <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>
</td> <div class="col-sm-9">
<td> <div class="row">
<input id="next" class="form-control" @bind="@_nextExecution" /> <div class="col">
</td> <input id="next" type="date" class="form-control" @bind="@_nextDate" />
</tr> </div>
</table> <div class="col">
<button type="button" class="btn btn-success" @onclick="SaveJob">@SharedLocalizer["Save"]</button> <input id="next" type="text" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> </div>
<br /> </div>
<br /> </div>
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo> </div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveJob">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
<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))
{
job.StartDate = DateTime.Parse(job.StartDate.Value.ToShortDateString() + " " + _startTime);
}
} }
else job.EndDate = _endDate;
if (job.EndDate != null)
{ {
job.StartDate = DateTime.Parse(_startDate); job.EndDate = job.EndDate.Value.Date;
if (!string.IsNullOrEmpty(_endTime))
{
job.EndDate = DateTime.Parse(job.EndDate.Value.ToShortDateString() + " " + _endTime);
}
} }
if (_endDate == string.Empty)
{
job.EndDate = null;
}
else
{
job.EndDate = DateTime.Parse(_endDate);
}
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> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
<td> <div class="col-sm-9">
<Label For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label> <select id="default" class="form-select" @bind="@_isDefault" required>
</td> <option value="True">@SharedLocalizer["Yes"]</option>
<td> <option value="False">@SharedLocalizer["No"]</option>
<select id="default" class="form-select" @bind="@_isDefault"> </select>
<option value="True">@SharedLocalizer["Yes"]</option> </div>
<option value="False">@SharedLocalizer["No"]</option> </div>
</select> </div>
</td> <button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
</tr> </form>
</table>
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
} }
<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()
@ -176,31 +244,88 @@ else
private async Task SaveLanguage() private async Task SaveLanguage()
{ {
var language = new Language validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
SiteId = PageState.Page.SiteId, var language = new Language
Name = CultureInfo.GetCultureInfo(_code).DisplayName, {
Code = _code, SiteId = PageState.Page.SiteId,
IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault)) Name = CultureInfo.GetCultureInfo(_code).DisplayName,
}; Code = _code,
IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault))
};
try
{
language = await LanguageService.AddLanguageAsync(language);
if (language.IsDefault)
{
await SetCultureAsync(language.Code);
}
await logger.LogInformation("Language Added {Language}", language);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message);
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 try
{ {
language = await LanguageService.AddLanguageAsync(language); var package = await PackageService.GetPackageAsync(packageid, version);
if (package != null)
if (language.IsDefault)
{ {
await SetCultureAsync(language.Code); _productname = package.Name;
if (!string.IsNullOrEmpty(package.License))
{
_license = package.License.Replace("\n", "<br />");
}
_packageid = package.PackageId;
_version = package.Version;
} }
StateHasChanged();
await logger.LogInformation("Language Added {Language}", language);
NavigationManager.NavigateTo(NavigateUrl());
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message); await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error); 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);
} }
} }
@ -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

@ -29,9 +29,9 @@ else
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td> <td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
<td> <td>
@if (UpgradeAvailable(context.Code)) @if (UpgradeAvailable(context.Code))
{ {
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button> <button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
} }
</td> </td>
</Row> </Row>
</Pager> </Pager>

View File

@ -59,7 +59,7 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
}; };
@ -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;
} }
@ -98,7 +100,10 @@
{ {
if (firstRender) if (firstRender)
{ {
await username.FocusAsync(); if(PageState.User == null)
{
await username.FocusAsync();
}
} }
} }
@ -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,206 +9,246 @@
@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>
</td>
<td>
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
</td>
</tr>
<tr>
<td>
<Label For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
</td>
<td>
<input id="level" class="form-control" @bind="@_level" readonly />
</td>
</tr>
<tr>
<td>
<Label For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
</td>
<td>
<input id="feature" class="form-control" @bind="@_feature" readonly />
</td>
</tr>
<tr>
<td>
<Label For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
</td>
<td>
<input id="function" class="form-control" @bind="@_function" readonly />
</td>
</tr>
<tr>
<td>
<Label For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
</td>
<td>
<input id="category" class="form-control" @bind="@_category" readonly />
</td>
</tr>
@if (_pageName != string.Empty)
{
<tr>
<td>
<Label For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
</td>
<td>
<input id="page" class="form-control" @bind="@_pageName" readonly />
</td>
</tr>
}
@if (_moduleTitle != string.Empty)
{
<tr>
<td>
<Label For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
</td>
</tr>
}
@if (_username != string.Empty)
{
<tr>
<td>
<Label For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
</td>
<td>
<input id="user" class="form-control" @bind="@_username" readonly />
</td>
</tr>
}
<tr>
<td>
<Label For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@_url" readonly />
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
</td>
<td>
<input id="template" class="form-control" @bind="@_template" readonly />
</td>
</tr>
<tr>
<td>
<Label For="message" HelpText="The message that the system generated" class="control-label" ResourceKey="Message">Message: </Label>
</td>
<td>
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
</td>
</tr>
@if (!string.IsNullOrEmpty(_exception))
{
<tr>
<td>
<Label For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
</td>
<td>
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
</td>
</tr>
}
<tr>
<td>
<Label For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
</td>
<td>
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
</td>
</tr>
<tr>
<td>
<Label For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
</td>
<td>
<input id="server" class="form-control" @bind="@_server" readonly />
</td>
</tr>
</table>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { <div class="col-sm-9">
private int _logId;
private string _logDate = string.Empty;
private string _level = string.Empty;
private string _feature = string.Empty;
private string _function = string.Empty;
private string _category = string.Empty;
private string _pageName = string.Empty;
private string _moduleTitle = string.Empty;
private string _username = string.Empty;
private string _url = string.Empty;
private string _template = string.Empty;
private string _message = string.Empty;
private string _exception = string.Empty;
private string _properties = string.Empty;
private string _server = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; </div>
</div>
<div class="row mb-1 align-items-center">
protected override async Task OnInitializedAsync() <div class="col-sm-9">
{
try </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 />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
<div class="col-sm-9">
<input id="level" class="form-control" @bind="@_level" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
<div class="col-sm-9">
<input id="feature" class="form-control" @bind="@_feature" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
<div class="col-sm-9">
<input id="function" class="form-control" @bind="@_function" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
<div class="col-sm-9">
<input id="category" class="form-control" @bind="@_category" readonly />
</div>
</div>
@if (_pageName != string.Empty)
{ {
_logId = Int32.Parse(PageState.QueryString["id"]); <div class="row mb-1 align-items-center">
var log = await LogService.GetLogAsync(_logId); <Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
if (log != null) <div class="col-sm-9">
<input id="page" class="form-control" @bind="@_pageName" readonly />
</div>
</div>
}
@if (_moduleTitle != string.Empty)
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
</div>
</div>
}
@if (_username != string.Empty)
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
<div class="col-sm-9">
<input id="user" class="form-control" @bind="@_username" readonly />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
<div class="col-sm-9">
<input id="template" class="form-control" @bind="@_template" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
<div class="col-sm-9">
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
</div>
</div>
@if (!string.IsNullOrEmpty(_exception))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
<div class="col-sm-9">
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
<div class="col-sm-9">
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
<div class="col-sm-9">
<input id="server" class="form-control" @bind="@_server" readonly />
</div>
</div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code {
private int _logId;
private string _logDate = string.Empty;
private string _level = string.Empty;
private string _feature = string.Empty;
private string _function = string.Empty;
private string _category = string.Empty;
private string _pageName = string.Empty;
private string _moduleTitle = string.Empty;
private string _username = string.Empty;
private string _url = string.Empty;
private string _template = string.Empty;
private string _message = string.Empty;
private string _exception = string.Empty;
private string _properties = string.Empty;
private string _server = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{ {
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture); _logId = Int32.Parse(PageState.QueryString["id"]);
_level = log.Level; var log = await LogService.GetLogAsync(_logId);
_feature = log.Feature; if (log != null)
_function = log.Function;
_category = log.Category;
if (log.PageId != null)
{ {
var page = await PageService.GetPageAsync(log.PageId.Value); _logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
if (page != null) _level = log.Level;
{ _feature = log.Feature;
_pageName = page.Name; _function = log.Function;
} _category = log.Category;
}
if (log.PageId != null && log.ModuleId != null) if (log.PageId != null)
{
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
if (pagemodule != null)
{ {
_moduleTitle = pagemodule.Title; var page = await PageService.GetPageAsync(log.PageId.Value);
if (page != null)
{
_pageName = page.Name;
}
} }
}
if (log.UserId != null) if (log.PageId != null && log.ModuleId != null)
{
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
if (user != null)
{ {
_username = user.Username; var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
if (pagemodule != null)
{
_moduleTitle = pagemodule.Title;
}
} }
}
_url = log.Url; if (log.UserId != null)
_template = log.MessageTemplate; {
_message = log.Message; var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
_exception = log.Exception; if (user != null)
_properties = log.Properties; {
_server = log.Server; _username = user.Username;
}
}
_url = log.Url;
_template = log.MessageTemplate;
_message = log.Message;
_exception = log.Exception;
_properties = log.Properties;
_server = log.Server;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
} }
} }
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
AddModuleMessage(Localizer["Error.Log.Load"], MessageType.Error);
}
} }
}

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,27 +35,27 @@ 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())
{ {
<Pager Items="@_logs"> <Pager Items="@_logs">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Date"]</th> <th>@Localizer["Date"]</th>
<th>@Localizer["Level"]</th> <th>@Localizer["Level"]</th>
<th>@Localizer["Feature"]</th> <th>@Localizer["Feature"]</th>
<th>@Localizer["Function"]</th> <th>@Localizer["Function"]</th>
</Header> </Header>
<Row> <Row>
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td> <td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td>

View File

@ -10,82 +10,75 @@
@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> <textarea id="description" class="form-control" @bind="@_description" rows="3" ></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> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
<tr> @foreach (Template template in _templates)
<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>
@foreach (Template template in _templates)
{
<option value="@template.Name">@template.Title</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions)
{
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{ {
<option value="@(version)">@(version)</option> <option value="@template.Name">@template.Title</option>
} }
} </select>
<option value="local">@SharedLocalizer["LocalVersion"]</option> </div>
</select> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
@if (!string.IsNullOrEmpty(_location)) <div class="col-sm-9">
{ <select id="reference" class="form-select" @bind="@_reference" required>
<tr> @foreach (string version in _versions)
<td> {
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label> if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
</td> {
<td> <option value="@(version)">@(version)</option>
<input id="module" class="form-control" @bind="@_location" readonly /> }
</td> }
</tr> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</select>
</div>
</div>
@if (!string.IsNullOrEmpty(_location))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_location" readonly />
</div>
</div>
}
</div>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button>
} }
</table> else
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button> {
} <button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button>
else </form>
{
<button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button>
} }
@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,14 +134,14 @@ 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 catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning); await logger.LogError(ex, "Error Creating Module");
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Creating Module"); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }

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,79 +10,71 @@
@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> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
<tr> @foreach (Template template in _templates)
<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>
@foreach (Template template in _templates)
{
<option value="@template.Name">@template.Title</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions)
{
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{ {
<option value="@(version)">@(version)</option> <option value="@template.Name">@template.Title</option>
} }
} </select>
<option value="local">@SharedLocalizer["LocalVersion"]</option> </div>
</select> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
@if (!string.IsNullOrEmpty(_location)) <div class="col-sm-9">
{ <select id="reference" class="form-select" @bind="@_reference" required>
<tr> @foreach (string version in _versions)
<td> {
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label> if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
</td> {
<td> <option value="@(version)">@(version)</option>
<input id="module" class="form-control" @bind="@_location" readonly /> }
</td> }
</tr> <option value="local">@SharedLocalizer["LocalVersion"]</option>
} </select>
</table> </div>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["CreateModule"]</button> </div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> @if (!string.IsNullOrEmpty(_location))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_location" readonly />
</div>
</div>
}
</div>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["CreateModule"]</button>
<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;
@ -111,23 +103,32 @@
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 }; if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition); {
GetLocation(); var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success); moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
}
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning); await logger.LogError(ex, "Error Creating Module");
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Creating Module"); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }

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" /> </div>
</td> </div>
</tr>
</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;
@ -168,30 +150,39 @@
private async Task SaveModuleDefinition() private async Task SaveModuleDefinition()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId); try
if (moduledefinition.Name != _name)
{ {
moduledefinition.Name = _name; var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
if (moduledefinition.Name != _name)
{
moduledefinition.Name = _name;
}
if (moduledefinition.Description != _description)
{
moduledefinition.Description = _description;
}
if (moduledefinition.Categories != _categories)
{
moduledefinition.Categories = _categories;
}
moduledefinition.Permissions = _permissionGrid.GetPermissions();
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
NavigationManager.NavigateTo(NavigateUrl());
} }
if (moduledefinition.Description != _description) catch (Exception ex)
{ {
moduledefinition.Description = _description; await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
} }
if (moduledefinition.Categories != _categories)
{
moduledefinition.Categories = _categories;
}
moduledefinition.Permissions = _permissionGrid.GetPermissions();
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
NavigationManager.NavigateTo(NavigateUrl());
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
} }
} }
} }

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,11 +35,14 @@ 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))
{ {
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button> <button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
} }
</td> </td>
</Row> </Row>
</Pager> </Pager>
@ -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()
{ {
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId); try
{
_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,53 +5,63 @@
@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>
</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</div>
</div>
</div>
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
<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()
{ {
if (_content != string.Empty) validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
try if (_content != string.Empty)
{ {
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content); try
if (success)
{ {
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success); bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
if (success)
{
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Content.ImportProblem"], MessageType.Warning);
}
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Content.ImportProblem"], MessageType.Warning); await logger.LogError(ex, "Error Importing Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message);
AddModuleMessage(Localizer["Error.Module.Import"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Importing Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message); AddModuleMessage(Localizer["Message.Required.ImportContent"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Module.Import"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Required.ImportContent"], MessageType.Warning); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }
} }

View File

@ -8,97 +8,93 @@
@inject IStringLocalizer<Settings> Localizer @inject IStringLocalizer<Settings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings"> <TabStrip>
@if (_containers != null) <TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
{ @if (_containers != null)
<table class="table table-borderless"> {
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label> <Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
</td> <div class="col-sm-9">
<td> <input id="title" type="text" name="Title" class="form-control" @bind="@_title" required />
<input id="title" type="text" name="Title" class="form-control" @bind="@_title" /> </div>
</td> </div>
</tr> <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>
<td> <div class="col-sm-9">
<Label For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label> <select id="container" class="form-select" @bind="@_containerType" required>
</td> @foreach (var container in _containers)
<td>
<select id="container" class="form-select" @bind="@_containerType">
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
</td>
<td>
<select id="allpages" class="form-select" @bind="@_allPages">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
</td>
<td>
<select id="page" class="form-select" @bind="@_pageId">
@foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option> <option value="@container.TypeName">@container.Name</option>
} }
} </select>
</select> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
</table> <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>
} <div class="col-sm-9">
</TabPanel> <select id="allpages" class="form-select" @bind="@_allPages" required>
<TabPanel Name="Permissions" ResourceKey="Permissions"> <option value="True">@SharedLocalizer["Yes"]</option>
@if (_permissions != null) <option value="False">@SharedLocalizer["No"]</option>
{ </select>
<table class="table table-borderless"> </div>
<tr> </div>
<td> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
<div class="col-sm-9">
<select id="page" class="form-select" @bind="@_pageId" required>
@foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
}
}
</select>
</div>
</div>
</div>
}
</TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<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>
@if (_moduleSettingsType != null)
{
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
@ModuleSettingsComponent
</TabPanel>
} }
</TabPanel> @if (_containerSettingsType != null)
@if (_moduleSettingsType != null) {
{ <TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings"> @ContainerSettingsComponent
@ModuleSettingsComponent </TabPanel>
</TabPanel> }
} </TabStrip>
@if (_containerSettingsType != null) <br />
{ <button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings"> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@ContainerSettingsComponent <br />
</TabPanel> <br />
} <AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
</TabStrip> </form>
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
@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;
@ -179,52 +175,61 @@
private async Task SaveModule() private async Task SaveModule()
{ {
if (!string.IsNullOrEmpty(_title)) validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); if (!string.IsNullOrEmpty(_title))
pagemodule.PageId = int.Parse(_pageId);
pagemodule.Title = _title;
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
{ {
pagemodule.ContainerType = string.Empty; var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
} pagemodule.PageId = int.Parse(_pageId);
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType) pagemodule.Title = _title;
{ pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
pagemodule.ContainerType = string.Empty; if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
}
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.Permissions = _permissionGrid.GetPermissions();
await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null)
{
if (_moduleSettings is ISettingsControl moduleSettingsControl)
{ {
// module settings updated using explicit interface pagemodule.ContainerType = string.Empty;
await moduleSettingsControl.UpdateSettings();
} }
else if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
{ {
// legacy support - module settings updated by convention ( ie. by calling a public method named "UpdateSettings" in settings component ) pagemodule.ContainerType = string.Empty;
_moduleSettings?.GetType().GetMethod("UpdateSettings")?.Invoke(_moduleSettings, null);
} }
} await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
if (_containerSettingsType != null && _containerSettings is ISettingsControl containerSettingsControl) var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.Permissions = _permissionGrid.GetPermissions();
await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null)
{
if (_moduleSettings is ISettingsControl moduleSettingsControl)
{
// module settings updated using explicit interface
await moduleSettingsControl.UpdateSettings();
}
else
{
// legacy support - module settings updated by convention ( ie. by calling a public method named "UpdateSettings" in settings component )
_moduleSettings?.GetType().GetMethod("UpdateSettings")?.Invoke(_moduleSettings, null);
}
}
if (_containerSettingsType != null && _containerSettings is ISettingsControl containerSettingsControl)
{
await containerSettingsControl.UpdateSettings();
}
NavigationManager.NavigateTo(NavigateUrl());
}
else
{ {
await containerSettingsControl.UpdateSettings(); AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
} }
NavigationManager.NavigateTo(NavigateUrl());
} }
else else
{ {
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }

View File

@ -6,176 +6,154 @@
@inject IStringLocalizer<Add> Localizer @inject IStringLocalizer<Add> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip Refresh="@_refresh">
<TabPanel Name="Settings" ResourceKey="Settings"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
@if (_themeList != null) <TabStrip Refresh="@_refresh">
{ <TabPanel Name="Settings" ResourceKey="Settings">
<table class="table table-borderless"> @if (_themeList != null)
<tr> {
<td width="30%"> <div class="container">
<Label For="Name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label> <div class="row mb-1 align-items-center">
</td> <Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
<td> <div class="col-sm-9">
<input id="Name" class="form-control" @bind="@_name" /> <input id="name" class="form-control" @bind="@_name" required />
</td> </div>
</tr> </div>
<tr> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label> <div class="col-sm-9">
</td> <select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
<td> <option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
<select id="Parent" class="form-select" @onchange="(e => ParentChanged(e))"> @foreach (Page page in _pageList)
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option> {
@foreach (Page page in _pageList) <option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<select id="insert" class="form-select" @bind="@_insert" required>
<option value="<<">@Localizer["AtBeginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">@Localizer["AtEnd"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{ {
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option> <select class="form-select" @bind="@_childid">
<option value="-1">&lt;@Localizer["Page.Select"]&gt;</option>
@foreach (Page page in _children)
{
<option value="@(page.PageId)">@(page.Name)</option>
}
</select>
} }
</select> </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
<td> <div class="col-sm-9">
<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> <select id="navigation" class="form-select" @bind="@_isnavigation" required>
</td>
<td>
<select id="Insert" class="form-select" @bind="@_insert">
<option value="<<">@Localizer["AtBeginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">@Localizer["AtEnd"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{
<select class="form-select" @bind="@_childid">
<option value="-1">&lt;@Localizer["Page.Select"]&gt;</option>
@foreach (Page page in _children)
{
<option value="@(page.PageId)">@(page.Name)</option>
}
</select>
}
</td>
</tr>
<tr>
<td>
<Label For="navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
</td>
<td>
<select id="navigation" class="form-select" @bind="@_isnavigation">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
</td>
<td>
<select id="clickable" class="form-select" @bind="@_isclickable">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<input id="Path" class="form-control" @bind="@_path" />
</td>
</tr>
<tr>
<td>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
</td>
<td>
<input id="Url" class="form-control" @bind="@_url" />
</td>
</tr>
</table>
<Section Name="Appearance" ResourceKey="Appearance">
<table class="table table-borderless">
<tr>
<td width="30%">
<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>
</td>
<td>
<input id="Title" class="form-control" @bind="@_title" />
</td>
</tr>
<tr>
<td>
<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)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<input id="Icon" class="form-control" @bind="@_icon" />
</td>
</tr>
<tr>
<td>
<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 class="row mb-1 align-items-center">
</Section> <Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
} <div class="col-sm-9">
</TabPanel> <select id="clickable" class="form-select" @bind="@_isclickable" required>
<TabPanel Name="Permissions" ResourceKey="Permissions"> <option value="True">@SharedLocalizer["Yes"]</option>
<table class="table table-borderless"> <option value="False">@SharedLocalizer["No"]</option>
<tr> </select>
<td> </div>
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <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>
</table> <div class="col-sm-9">
</TabPanel> <input id="path" class="form-control" @bind="@_path" />
@if (_themeSettingsType != null) </div>
{ </div>
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings"> <div class="row mb-1 align-items-center">
@ThemeSettingsComponent <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>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" />
</div>
</div>
</div>
<Section Name="Appearance" ResourceKey="Appearance">
<div class="container">
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<input id="title" class="form-control" @bind="@_title" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9">
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
<div class="col-sm-9">
<select id="container" class="form-select" @bind="@_containertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<input id="icon" class="form-control" @bind="@_icon" />
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<select id="personalizable" class="form-select" @bind="@_ispersonalizable" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
</Section>
}
</TabPanel> </TabPanel>
} <TabPanel Name="Permissions" ResourceKey="Permissions">
</TabStrip> <div class="container">
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button> <div class="row mb-1 align-items-center">
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</div>
</div>
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</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()
{ {
@ -300,110 +280,125 @@
private async Task SavePage() private async Task SavePage()
{ {
Page page = null; validated = true;
try var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(_themetype) && _containertype != "-") Page page = null;
try
{ {
page = new Page(); if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
page.SiteId = PageState.Page.SiteId;
page.Name = _name;
page.Title = _title;
if (_path == "")
{ {
_path = _name; page = new Page();
} page.SiteId = PageState.Page.SiteId;
page.Name = _name;
if (_path.Contains("/")) page.Title = _title;
{ if (string.IsNullOrEmpty(_path))
_path = _path.Substring(_path.LastIndexOf("/") + 1);
}
if (string.IsNullOrEmpty(_parentid))
{
page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path);
}
else
{
page.ParentId = Int32.Parse(_parentid);
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
if (parent.Path == string.Empty)
{ {
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); _path = _name;
}
if (_path.Contains("/"))
{
_path = _path.Substring(_path.LastIndexOf("/") + 1);
}
if (_parentid == "-1")
{
page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path);
} }
else else
{ {
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); page.ParentId = Int32.Parse(_parentid);
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
if (parent.Path == string.Empty)
{
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
}
else
{
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
}
} }
}
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList)) if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
{ {
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning); AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
return; return;
} }
Page child; if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
switch (_insert) {
{ AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
case "<<": return;
page.Order = 0; }
break;
case "<":
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.Order = child.Order - 1;
break;
case ">":
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.Order = child.Order + 1;
break;
case ">>":
page.Order = int.MaxValue;
break;
}
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); Page child;
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable)); switch (_insert)
page.Url = _url; {
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; case "<<":
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) page.Order = 0;
{ break;
page.ThemeType = string.Empty; case "<":
} child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty; page.Order = child.Order - 1;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType) break;
{ case ">":
page.DefaultContainerType = string.Empty; child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
} page.Order = child.Order + 1;
page.Icon = (_icon == null ? string.Empty : _icon); break;
page.Permissions = _permissionGrid.GetPermissions(); case ">>":
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); page.Order = int.MaxValue;
page.UserId = null; break;
}
page = await PageService.AddPageAsync(page); page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
page.Url = _url;
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
page.ThemeType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
page.DefaultContainerType = string.Empty;
}
page.Icon = (_icon == null ? string.Empty : _icon);
page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
page.UserId = null;
await logger.LogInformation("Page Added {Page}", page); page = await PageService.AddPageAsync(page);
if (PageState.QueryString.ContainsKey("cp")) await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path)); await logger.LogInformation("Page Added {Page}", page);
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl(page.Path));
}
} }
else else
{ {
NavigationManager.NavigateTo(NavigateUrl(page.Path)); AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
} }
}
else
{
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
}
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
} }
} }
@ -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,192 +7,173 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip Refresh="@_refresh"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabPanel Name="Settings" ResourceKey="Settings"> <TabStrip Refresh="@_refresh">
@if (_themeList != null) <TabPanel Name="Settings" ResourceKey="Settings">
{ @if (_themeList != null)
<table class="table table-borderless"> {
<tr> <div class="container">
<td width="30%"> <div class="row mb-1 align-items-center">
<Label For="Name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="Enter the page name" 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="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
<td> <div class="col-sm-9">
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label> <select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
</td> <option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
<td> @foreach (Page page in _pageList)
<select id="Parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))">
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
@foreach (Page page in _pageList)
{
if (page.PageId != _pageId)
{ {
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option> if (page.PageId != _pageId)
} {
} <option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
</select> }
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<select id="Move" class="form-select" @bind="@_insert">
@if (_parentid == _currentparentid)
{
<option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option>
}
<option value="<<">@Localizer["ToBeginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">@Localizer["ToEnd"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{
<select class="form-select" @bind="@_childid">
<option value="-1">&lt;@Localizer["Page.Select"]&gt;</option>
@foreach (Page page in _children)
{
<option value="@(page.PageId)">@(page.Name)</option>
} }
</select> </select>
} </div>
</td> </div>
</tr> <div class="row mb-1 align-items-center">
<tr> <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>
<td> <div class="col-sm-9">
<Label For="Navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label> <select id="move" class="form-select" @bind="@_insert" required>
</td> @if (_parentid == _currentparentid)
<td>
<select id="Navigation" class="form-select" @bind="@_isnavigation">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Clickablen" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
</td>
<td>
<select id="Navigation" class="form-select" @bind="@_isclickable">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<input id="Path" class="form-control" @bind="@_path" />
</td>
</tr>
<tr>
<td>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
</td>
<td>
<input id="Url" class="form-control" @bind="@_url" />
</td>
</tr>
</table>
<Section Name="Appearance" ResourceKey="Appearance">
<table class="table table-borderless">
<tr>
<td width="30%">
<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>
</td>
<td>
<input id="Title" class="form-control" @bind="@_title" />
</td>
</tr>
<tr>
<td>
<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)
{ {
<option value="@theme.TypeName">@theme.Name</option> <option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option>
} }
</select> <option value="<<">@Localizer["ToBeginning"]</option>
</td> @if (_children != null && _children.Count > 0)
</tr>
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
} }
<option value=">>">@Localizer["ToEnd"]</option>
</select> </select>
</td> @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
</tr> {
<tr> <select class="form-select" @bind="@_childid">
<td> <option value="-1">&lt;@Localizer["Page.Select"]&gt;</option>
<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> @foreach (Page page in _children)
</td> {
<td> <option value="@(page.PageId)">@(page.Name)</option>
<input id="Icon" class="form-control" @bind="@_icon" /> }
</td> </select>
</tr> }
<tr> </div>
<td> </div>
<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> <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>
<td> <div class="col-sm-9">
<select id="Personalizable" class="form-select" @bind="@_ispersonalizable"> <select id="navigation" class="form-select" @bind="@_isnavigation" required>
<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 class="row mb-1 align-items-center">
</Section> <Label Class="col-sm-3" For="clickable" HelpText="Select whether the link in the site navigation is enabled or disabled" ResourceKey="Clickable">Clickable? </Label>
<br /><br /> <div class="col-sm-9">
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo> <select id="clickable" class="form-select" @bind="@_isclickable" required>
} <option value="True">@SharedLocalizer["Yes"]</option>
</TabPanel> <option value="False">@SharedLocalizer["No"]</option>
<TabPanel Name="Permissions" ResourceKey="Permissions"> </select>
@if (_permissions != null) </div>
{ </div>
<table class="table table-borderless"> <div class="row mb-1 align-items-center">
<tr> <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>
<td> <div class="col-sm-9">
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" /> <input id="path" class="form-control" @bind="@_path" maxlength="256"/>
</td> </div>
</tr> </div>
</table> <div class="row mb-1 align-items-center">
} <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>
</TabPanel> <div class="col-sm-9">
@if (_themeSettingsType != null) <input id="url" class="form-control" @bind="@_url" maxlength="500"/>
{ </div>
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings"> </div>
@ThemeSettingsComponent </div>
<Section Name="Appearance" ResourceKey="Appearance">
<div class="container">
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<input id="title" class="form-control" @bind="@_title" maxlength="200"/>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9">
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
<div class="col-sm-9">
<select id="container" class="form-select" @bind="@_containertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<input id="icon" class="form-control" @bind="@_icon" maxlength="50"/>
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<select id="personalizable" class="form-select" @bind="@_ispersonalizable" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
</Section>
<br /><br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
</TabPanel> </TabPanel>
} <TabPanel Name="Permissions" ResourceKey="Permissions">
</TabStrip> @if (_permissions != null)
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button> {
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button> <div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</div>
</div>
}
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
<br />
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</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
{ {
@ -375,134 +356,142 @@
private async Task SavePage() private async Task SavePage()
{ {
Page page = null; validated = true;
try var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && _containertype != "-") Page page = null;
try
{ {
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
string currentPath = page.Path; {
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
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;
} }
if (_path.Contains("/")) if (_path.Contains("/"))
{
_path = _path.Substring(_path.LastIndexOf("/") + 1);
}
if (string.IsNullOrEmpty(_parentid) || _parentid == "-1")
{
page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path);
}
else
{
page.ParentId = Int32.Parse(_parentid);
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
if (parent.Path == string.Empty)
{ {
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path); _path = _path.Substring(_path.LastIndexOf("/") + 1);
}
if (_parentid == "-1")
{
page.ParentId = null;
page.Path = Utilities.GetFriendlyUrl(_path);
} }
else else
{ {
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); page.ParentId = Int32.Parse(_parentid);
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
if (parent.Path == string.Empty)
{
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
}
else
{
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
}
} }
}
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList)) if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
{
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
return;
}
if (_insert != "=")
{
Page child;
switch (_insert)
{ {
case "<<": AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
page.Order = 0; return;
break;
case "<":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) page.Order = child.Order - 1;
break;
case ">":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) page.Order = child.Order + 1;
break;
case ">>":
page.Order = int.MaxValue;
break;
} }
}
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
page.Url = _url;
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
page.ThemeType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
page.DefaultContainerType = string.Empty;
}
page.Icon = _icon ?? string.Empty;
page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
page.UserId = null;
page = await PageService.UpdatePageAsync(page); if (_insert != "=")
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId); {
if (_currentparentid == string.Empty) Page child;
{ switch (_insert)
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, null); {
case "<<":
page.Order = 0;
break;
case "<":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) page.Order = child.Order - 1;
break;
case ">":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) page.Order = child.Order + 1;
break;
case ">>":
page.Order = int.MaxValue;
break;
}
}
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.IsClickable = (_isclickable == null ? true : Boolean.Parse(_isclickable));
page.Url = _url;
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
page.ThemeType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
page.DefaultContainerType = string.Empty;
}
page.Icon = _icon ?? string.Empty;
page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
page.UserId = null;
page = await PageService.UpdatePageAsync(page);
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
if (_currentparentid == string.Empty)
{
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, null);
}
else
{
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, int.Parse(_currentparentid));
}
// update child paths
if (_parentid != _currentparentid)
{
foreach (Page p in PageState.Pages.Where(item => item.Path.StartsWith(currentPath)))
{
p.Path = p.Path.Replace(currentPath, page.Path);
await PageService.UpdatePageAsync(p);
}
}
if (_themeSettingsType != null && _themeSettings is ISettingsControl themeSettingsControl)
{
await themeSettingsControl.UpdateSettings();
}
await logger.LogInformation("Page Saved {Page}", page);
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl(page.Path));
}
} }
else else
{ {
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, int.Parse(_currentparentid)); AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
}
// update child paths
if (_parentid != _currentparentid)
{
foreach (Page p in PageState.Pages.Where(item => item.Path.StartsWith(currentPath)))
{
p.Path = p.Path.Replace(currentPath, page.Path);
await PageService.UpdatePageAsync(p);
}
}
if (_themeSettingsType != null && _themeSettings is ISettingsControl themeSettingsControl)
{
await themeSettingsControl.UpdateSettings();
}
await logger.LogInformation("Page Saved {Page}", page);
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl(page.Path));
} }
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning); await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
} }
} }

View File

@ -5,105 +5,90 @@
@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" /> <option value="True">@SharedLocalizer["Yes"]</option>
</td> <option value="False">@SharedLocalizer["No"]</option>
</tr> </select>
<tr> </div>
<td> </div>
<Label For="options" HelpText="A comma delimited list of options the user can select from" ResourceKey="Options">Options: </Label> <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>
<td> <div class="col-sm-9">
<input id="options" class="form-control" @bind="@_options" /> <select id="private" class="form-select" @bind="@_isprivate" required>
</td> <option value="True">@SharedLocalizer["Yes"]</option>
</tr> <option value="False">@SharedLocalizer["No"]</option>
<tr> </select>
<td> </div>
<Label For="required" HelpText="Should a user be required to provide a value for this profile item?" ResourceKey="Required">Required? </Label> </div>
</td> </div>
<td>
<select id="required" class="form-select" @bind="@_isrequired">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
</td>
<td>
<select id="private" class="form-select" @bind="@_isprivate">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveProfile">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@if (PageState.QueryString.ContainsKey("id"))
{
<br /> <br />
<br /> <button type="button" class="btn btn-success" @onclick="SaveProfile">@SharedLocalizer["Save"]</button>
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
} @if (PageState.QueryString.ContainsKey("id"))
{
<br />
<br />
<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;
@ -159,45 +144,54 @@
private async Task SaveProfile() private async Task SaveProfile()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
Profile profile; try
if (_profileid != -1)
{ {
profile = await ProfileService.GetProfileAsync(_profileid); Profile profile;
} if (_profileid != -1)
else {
{ profile = await ProfileService.GetProfileAsync(_profileid);
profile = new Profile(); }
} else
{
profile = new Profile();
}
profile.SiteId = PageState.Site.SiteId; profile.SiteId = PageState.Site.SiteId;
profile.Name = _name; profile.Name = _name;
profile.Title = _title; profile.Title = _title;
profile.Description = _description; profile.Description = _description;
profile.Category = _category; profile.Category = _category;
profile.ViewOrder = int.Parse(_vieworder); profile.ViewOrder = int.Parse(_vieworder);
profile.MaxLength = int.Parse(_maxlength); profile.MaxLength = int.Parse(_maxlength);
profile.DefaultValue = _defaultvalue; profile.DefaultValue = _defaultvalue;
profile.Options = _options; profile.Options = _options;
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired)); profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate)); profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
if (_profileid != -1) if (_profileid != -1)
{ {
profile = await ProfileService.UpdateProfileAsync(profile); profile = await ProfileService.UpdateProfileAsync(profile);
} }
else else
{ {
profile = await ProfileService.AddProfileAsync(profile); profile = await ProfileService.AddProfileAsync(profile);
} }
await logger.LogInformation("Profile Saved {Profile}", profile); await logger.LogInformation("Profile Saved {Profile}", profile);
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Profile {ProfleId} {Error}", _profileid, ex.Message);
AddModuleMessage(Localizer["Error.Profile.Save"], MessageType.Error);
}
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving Profile {ProfleId} {Error}", _profileid, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Profile.Save"], MessageType.Error);
} }
} }
} }

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> <button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
<td> <button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<Label For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label> </form>
</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-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
</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;
@ -81,49 +74,58 @@ else
private async Task Register() private async Task Register()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
bool _isEmailValid = Utilities.IsValidEmail(_email); try
if (_username != "" && _password != "" && _confirm != "" && _isEmailValid)
{ {
if (_password == _confirm) bool _isEmailValid = Utilities.IsValidEmail(_email);
{
var user = new User
{
SiteId = PageState.Site.SiteId,
Username = _username,
DisplayName = (_displayname == string.Empty ? _username : _displayname),
Email = _email,
Password = _password
};
user = await UserService.AddUserAsync(user);
if (user != null) if (_isEmailValid)
{
if (_password == _confirm)
{ {
await logger.LogInformation("User Created {Username} {Email}", _username, _email); var user = new User
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info); {
SiteId = PageState.Site.SiteId,
Username = _username,
DisplayName = (_displayname == string.Empty ? _username : _displayname),
Email = _email,
Password = _password
};
user = await UserService.AddUserAsync(user);
if (user != null)
{
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
}
else
{
await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
AddModuleMessage(Localizer["Error.User.AddInfo"], MessageType.Error);
}
} }
else else
{ {
await logger.LogError("Error Adding User {Username} {Email}", _username, _email); AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
AddModuleMessage(Localizer["Error.User.AddInfo"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
} }
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning); await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", _username, _email, ex.Message);
AddModuleMessage(Localizer["Error.User.Add"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", _username, _email, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.User.Add"], MessageType.Error);
} }
} }
@ -131,4 +133,4 @@ else
{ {
NavigationManager.NavigateTo(NavigateUrl(string.Empty)); NavigationManager.NavigateTo(NavigateUrl(string.Empty));
} }
} }

View File

@ -5,24 +5,28 @@
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="container"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="form-group"> <div class="container">
<label for="Username" class="control-label">@SharedLocalizer["Username"] </label> <div class="form-group">
<input type="text" class="form-control" placeholder="Username" @bind="@_username" readonly id="Username" /> <label for="Username" class="control-label">@SharedLocalizer["Username"] </label>
<input type="text" class="form-control" placeholder="Username" @bind="@_username" readonly id="Username" />
</div>
<div class="form-group">
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" required />
</div>
<div class="form-group">
<label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" required />
</div>
<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>
</div> </div>
<div class="form-group"> </form>
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" />
</div>
<div class="form-group">
<label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" />
</div>
<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>
</div>
@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;
@ -43,45 +47,54 @@
private async Task Reset() private async Task Reset()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty) try
{ {
if (_password == _confirm) if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty)
{ {
var user = new User if (_password == _confirm)
{ {
SiteId = PageState.Site.SiteId, var user = new User
Username = _username, {
Password = _password SiteId = PageState.Site.SiteId,
}; Username = _username,
user = await UserService.ResetPasswordAsync(user, PageState.QueryString["token"]); Password = _password
};
user = await UserService.ResetPasswordAsync(user, PageState.QueryString["token"]);
if (user != null) if (user != null)
{ {
await logger.LogInformation("User Password Reset {Username}", _username); await logger.LogInformation("User Password Reset {Username}", _username);
NavigationManager.NavigateTo(NavigateUrl("login")); NavigationManager.NavigateTo(NavigateUrl("login"));
}
else
{
await logger.LogError("Error Resetting User Password {Username}", _username);
AddModuleMessage(Localizer["Error.Password.ResetInfo"], MessageType.Error);
}
} }
else else
{ {
await logger.LogError("Error Resetting User Password {Username}", _username); AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Password.ResetInfo"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
} }
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning); await logger.LogError(ex, "Error Resetting User Password {Username} {Error}", _username, ex.Message);
AddModuleMessage(Localizer["Error.Password.Reset"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Resetting User Password {Username} {Error}", _username, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.Password.Reset"], MessageType.Error);
} }
} }

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,71 +11,71 @@
} }
else else
{ {
<table class="table table-borderless">
<tr>
<td width="30%">
<Label For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label>
</td>
<td>
<input id="role" class="form-control" @bind="@name" disabled />
</td>
</tr>
<tr>
<td>
<Label For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
</td>
<td>
<select id="user" class="form-select" @bind="@userid">
<option value="-1">&lt;@Localizer["User.Select"]&gt;</option>
@foreach (UserRole userrole in users)
{
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
</td>
<td>
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
</td>
</tr>
<tr>
<td>
<Label For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
</td>
<td>
<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>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<hr class="app-rule" /> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<p align="center"> <div class="container">
<Pager Items="@userroles"> <div class="row mb-1 align-items-center">
<Header> <Label Class="col-sm-3" For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label>
<th>@Localizer["Users"]</th> <div class="col-sm-9">
<th>@Localizer["Effective"]</th> <input id="role" class="form-control" @bind="@name" disabled />
<th>@Localizer["Expiry"]</th> </div>
<th>&nbsp;</th> </div>
</Header> <div class="row mb-1 align-items-center">
<Row> <Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
<td>@context.User.DisplayName</td> <div class="col-sm-9">
<td>@context.EffectiveDate</td> <select id="user" class="form-select" @bind="@userid" required>
<td>@context.ExpiryDate</td> <option value="-1">&lt;@Localizer["User.Select"]&gt;</option>
<td> @foreach (UserRole userrole in users)
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || PageState.User.Username == UserNames.Host)" ResourceKey="DeleteUserRole" /> {
</td> <option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
</Row> }
</Pager> </select>
</p> </div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<div class="col-sm-9">
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<div class="col-sm-9">
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" required />
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<hr class="app-rule" />
<div class="row mb-1 align-items-center">
<p align="center">
<Pager Items="@userroles">
<Header>
<th>@Localizer["Users"]</th>
<th>@Localizer["Effective"]</th>
<th>@Localizer["Expiry"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.User.DisplayName</td>
<td>@context.EffectiveDate</td>
<td>@context.ExpiryDate</td>
<td>
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || PageState.User.Username == UserNames.Host)" ResourceKey="DeleteUserRole" />
</td>
</Row>
</Pager>
</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;
@ -123,59 +123,78 @@ else
private async Task SaveUserRole() private async Task SaveUserRole()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (userid != -1) try
{ {
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault(); if (userid != -1)
if (userrole != null)
{ {
userrole.EffectiveDate = effectivedate; var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
userrole.ExpiryDate = expirydate; if (userrole != null)
await UserRoleService.UpdateUserRoleAsync(userrole); {
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.UpdateUserRoleAsync(userrole);
}
else
{
userrole = new UserRole();
userrole.UserId = userid;
userrole.RoleId = roleid;
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.AddUserRoleAsync(userrole);
}
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
} }
else else
{ {
userrole = new UserRole(); AddModuleMessage(Localizer["Message.Required.UserSelect"], MessageType.Warning);
userrole.UserId = userid;
userrole.RoleId = roleid;
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.AddUserRoleAsync(userrole);
} }
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Required.UserSelect"], MessageType.Warning); await logger.LogError(ex, "Error Saving User Roles {RoleId} {Error}", roleid, ex.Message);
AddModuleMessage(Localizer["Error.User.SaveRole"], MessageType.Error);
} }
} }
catch (Exception ex)
else
{ {
await logger.LogError(ex, "Error Saving User Roles {RoleId} {Error}", roleid, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.User.SaveRole"], MessageType.Error);
} }
} }
private async Task DeleteUserRole(int UserRoleId) private async Task DeleteUserRole(int UserRoleId)
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
await UserRoleService.DeleteUserRoleAsync(UserRoleId); try
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId); {
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success); await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await GetUserRoles(); await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
StateHasChanged(); AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
}
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
} }
} }
} }

View File

@ -13,251 +13,217 @@
@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> @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
</td> {
<td> <textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) }
{ else
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea> {
} <textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea>
else }
{ </div>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea> </div>
} <div class="row mb-1 align-items-center">
</td> <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>
</tr> <div class="col-sm-9">
<select id="allowRegister" class="form-select" @bind="@_allowregistration" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
<div class="col-sm-9">
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
<div class="col-sm-9">
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
<div class="col-sm-9">
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
<div class="col-sm-9">
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
<div class="col-sm-9">
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
<div class="col-sm-9">
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="@Constants.DefaultAdminContainer">&lt;@Localizer["DefaultAdminContainer"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<tr> </div>
<td>
<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>
</td>
<td>
<select id="allowRegister" class="form-select" @bind="@_allowregistration">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
</td>
<td>
<select id="isDeleted" class="form-select" @bind="@_isdeleted">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
</table>
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<table class="table table-borderless">
<tr>
<td width="30%">
<Label For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
</td>
<td>
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
</td>
</tr>
<tr>
<td>
<Label For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
</td>
<td>
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</td>
</tr>
<tr>
<td>
<Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
</td>
<td>
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="@Constants.DefaultAdminContainer">&lt;@Localizer["DefaultAdminContainer"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
</table>
</Section>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<table class="table table-borderless">
<tr>
<td width="30%">&nbsp;</td>
<td>
<strong>@Localizer["Smtp.Required.EnableNotificationJob"]</strong><br />
</td>
</tr>
<tr>
<td>
<Label For="host" HelpText="Enter the host name of the SMTP server" ResourceKey="Host">Host: </Label>
</td>
<td>
<input id="host" class="form-control" @bind="@_smtphost" />
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<input id="port" class="form-control" @bind="@_smtpport" />
</td>
</tr>
<tr>
<td>
<Label For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
</td>
<td>
<select id="enabledSSl" class="form-select" @bind="@_smtpssl">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
</td>
<td>
<input id="username" class="form-control" @bind="@_smtpusername" />
</td>
</tr>
<tr>
<td>
<Label For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@_smtppassword" />
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<input id="sender" class="form-control" @bind="@_smtpsender" />
</td>
</tr>
</table>
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
<br /><br />
</Section>
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
<table class="table table-borderless">
<tr>
<td width="30%">
<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>
</td>
<td>
<select id="isEnabled" class="form-select" @bind="@_pwaisenabled">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
</td>
</tr>
<tr>
<td>
<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>
</td>
<td>
<FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
</td>
</tr>
</table>
</Section>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
<table class="table table-borderless">
<tr>
<td width="30%">
<Label For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</td>
</tr>
<tr>
<td>
<Label For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
</td>
<td>
<input id="database" class="form-control" @bind="@_database" readonly />
</td>
</tr>
<tr>
<td>
<Label For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
</td>
<td>
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
</td>
</tr>
</table>
</Section> </Section>
} <Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<br /> <div class="container">
<button type="button" class="btn btn-success" @onclick="SaveSite">@SharedLocalizer["Save"]</button> <div class="row mb-1 align-items-center">
<ActionDialog Header="Delete Site" Message="@Localizer["Confirm.DeleteSite"]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteSite())" ResourceKey="DeleteSite" /> <div class="col-sm-3">
<br /> </div>
<br /> <div class="col-sm-9">
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo> <strong>@Localizer["Smtp.Required.EnableNotificationJob"]</strong><br />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="host" HelpText="Enter the host name of the SMTP server" ResourceKey="Host">Host: </Label>
<div class="col-sm-9">
<input id="host" class="form-control" @bind="@_smtphost" />
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<input id="port" class="form-control" @bind="@_smtpport" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
<div class="col-sm-9">
<select id="enabledSSl" class="form-select" @bind="@_smtpssl" >
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
<div class="col-sm-9">
<input id="username" class="form-control" @bind="@_smtpusername" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
<div class="col-sm-9">
<input id="password" type="password" class="form-control" @bind="@_smtppassword" />
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<input id="sender" class="form-control" @bind="@_smtpsender" />
</div>
</div>
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
<br /><br />
</div>
</Section>
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
<div class="container">
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<select id="isEnabled" class="form-select" @bind="@_pwaisenabled" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
<div class="col-sm-9">
<FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
</div>
</div>
</div>
</Section>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
<div class="col-sm-9">
<input id="database" class="form-control" @bind="@_database" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
</div>
</div>
</div>
</Section>
}
<br />
<button type="button" class="btn btn-success" @onclick="SaveSite">@SharedLocalizer["Save"]</button>
<ActionDialog Header="Delete Site" Message="@Localizer["Confirm.DeleteSite"]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteSite())" ResourceKey="DeleteSite" />
<br />
<br />
<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>();
@ -422,121 +388,130 @@
private async Task SaveSite() private async Task SaveSite()
{ {
try validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-") try
{ {
var unique = true; if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) var unique = true;
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId)) foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{ {
unique = false; if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
{
unique = false;
}
} }
} }
}
if (unique) if (unique)
{
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
{ {
bool refresh = (site.DefaultThemeType != _themetype || site.DefaultContainerType != _containertype); var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
site.Name = _name;
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
site.LogoFileId = null;
var logofileid = _logofilemanager.GetFileId();
if (logofileid != -1)
{ {
site.LogoFileId = logofileid; bool refresh = (site.DefaultThemeType != _themetype || site.DefaultContainerType != _containertype);
}
var faviconFieldId = _faviconfilemanager.GetFileId();
if (faviconFieldId != -1)
{
site.FaviconFileId = faviconFieldId;
}
site.DefaultThemeType = _themetype;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled)); site.Name = _name;
var pwaappiconfileid = _pwaappiconfilemanager.GetFileId(); site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
if (pwaappiconfileid != -1) site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
{
site.PwaAppIconFileId = pwaappiconfileid;
}
var pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
if (pwasplashiconfileid != -1)
{
site.PwaSplashIconFileId = pwasplashiconfileid;
}
site = await SiteService.UpdateSiteAsync(site); site.LogoFileId = null;
var logofileid = _logofilemanager.GetFileId();
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); if (logofileid != -1)
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{ {
if (!names.Contains(alias.Name)) site.LogoFileId = logofileid;
}
var faviconFieldId = _faviconfilemanager.GetFileId();
if (faviconFieldId != -1)
{
site.FaviconFileId = faviconFieldId;
}
site.DefaultThemeType = _themetype;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled));
var pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
if (pwaappiconfileid != -1)
{
site.PwaAppIconFileId = pwaappiconfileid;
}
var pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
if (pwasplashiconfileid != -1)
{
site.PwaSplashIconFileId = pwasplashiconfileid;
}
site = await SiteService.UpdateSiteAsync(site);
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{ {
await AliasService.DeleteAliasAsync(alias.AliasId); if (!names.Contains(alias.Name))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
}
}
foreach (string name in names)
{
if (!_aliasList.Exists(item => item.Name == name))
{
Alias alias = new Alias();
alias.Name = name;
alias.TenantId = site.TenantId;
alias.SiteId = site.SiteId;
await AliasService.AddAliasAsync(alias);
}
} }
} }
foreach (string name in names) await logger.LogInformation("Site Settings Saved {Site}", site);
if (refresh)
{ {
if (!_aliasList.Exists(item => item.Name == name)) NavigationManager.NavigateTo(NavigateUrl()); // refresh to show new theme or container
{ }
Alias alias = new Alias(); else
alias.Name = name; {
alias.TenantId = site.TenantId; AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
alias.SiteId = site.SiteId;
await AliasService.AddAliasAsync(alias);
}
} }
} }
}
await logger.LogInformation("Site Settings Saved {Site}", site); else
{
if (refresh) AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
{
NavigationManager.NavigateTo(NavigateUrl()); // refresh to show new theme or container
}
else
{
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
}
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
} }
} }
else catch (Exception ex)
{ {
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning); await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
} }
} }
catch (Exception ex) else
{ {
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
} }
} }

View File

@ -19,158 +19,140 @@
} }
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> <option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label> @foreach (var theme in _themes)
</td>
<td>
<select id="defaultTheme" class="form-select" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-select" @bind="@_containertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
</td>
<td>
<select id="adminContainer" class="form-select" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="">&lt;@Localizer["DefaultContainer.Admin"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
</td>
<td>
<select id="siteTemplate" class="form-select" @bind="@_sitetemplatetype">
<option value="-">&lt;@Localizer["SiteTemplate.Select"]&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates)
{
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))">
<option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option>
<option value="+">&lt;@Localizer["Tenant.Add"]&gt;</option>
@foreach (Tenant tenant in _tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
}
</select>
</td>
</tr>
@if (_tenantid == "+")
{
<tr>
<td colspan="2">
<hr class="app-rule" />
</td>
</tr>
<tr>
<td>
<Label For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_tenantName" />
</td>
</tr>
<tr>
<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)
{ {
if (database.IsDefault) <option value="@theme.TypeName">@theme.Name</option>
{
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
}
else
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
} }
</select> </select>
</td> </div>
</tr> </div>
if (_databaseConfigType != null) <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
<div class="col-sm-9">
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
<div class="col-sm-9">
<select id="adminContainer" class="form-select" @bind="@_admincontainertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="">&lt;@Localizer["DefaultContainer.Admin"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
<div class="col-sm-9">
<select id="siteTemplate" class="form-select" @bind="@_sitetemplatetype" required>
<option value="-">&lt;@Localizer["SiteTemplate.Select"]&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates)
{
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<div class="col-sm-9">
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
<option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option>
<option value="+">&lt;@Localizer["Tenant.Add"]&gt;</option>
@foreach (Tenant tenant in _tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
}
</select>
</div>
</div>
@if (_tenantid == "+")
{ {
@DatabaseConfigComponent; <div class="row mb-1 align-items-center">
<hr class="app-rule" />
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
<div class="col-sm-9">
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
@foreach (var database in _databases)
{
if (database.IsDefault)
{
<option value="@database.Name" selected>@Localizer[@database.Name]</option>
}
else
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
}
</select>
</div>
</div>
if (_databaseConfigType != null)
{
@DatabaseConfigComponent;
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label>
<div class="col-sm-9">
<input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label>
<div class="col-sm-9">
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" required />
</div>
</div>
} }
<tr> </div>
<td> <br />
<Label For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label> <br />
</td> <button type="button" class="btn btn-success" @onclick="SaveSite">@SharedLocalizer["Save"]</button>
<td> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly /> </form>
</td>
</tr>
<tr>
<td>
<Label For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label>
</td>
<td>
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="SaveSite">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
} }
@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;
@ -274,111 +256,120 @@ else
private async Task SaveSite() private async Task SaveSite()
{ {
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-") validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
var duplicates = new List<string>(); if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
var aliases = await AliasService.GetAliasesAsync();
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{ {
if (aliases.Exists(item => item.Name == name)) var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync();
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{ {
duplicates.Add(name); if (aliases.Exists(item => item.Name == name))
}
}
if (duplicates.Count == 0)
{
InstallConfig config = new InstallConfig();
if (_tenantid == "+")
{
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null)
{ {
// validate host credentials duplicates.Add(name);
var user = new User(); }
user.SiteId = PageState.Site.SiteId; }
user.Username = UserNames.Host;
user.Password = _hostpassword;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
{
var connectionString = String.Empty;
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
connectionString = databaseConfigControl.GetConnectionString();
}
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
if (connectionString != "") if (duplicates.Count == 0)
{
InstallConfig config = new InstallConfig();
if (_tenantid == "+")
{
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null)
{
// validate host credentials
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = UserNames.Host;
user.Password = _hostpassword;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
{ {
config.TenantName = _tenantName; var connectionString = String.Empty;
config.DatabaseType = database.DBType; if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
config.ConnectionString = connectionString; {
config.HostEmail = user.Email; connectionString = databaseConfigControl.GetConnectionString();
config.HostPassword = _hostpassword; }
config.HostName = user.DisplayName; var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
config.IsNewTenant = true;
if (connectionString != "")
{
config.TenantName = _tenantName;
config.DatabaseType = database.DBType;
config.ConnectionString = connectionString;
config.HostEmail = user.Email;
config.HostPassword = _hostpassword;
config.HostName = user.DisplayName;
config.IsNewTenant = true;
}
else
{
AddModuleMessage(Localizer["Error.Required.ServerDatabase"], MessageType.Error);
}
} }
else else
{ {
AddModuleMessage(Localizer["Error.Required.ServerDatabase"], MessageType.Error); AddModuleMessage(Localizer["Error.InvalidPassword"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Error.InvalidPassword"], MessageType.Error); AddModuleMessage(Localizer["Error.TenantName.Exists"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Error.TenantName.Exists"], MessageType.Error); var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid));
if (tenant != null)
{
config.TenantName = tenant.Name;
config.DatabaseType = tenant.DBType;
config.ConnectionString = tenant.DBConnectionString;
config.IsNewTenant = false;
}
}
if (!string.IsNullOrEmpty(config.TenantName))
{
config.SiteName = _name;
config.Aliases = _urls;
config.DefaultTheme = _themetype;
config.DefaultContainer = _containertype;
config.DefaultAdminContainer = _admincontainertype;
config.SiteTemplate = _sitetemplatetype;
ShowProgressIndicator();
var installation = await InstallationService.Install(config);
if (installation.Success)
{
var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
var uri = new Uri(NavigationManager.Uri);
NavigationManager.NavigateTo(uri.Scheme + "://" + aliasname, true);
}
else
{
await logger.LogError("Error Creating Site {Error}", installation.Message);
AddModuleMessage(installation.Message, MessageType.Error);
}
} }
} }
else else
{ {
var tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); AddModuleMessage(string.Format(Localizer["Message.SiteName.InUse"], string.Join(", ", duplicates.ToArray())), MessageType.Warning);
if (tenant != null)
{
config.TenantName = tenant.Name;
config.DatabaseType = tenant.DBType;
config.ConnectionString = tenant.DBConnectionString;
config.IsNewTenant = false;
}
}
if (!string.IsNullOrEmpty(config.TenantName))
{
config.SiteName = _name;
config.Aliases = _urls;
config.DefaultTheme = _themetype;
config.DefaultContainer = _containertype;
config.DefaultAdminContainer = _admincontainertype;
config.SiteTemplate = _sitetemplatetype;
ShowProgressIndicator();
var installation = await InstallationService.Install(config);
if (installation.Success)
{
var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
var uri = new Uri(NavigationManager.Uri);
NavigationManager.NavigateTo(uri.Scheme + "://" + aliasname, true);
}
else
{
await logger.LogError("Error Creating Site {Error}", installation.Message);
AddModuleMessage(installation.Message, MessageType.Error);
}
} }
} }
else else
{ {
AddModuleMessage(string.Format(Localizer["Message.SiteName.InUse"], string.Join(", ", duplicates.ToArray())), MessageType.Warning); AddModuleMessage(Localizer["Message.Required.Tenant"], MessageType.Warning);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Required.Tenant"], MessageType.Warning); 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,66 +11,56 @@
@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> <input id="owner" class="form-control" @bind="@_owner" />
<td> </div>
<input id="owner" class="form-control" @bind="@_owner" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <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>
<tr> <div class="col-sm-9">
<td> <input id="module" class="form-control" @bind="@_theme" />
<Label For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label> </div>
</td> </div>
<td> <div class="row mb-1 align-items-center">
<input id="module" class="form-control" @bind="@_theme" /> <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>
</td> <div class="col-sm-9">
</tr> <select id="template" class="form-select" @onchange="(e => TemplateChanged(e))">
<tr> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
<td> @foreach (Template template in _templates)
<Label For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label> {
</td> <option value="@template.Name">@template.Title</option>
<td> }
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))"> </select>
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option> </div>
@foreach (Template template in _templates) </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<div class="col-sm-9">
<select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions)
{
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{ {
<option value="@template.Name">@template.Title</option> <option value="@(version)">@(version)</option>
} }
</select> }
</td> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</tr> </select>
<tr> </div>
<td> </div>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label> @if (!string.IsNullOrEmpty(_location)) {
</td> <div class="row mb-1 align-items-center">
<td> <Label Class="col-sm-3" For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
<select id="reference" class="form-select" @bind="@_reference"> <div class="col-sm-9">
@foreach (string version in _versions) <input id="module" class="form-control" @bind="@_location" readonly />
{ </div>
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0) </div>
{ }
<option value="@(version)">@(version)</option> </div>
} <br />
}
<option value="local">@SharedLocalizer["LocalVersion"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<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 />
</td>
</tr>
}
</table>
<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,11 +36,14 @@ 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))
{ {
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button> <button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
} }
</td> </td>
<td></td> <td></td>
</Row> </Row>
@ -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> <input id="to" class="form-control" @bind="@username" />
<td> </div> >
<input id="to" class="form-control" @bind="@username" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
<tr> <div class="col-sm-9">
<td> <input id="subject" class="form-control" @bind="@subject" />
<Label For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label> </div>
</td> </div>
<td> <div class="row mb-1 align-items-center">
<input id="subject" class="form-control" @bind="@subject" /> <Label Class="col-sm-3" For="message" HelpText="Enter the message" ResourceKey="Message">Message: </Label>
</td> <div class="col-sm-9">
</tr> <textarea id="message" class="form-control" @bind="@body" rows="5" />
<tr> </div>
<td> </div>
<Label For="message" HelpText="Enter the message" ResourceKey="Message">Message: </Label> </div>
</td> <br/>
<td>
<textarea id="message" class="form-control" @bind="@body" rows="5" />
</td>
</tr>
</table>
<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> <input id="password" type="password" class="form-control" @bind="@password" autocomplete="new-password" />
<td> </div>
<input id ="password" type="password" class="form-control" @bind="@password" autocomplete="new-password" /> </div>
</td> <div class="row mb-1 align-items-center">
</tr> <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>
<tr> <div class="col-sm-9">
<td>
<Label For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
</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,58 +68,56 @@ 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">
@foreach (Profile profile in profiles) <div class="row mb-1 align-items-center">
{ @foreach (Profile profile in profiles)
var p = profile;
if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{ {
if (p.Category != category) var p = profile;
if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{ {
<tr> if (p.Category != category)
<th colspan="2" style="text-align: center;"> {
<div class="col text-center pb-2">
@p.Category @p.Category
</th> </div>
</tr> category = p.Category;
category = p.Category; }
} <div class="row mb-1 align-items-center">
<tr> <Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<td width="30%"> <div class="col-sm-9">
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label> @if (!string.IsNullOrEmpty(p.Options))
</td>
<td>
@if (!string.IsNullOrEmpty(p.Options))
{
<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)
{ {
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" /> <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 else
{ {
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" /> @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))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
} }
} </div>
</td> </div>
</tr> }
} }
} </div>
</table> </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> <textarea class="form-control" @bind="@body" rows="5" readonly />
<td> </div>
<textarea class="form-control" @bind="@body" rows="5" readonly /> </div>
</td>
</tr>
} }
@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,87 +11,78 @@
<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">
@foreach (Profile profile in profiles) <div class="row mb-1 align-items-center">
{ @foreach (Profile profile in profiles)
var p = profile;
if (p.Category != category)
{ {
<tr> var p = profile;
<th colspan="2" style="text-align: center;"> if (p.Category != category)
@p.Category {
</th> <div class="col text-center pb-2">
</tr> <strong>@p.Category</strong>
category = p.Category; </div>
category = p.Category;
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<div class="col-sm-9">
@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))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</div>
</div>
} }
<tr>
<td width="30%"> </div>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label> </div>
</td>
<td>
@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))" />
}
else
{
<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>
} }
</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,102 +20,106 @@ 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">
@foreach (Profile profile in profiles) <div class="row mb-1 align-items-center">
{ @foreach (Profile profile in profiles)
var p = profile;
if (p.Category != category)
{ {
<tr> var p = profile;
<th colspan="2" style="text-align: center;"> if (p.Category != category)
@p.Category {
</th> <div class="col text-center pb-2">
</tr> <strong>@p.Category</strong>
category = p.Category; </div>
category = p.Category;
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<div class="col-sm-9">
@if (!string.IsNullOrEmpty(p.Options))
{
<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)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
}
</div>
</div>
} }
<tr> </div>
<td width="30%"> </div>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
@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))" />
}
else
{
<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>
} }
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>

View File

@ -14,33 +14,32 @@
} }
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>
<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>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
</Header> </Header>
<Row> <Row>
<td> <td>
<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,36 +29,34 @@ 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">
<Header> <Header>
<th>@Localizer["Roles"]</th> <th>@Localizer["Roles"]</th>
<th>@Localizer["Effective"]</th> <th>@Localizer["Effective"]</th>
<th>@Localizer["Expiry"]</th> <th>@Localizer["Expiry"]</th>
<th>&nbsp;</th> <th>&nbsp;</th>
</Header> </Header>
<Row> <Row>
<td>@context.Role.Name</td> <td>@context.Role.Name</td>

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();
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
if (upload.Length == 1) if (file != null)
{ {
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); FileId = file.FileId;
if (file != null) await SetImage();
{ await OnUpload.InvokeAsync(FileId);
FileId = file.FileId;
await SetImage();
}
} }
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") {
@if (Toolbar == "Top" && _pages > 0 && Items.Count() > _maxItems)
{ {
<div class="mx-auto text-center"> <ul class="pagination justify-content-center my-2">
@if (_endPage > 1) <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)
{ {
<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> <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>
</li>
@for (int i = _startPage; i <= _endPage; i++)
{ {
<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> var pager = i;
} if (pager == _page)
@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++)
{ {
var pager = i; <li class="page-item active">
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))> <a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
@pager </li>
</button> }
else
{
<li class="page-item">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
} }
<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) <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)
{ {
<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@((_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>
} }
@if (_endPage > 1) <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>
<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>
} <li class="page-item disabled">
@if (_endPage > 1) <a class="page-link">Page @_page of @_pages</a>
{ </li>
<span class="btn btn-link disabled">Page @_page of @_pages</span> </ul>
}
</div>
} }
@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> </div>
} }
@if (Toolbar == "Bottom") @if (Toolbar == "Bottom" && _pages > 0 && Items.Count() > _maxItems)
{ {
<div class="mx-auto text-center"> <ul class="pagination justify-content-center my-2">
@if (_endPage > 1) <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)
{ {
<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> <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>
</li>
@for (int i = _startPage; i <= _endPage; i++)
{ {
<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> var pager = i;
} if (pager == _page)
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-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++)
{ {
var pager = i; <li class="page-item active">
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))> <a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
@pager </li>
</button> }
else
{
<li class="page-item">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
} }
<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) <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)
{ {
<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@((_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>
} }
@if (_endPage > 1) <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>
<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>
} <li class="page-item disabled">
@if (_endPage > 1) <a class="page-link">Page @_page of @_pages</a>
{ </li>
<span class="btn btn-link disabled">Page @_page of @_pages</span> </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 UpdateList(int currentPage) public void SetPagerSize()
{ {
ItemList = Items.Skip((currentPage - 1) * _maxItems).Take(_maxItems); _startPage = ((_page - 1) / _displayPages) * _displayPages + 1;
_page = currentPage; _endPage = _startPage + _displayPages - 1;
if (_endPage > _pages)
{
_endPage = _pages;
}
StateHasChanged(); StateHasChanged();
} }
public void SetPagerSize(string direction) public void UpdateList(int page)
{ {
if (direction == "forward") ItemList = Items.Skip((page - 1) * _maxItems).Take(_maxItems);
{ _page = page;
if (_endPage + 1 < _pages) SetPagerSize();
{ }
_startPage = _endPage + 1;
}
else
{
_startPage = 1;
}
if (_endPage + _maxPages < _pages) public void SkipPages(string direction)
{ {
_endPage = _startPage + _maxPages - 1; switch (direction)
}
else
{
_endPage = _pages;
}
StateHasChanged();
}
else if (direction == "back")
{ {
_endPage = _startPage - 1; case "forward":
_startPage = _startPage - _maxPages; _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)
{ {
if (_page < _pages) case "next":
{ if (_page < _pages)
if (_page == _endPage)
{ {
SetPagerSize("forward"); _page += 1;
} }
_page += 1; break;
} case "previous":
} if (_page > 1)
else if (direction == "previous")
{
if (_page > 1)
{
if (_page == _startPage)
{ {
SetPagerSize("back"); _page -= 1;
} }
_page -= 1; break;
}
} }
UpdateList(_page); UpdateList(_page);

View File

@ -7,73 +7,96 @@
@if (_permissions != null) @if (_permissions != null)
{ {
<br /> <div class="container">
<table class="table table-borderless" style="width: 50%; min-width: 250px;"> <div class="row">
<tbody> <div class="col">
<tr> <table class="table table-borderless">
<th scope="col">@Localizer["Role"]</th> <tbody>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
}
</tr>
@foreach (Role role in _roles)
{
<tr>
<td>@role.Name</td>
@foreach (PermissionString permission in _permissions)
{
var p = permission;
<td style="text-align: center;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled=@GetPermissionDisabled(role.Name) OnChange="@(e => PermissionChanged(e, p.PermissionName, role.Name))" />
</td>
}
</tr>
}
</tbody>
</table>
@if (_users.Count != 0)
{
<table class="table table-borderless" style="width: 50%; min-width: 250px;">
<thead>
<tr>
<th scope="col">@Localizer["User"]</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
}
</tr>
</thead>
<tbody>
@foreach (User user in _users)
{
string userid = "[" + user.UserId.ToString() + "]";
<tr> <tr>
<td>@user.DisplayName</td> <th scope="col">@Localizer["Role"]</th>
@foreach (PermissionString permission in _permissions) @foreach (PermissionString permission in _permissions)
{ {
var p = permission; <th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
<td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
</td>
} }
</tr> </tr>
} @foreach (Role role in _roles)
</tbody> {
</table> <tr>
} <td>@role.Name</td>
<table class="table table-borderless" style="width: 50%; min-width: 250px;"> @foreach (PermissionString permission in _permissions)
<tbody> {
<tr> var p = permission;
<td class="input-group"> <td style="text-align: center;">
<input type="text" name="Username" class="form-control" placeholder="@Localizer["Username.Enter"]" @bind="@_username" /> <TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled=@GetPermissionDisabled(role.Name) OnChange="@(e => PermissionChanged(e, p.PermissionName, role.Name))" />
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button> </td>
</td> }
</tr> </tr>
</tbody> }
</table> </tbody>
<br /> </table>
<ModuleMessage Type="MessageType.Error" Message="@_message" /> <br />
</div>
</div>
<div class="row">
<div class="col">
@if (_users.Count != 0)
{
<div class="row">
<div class="col">
</div>
</div>
<table class="table table-borderless">
<thead>
<tr>
<th scope="col">@Localizer["User"]</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
}
</tr>
</thead>
<tbody>
@foreach (User user in _users)
{
string userid = "[" + user.UserId.ToString() + "]";
<tr>
<td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions)
{
var p = permission;
<td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
</td>
}
</tr>
}
</tbody>
</table>
<br />
}
</div>
</div>
<div class="row">
<div class="col">
<table class="table table-borderless">
<tbody>
<tr>
<td class="input-group">
<input type="text" name="Username" class="form-control" placeholder="@Localizer["Username.Enter"]" @bind="@_username" />
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
</td>
</tr>
</tbody>
</table>
<br />
</div>
</div>
<div class="row">
<div class="col">
<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)
@ -16,7 +16,7 @@
@code { @code {
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
}; };

View File

@ -5,46 +5,44 @@
@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> <select id="files" class="form-select" @bind="@_allowfilemanagement">
<td> <option value="true">@SharedLocalizer["Yes"]</option>
<select id="files" class="form-select" @bind="@_allowfilemanagement"> <option value="false">@SharedLocalizer["No"]</option>
<option value="true">@SharedLocalizer["Yes"]</option> </select>
<option value="false">@SharedLocalizer["No"]</option> </div>
</select> </div>
</td> </div>
</tr>
</table>
@code { @code {
private string _allowfilemanagement; private string _allowfilemanagement;
protected override void OnInitialized() protected override void OnInitialized()
{
try
{ {
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"); try
{
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
} }
catch (Exception ex)
public async Task UpdateSettings()
{ {
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); try
{
var settings = ModuleState.Settings;
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
} }
} }
public async Task UpdateSettings()
{
try
{
var settings = ModuleState.Settings;
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

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

@ -273,10 +273,43 @@
<data name="FullName" xml:space="preserve"> <data name="FullName" xml:space="preserve">
<value>Full Name:</value> <value>Full Name:</value>
</data> </data>
<data name="LocalVersion" xml:space="preserve"> <data name="LocalVersion" xml:space="preserve">
<value>Local Version</value> <value>Local Version</value>
</data> </data>
<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

@ -1,65 +1,65 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
Version 2.0 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
The primary goals of this format is to allow a simple XML format : using a System.ComponentModel.TypeConverter
that is mostly human readable. The generation and parsing of the : and then encoded with base64 encoding.
various data types are done through the TypeConverter classes -->
associated with the data types. <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: 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: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,21 +109,34 @@ namespace Oqtane.Services
foreach (KeyValuePair<string, string> kvp in settings) foreach (KeyValuePair<string, string> kvp in settings)
{ {
Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase)); 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));
if (setting == null) if (setting == null)
{ {
setting = new Setting(); setting = new Setting();
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,13 +176,19 @@ 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;
} }
else else
@ -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,182 +14,177 @@
@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"> </div>
<button type="button" class="btn-close" aria-label="Close" @onclick="HideControlPanel"></button> <div class="@BodyClass">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<div class="row d-flex">
<div class="col">
<button data-bs-dismiss="offcanvas" type="button" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
</div>
</div> </div>
</div>
<div class="@BodyClass">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) <hr class="app-rule" />
{
<div class="row d-flex">
<div class="col">
<button type="button" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
</div>
</div>
<hr class="app-rule" /> <div class="row">
<div class="col text-center">
<label class="control-label">@Localizer["Page.Manage"] </label>
</div>
</div>
<div class="row d-flex">
<div class="col d-flex justify-content-between">
<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" 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>
</div>
</div>
<br />
<div class="row d-flex">
<div class="col">
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
}
else
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("publish"))>@Localizer["Page.Publish"]</button>
}
</div>
</div>
}
<div class="row"> @if (_deleteConfirmation)
<div class="col text-center"> {
<label class="control-label">@Localizer["Page.Manage"] </label> <div class="app-admin-modal">
</div> <div class="modal" tabindex="-1" role="dialog">
</div> <div class="modal-dialog">
<div class="row d-flex"> <div class="modal-content">
<div class="col d-flex justify-content-between"> <div class="modal-header">
<button type="button" class="btn btn-secondary col-3" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button> <h5 class="modal-title">@Localizer["Page.Delete"]</h5>
<button type="button" class="btn btn-secondary col-3" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button> <button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
<button type="button" class="btn btn-danger col-3" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button> </div>
</div> <div class="modal-body">
</div> <p>Are You Sure You Want To Delete This Page?</p>
<br /> </div>
<div class="row d-flex"> <div class="modal-footer">
<div class="col"> <button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone)) <button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
}
else
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("publish"))>@Localizer["Page.Publish"]</button>
}
</div>
</div>
}
@if (_deleteConfirmation)
{
<div class="app-admin-modal">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
</div>
<div class="modal-body">
<p>Are You Sure You Want To Delete This Page?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
} </div>
<hr class="app-rule" /> }
<div class="row"> <hr class="app-rule" />
<div class="col text-center"> <div class="row">
<label for="Module" class="control-label">@Localizer["Module.Manage"] </label> <div class="col text-center">
<select class="form-select" @bind="@ModuleType"> <label for="Module" class="control-label">@Localizer["Module.Manage"] </label>
<option value="new">@Localizer["Module.AddNew"]</option> <select class="form-select" @bind="@ModuleType">
<option value="existing">@Localizer["Module.AddExisting"]</option> <option value="new">@Localizer["Module.AddNew"]</option>
</select> <option value="existing">@Localizer["Module.AddExisting"]</option>
@if (ModuleType == "new") </select>
@if (ModuleType == "new")
{
@if (_moduleDefinitions != null)
{ {
@if (_moduleDefinitions != null) <select class="form-select" @onchange="(e => CategoryChanged(e))">
{ @foreach (var category in _categories)
<select class="form-select" @onchange="(e => CategoryChanged(e))"> {
@foreach (var category in _categories) if (category == Category)
{ {
if (category == Category) <option value="@category" selected>@category @Localizer["Modules"]</option>
{
<option value="@category" selected>@category @Localizer["Modules"]</option>
}
else
{
<option value="@category">@category @Localizer["Modules"]</option>
}
}
</select>
<select class="form-select" @onchange="(e => ModuleChanged(e))">
@if (ModuleDefinitionName == "-")
{
<option value="-" selected>&lt;@Localizer["Module.Select"]&gt;</option>
} }
else else
{ {
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option> <option value="@category">@category @Localizer["Modules"]</option>
} }
@foreach (var moduledefinition in _moduleDefinitions) }
</select>
<select class="form-select" @onchange="(e => ModuleChanged(e))">
@if (ModuleDefinitionName == "-")
{
<option value="-" selected>&lt;@Localizer["Module.Select"]&gt;</option>
}
else
{
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
}
@foreach (var moduledefinition in _moduleDefinitions)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions)) if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{ {
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString())) <option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
} }
} }
</select> }
@((MarkupString) Description) </select>
} @((MarkupString) Description)
} }
else }
{ else
<select class="form-select" @onchange="(e => PageChanged(e))"> {
<option value="-">&lt;@Localizer["Page.Select"]&gt;</option> <select class="form-select" @onchange="(e => PageChanged(e))">
@foreach (Page p in _pages) <option value="-">&lt;@Localizer["Page.Select"]&gt;</option>
{ @foreach (Page p in _pages)
<option value="@p.PageId">@p.Name</option>
}
</select>
<select class="form-select" @bind="@ModuleId">
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
@foreach (Module module in _modules)
{
<option value="@module.ModuleId">@module.Title</option>
}
</select>
}
</div>
</div>
<div class="row">
<div class="col text-center">
<label for="Title" class="control-label">@Localizer["Title"] </label>
<input type="text" name="Title" class="form-control" @bind="@Title" />
</div>
</div>
@if (_pane.Length > 1)
{
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">@Localizer["Pane"] </label>
<select class="form-select" @bind="@Pane">
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
}
</select>
</div>
</div>
}
<div class="row">
<div class="col text-center">
<label for="Container" class="control-label">@Localizer["Container"] </label>
<select class="form-select" @bind="@ContainerType">
@foreach (var container in _containers)
{ {
<option value="@container.TypeName">@container.Name</option> <option value="@p.PageId">@p.Name</option>
}
</select>
<select class="form-select" @bind="@ModuleId">
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
@foreach (Module module in _modules)
{
<option value="@module.ModuleId">@module.Title</option>
}
</select>
}
</div>
</div>
<div class="row">
<div class="col text-center">
<label for="Title" class="control-label">@Localizer["Title"] </label>
<input type="text" name="Title" class="form-control" @bind="@Title" />
</div>
</div>
@if (_pane.Length > 1)
{
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">@Localizer["Pane"] </label>
<select class="form-select" @bind="@Pane">
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
} }
</select> </select>
</div> </div>
</div> </div>
<br /> }
<button type="button" class="btn btn-primary col-12" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button> <div class="row">
@((MarkupString) Message) <div class="col text-center">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) <label for="Container" class="control-label">@Localizer["Container"] </label>
{ <select class="form-select" @bind="@ContainerType">
<hr class="app-rule" /> @foreach (var container in _containers)
<NavLink class="btn btn-info col-12" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink> {
} <option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div> </div>
<br />
<button type="button" class="btn btn-primary col-12" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
@((MarkupString) Message)
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<hr class="app-rule" />
<NavLink class="btn btn-info col-12" data-bs-dismiss="offcanvas" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink>
}
</div> </div>
</div> </div>
} }
@ -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)
{ {

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