Compare commits
134 Commits
Author | SHA1 | Date | |
---|---|---|---|
7b4d13b73e | |||
2909aa1656 | |||
64131b6764 | |||
0b78d75a21 | |||
b35c342960 | |||
24c858d379 | |||
b8a31a8be9 | |||
02c30d6454 | |||
985f003e6d | |||
98045e1e2e | |||
5762ce58a4 | |||
2787ee71fc | |||
e61a6df4d7 | |||
0a5c2ecbf5 | |||
6bfab696ad | |||
928f2dd496 | |||
bcf75892f7 | |||
594761385f | |||
d05fba06ec | |||
25155b1b38 | |||
ded6c9c199 | |||
c62e6c0045 | |||
b3feda9fd1 | |||
7ef8e2c8b8 | |||
d5ff211871 | |||
51c23e3842 | |||
557b30815e | |||
145459bfc3 | |||
f97a6a2bee | |||
1134422891 | |||
6012275c7b | |||
2f07063375 | |||
310d1ed485 | |||
a48edbb16e | |||
d6258409fc | |||
1b94b1247b | |||
9ef63ae60e | |||
f99de4be48 | |||
80fd1820c2 | |||
0a4a983d20 | |||
c2cc830691 | |||
4d0490d1c6 | |||
02d1838547 | |||
5c6edff778 | |||
7cbca32ddd | |||
8950e315f8 | |||
a703df40c0 | |||
225fce8810 | |||
bdc0f0fcdd | |||
5bbb8c4858 | |||
35b9551bfb | |||
c8c5a05b39 | |||
2771f0301a | |||
c4f04edc59 | |||
5530422846 | |||
ce77c81fb5 | |||
bc488d4ac2 | |||
fe72a10346 | |||
0da88398b4 | |||
c42de3da20 | |||
4bf9f36baa | |||
380cb192c7 | |||
8882e19ec5 | |||
7f52059b98 | |||
1ce3cc4d7c | |||
657c71e94d | |||
c8cfb3c7b7 | |||
6e7e90acf4 | |||
6d3a556d34 | |||
7d9188b659 | |||
4f0a805c79 | |||
53f3320492 | |||
f9ce51b4a5 | |||
f8bf432c0d | |||
a822482172 | |||
b22f8a0b02 | |||
227331bf24 | |||
744688cbe1 | |||
79c8126c4a | |||
0b7c8e4ef7 | |||
35e00f61d8 | |||
aba3d58df8 | |||
45984a8166 | |||
ea5655ae42 | |||
f06cb0dfbb | |||
1abae55976 | |||
a83ed40ec4 | |||
75ecae4672 | |||
16a6f942c5 | |||
aa98508e57 | |||
583383aee1 | |||
13f69f81d7 | |||
43c34fcd64 | |||
c272238539 | |||
6e0d2706a8 | |||
25a7289ce8 | |||
1ba7d045e4 | |||
a4d75befe7 | |||
88377529bc | |||
91b9a0280f | |||
25173ae85c | |||
ad3350705e | |||
a16bc5db28 | |||
51657338f5 | |||
0fe3ea25af | |||
806daaf7c9 | |||
21ff4a83b5 | |||
ecc9aa40d7 | |||
c34ca2a59b | |||
dde7094fe3 | |||
105afdfefc | |||
4c254a8686 | |||
33ca203e57 | |||
2ff4133cd4 | |||
49ad85713e | |||
1978bf151f | |||
506378de82 | |||
53ead7a03f | |||
ab979fd63c | |||
1e84a2238b | |||
5618adf86c | |||
345b0bc95f | |||
b1d6c35e99 | |||
d767f1a101 | |||
c15f2b9a12 | |||
a21a53662b | |||
ebb5340019 | |||
6108bd214e | |||
eed27e101a | |||
2767680bed | |||
4080e30b6f | |||
e89257be62 | |||
d3c40a7e8b | |||
60657d5d25 |
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.Localization
|
||||
{
|
||||
public static class OqtaneLocalizationExtensions
|
||||
@ -18,5 +20,42 @@ namespace Microsoft.Extensions.Localization
|
||||
}
|
||||
return localizedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an IStringLocalizer based on a type name. This extension method is useful in scenarios where the default IStringLocalizer is unable to locate the resources.
|
||||
/// </summary>
|
||||
/// <param name="localizerFactory"></param>
|
||||
/// <param name="fullTypeName">the full type name ie. GetType().FullName</param>
|
||||
/// <returns></returns>
|
||||
public static IStringLocalizer Create(this IStringLocalizerFactory localizerFactory, string fullTypeName)
|
||||
{
|
||||
var typename = fullTypeName;
|
||||
|
||||
// handle generic types
|
||||
var type = Type.GetType(fullTypeName);
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
typename = type.GetGenericTypeDefinition().FullName;
|
||||
typename = typename.Substring(0, typename.IndexOf("`")); // remove generic type info
|
||||
}
|
||||
|
||||
// format typename
|
||||
if (typename.Contains(","))
|
||||
{
|
||||
typename = typename.Substring(0, typename.IndexOf(",")); // remove assembly info
|
||||
}
|
||||
|
||||
// remove rootnamespace
|
||||
var rootnamespace = "";
|
||||
var attributes = type.Assembly.GetCustomAttributes(typeof(RootNamespaceAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
rootnamespace = ((RootNamespaceAttribute)attributes[0]).RootNamespace;
|
||||
}
|
||||
typename = typename.Replace(rootnamespace + ".", "");
|
||||
|
||||
// create IStringLocalizer using factory
|
||||
return localizerFactory.Create(typename, type.Assembly.GetName().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
|
||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) " ResourceKey="Server">Server:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||
</div>
|
||||
@ -51,7 +51,7 @@
|
||||
@if (_encryption == "true")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption" ResourceKey="TrustServerCertificate">Certificate:</Label>
|
||||
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True." ResourceKey="TrustServerCertificate">Certificate:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
||||
<option value="true">@Localizer["Self Signed"]</option>
|
||||
|
@ -26,29 +26,42 @@
|
||||
<div class="col-sm-9">
|
||||
@if (_databases != null)
|
||||
{
|
||||
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
|
||||
@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 class="input-group">
|
||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||
@foreach (var database in _databases)
|
||||
{
|
||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||
}
|
||||
</select>
|
||||
@if (!_showConnectionString)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@{
|
||||
if (_databaseConfigType != null)
|
||||
{
|
||||
@DatabaseConfigComponent;
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if (!_showConnectionString)
|
||||
{
|
||||
if (_databaseConfigType != null)
|
||||
{
|
||||
@DatabaseConfigComponent
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">String:</Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
||||
@ -63,8 +76,8 @@
|
||||
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||
<input id="password" type="@_passwordType" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglePassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,8 +85,8 @@
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword">@_togglepassword</button>
|
||||
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword">@_toggleConfirmPassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -107,11 +120,15 @@
|
||||
private Type _databaseConfigType;
|
||||
private object _databaseConfig;
|
||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||
private bool _showConnectionString = false;
|
||||
private string _connectionString = string.Empty;
|
||||
|
||||
private string _hostUsername = string.Empty;
|
||||
private string _hostPassword = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string _passwordType = "password";
|
||||
private string _confirmPasswordType = "password";
|
||||
private string _togglePassword = string.Empty;
|
||||
private string _toggleConfirmPassword = string.Empty;
|
||||
private string _confirmPassword = string.Empty;
|
||||
private string _hostEmail = string.Empty;
|
||||
private bool _register = true;
|
||||
@ -120,7 +137,9 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
{
|
||||
@ -138,7 +157,7 @@
|
||||
try
|
||||
{
|
||||
_databaseName = (string)eventArgs.Value;
|
||||
|
||||
_showConnectionString = false;
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
catch
|
||||
@ -175,9 +194,16 @@
|
||||
private async Task Install()
|
||||
{
|
||||
var connectionString = String.Empty;
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
if (_showConnectionString)
|
||||
{
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
connectionString = _connectionString;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
||||
@ -221,24 +247,48 @@
|
||||
{
|
||||
_message = Localizer["Message.Password.Invalid"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.DbInfo"];
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordtype == "password")
|
||||
{
|
||||
_passwordtype = "text";
|
||||
_togglepassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordtype = "password";
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
_message = Localizer["Message.Require.DbInfo"];
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordType == "password")
|
||||
{
|
||||
_passwordType = "text";
|
||||
_togglePassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordType = "password";
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleConfirmPassword()
|
||||
{
|
||||
if (_confirmPasswordType == "password")
|
||||
{
|
||||
_confirmPasswordType = "text";
|
||||
_toggleConfirmPassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_confirmPasswordType = "password";
|
||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleConnectionString()
|
||||
{
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
_connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
_showConnectionString = !_showConnectionString;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,8 +11,7 @@
|
||||
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
string message = string.Format(Localizer["Error.Module.Load"], module.ModuleDefinitionName);
|
||||
AddModuleMessage(message, MessageType.Error);
|
||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], module.ModuleDefinitionName), MessageType.Error);
|
||||
}
|
||||
|
||||
await logger.LogCritical("Error Loading Module {Module}", module);
|
||||
|
@ -56,6 +56,10 @@ else
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
if (_jobs.Count == 0)
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
||||
|
@ -20,6 +20,7 @@ else
|
||||
@if (_availableCultures.Count() == 0)
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -31,7 +32,7 @@ else
|
||||
<select id="_code" class="form-select" @bind="@_code" required>
|
||||
@foreach (var culture in _availableCultures)
|
||||
{
|
||||
<option value="@culture.Name">@culture.DisplayName</option>
|
||||
<option value="@culture.Name">@(culture.DisplayName + " (" + culture.Name + ")")</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@ -47,11 +48,11 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</form>
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
||||
<TabPanel Name="Translations" Heading="Translations" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
||||
<div class="row justify-content-center mb-3">
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
@ -78,6 +79,7 @@ else
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
@ -113,7 +115,7 @@ else
|
||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Language: </Label>
|
||||
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Translation: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
|
||||
</div>
|
||||
@ -159,33 +161,32 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
|
||||
private string _code = string.Empty;
|
||||
private string _isDefault = "False";
|
||||
private string _message;
|
||||
private IEnumerable<Culture> _supportedCultures;
|
||||
private IEnumerable<Culture> _availableCultures;
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
private string _search = "";
|
||||
private string _productname = "";
|
||||
private string _license = "";
|
||||
private string _packageid = "";
|
||||
private string _version = "";
|
||||
private string _code = string.Empty;
|
||||
private string _isDefault = "False";
|
||||
private string _message;
|
||||
private IEnumerable<Culture> _supportedCultures;
|
||||
private IEnumerable<Culture> _availableCultures;
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
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;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||
var languagesCodes = languages.Select(l => l.Code).ToList();
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||
var languagesCodes = languages.Select(l => l.Code).ToList();
|
||||
|
||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
||||
_availableCultures = _supportedCultures
|
||||
.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
||||
await LoadTranslations();
|
||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
||||
_availableCultures = _supportedCultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
||||
await LoadTranslations();
|
||||
|
||||
if (_supportedCultures.Count() == 1)
|
||||
{
|
||||
|
@ -19,15 +19,17 @@ else
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["Code"]</th>
|
||||
<th>@Localizer["Translation"]</th>
|
||||
<th>@Localizer["Default"]</th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Code</td>
|
||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||
<td>
|
||||
<td>@context.Version</td>
|
||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||
<td>
|
||||
@if (UpgradeAvailable(context.Code))
|
||||
{
|
||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||
@ -50,9 +52,6 @@ else
|
||||
var cultures = await LocalizationService.GetCulturesAsync();
|
||||
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
||||
|
||||
// Adds English as default language
|
||||
_languages.Insert(0, new Language { Name = culture.DisplayName, Code = culture.Name, IsDefault = !_languages.Any(l => l.IsDefault) });
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_packages = await PackageService.GetPackagesAsync("translation");
|
||||
@ -81,7 +80,7 @@ else
|
||||
var upgradeavailable = false;
|
||||
if (_packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
||||
var package = _packages.Where(item => item.PackageId == ("Oqtane.Client." + code)).FirstOrDefault();
|
||||
if (package != null)
|
||||
{
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
||||
|
@ -184,7 +184,7 @@
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(login))
|
||||
{
|
||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password};
|
||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||
|
||||
if (!twofactor)
|
||||
{
|
||||
@ -206,7 +206,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.Site.Settings["LoginOptions:TwoFactor"] == "required" || user.TwoFactorRequired)
|
||||
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||
{
|
||||
twofactor = true;
|
||||
validated = false;
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Admin.Logs
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ILogService LogService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@ -91,19 +92,23 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _level = "-";
|
||||
private string _function = "-";
|
||||
private string _rows = "10";
|
||||
private string _level = "-";
|
||||
private string _function = "-";
|
||||
private string _rows = "10";
|
||||
private int _page = 1;
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}"));
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("level"))
|
||||
{
|
||||
_level = PageState.QueryString["level"];
|
||||
|
@ -76,64 +76,71 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _moduledefinitionname = string.Empty;
|
||||
private string _owner = string.Empty;
|
||||
private string _module = string.Empty;
|
||||
private string _description = string.Empty;
|
||||
private List<Template> _templates;
|
||||
private string _template = "-";
|
||||
private string[] _versions;
|
||||
private string _reference = Constants.Version;
|
||||
private string _minversion = "2.0.0";
|
||||
private string _location = string.Empty;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _moduledefinitionname = string.Empty;
|
||||
private string _owner = string.Empty;
|
||||
private string _module = string.Empty;
|
||||
private string _description = string.Empty;
|
||||
private List<Template> _templates;
|
||||
private string _template = "-";
|
||||
private string[] _versions;
|
||||
private string _reference = "local";
|
||||
private string _minversion = "2.0.0";
|
||||
private string _location = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
|
||||
if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
|
||||
}
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
|
||||
if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
|
||||
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Module Creator");
|
||||
}
|
||||
}
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
|
||||
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Module Creator");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateModule()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
try
|
||||
{
|
||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||
private async Task CreateModule()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
|
||||
{
|
||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
|
||||
GetLocation();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
GetLocation();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -35,6 +35,7 @@
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
@ -90,9 +91,9 @@
|
||||
<div class="modal-body">
|
||||
<p style="height: 200px; overflow-y: scroll;">
|
||||
<h3>@_productname</h3>
|
||||
@if (!string.IsNullOrEmpty(_license))
|
||||
@if (!string.IsNullOrEmpty(_packagelicense))
|
||||
{
|
||||
@((MarkupString)_license)
|
||||
@((MarkupString)_packagelicense)
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -114,145 +115,145 @@
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
private string _search = "";
|
||||
private string _productname = "";
|
||||
private string _license = "";
|
||||
private string _packageid = "";
|
||||
private string _version = "";
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
private string _search = "";
|
||||
private string _productname = "";
|
||||
private string _packageid = "";
|
||||
private string _packagelicense = "";
|
||||
private string _packageversion = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Package.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Package.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadModuleDefinitions()
|
||||
{
|
||||
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
|
||||
private async Task LoadModuleDefinitions()
|
||||
{
|
||||
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
|
||||
|
||||
if (_packages != null)
|
||||
{
|
||||
foreach (Package package in _packages.ToArray())
|
||||
{
|
||||
if (moduledefinitions.Exists(item => item.PackageName == package.PackageId))
|
||||
{
|
||||
_packages.Remove(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_packages != null)
|
||||
{
|
||||
foreach (Package package in _packages.ToArray())
|
||||
{
|
||||
if (moduledefinitions.Exists(item => item.PackageName == package.PackageId))
|
||||
{
|
||||
_packages.Remove(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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()
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error On Search");
|
||||
}
|
||||
}
|
||||
private async Task Search()
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error On Search");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
_search = "";
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error On Reset");
|
||||
}
|
||||
}
|
||||
private async Task Reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
_search = "";
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error On Reset");
|
||||
}
|
||||
}
|
||||
|
||||
private void HideModal()
|
||||
{
|
||||
_productname = "";
|
||||
_license = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
private void HideModal()
|
||||
{
|
||||
_productname = "";
|
||||
_packagelicense = "";
|
||||
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 GetPackage(string packageid, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
var package = await PackageService.GetPackageAsync(packageid, version);
|
||||
if (package != null)
|
||||
{
|
||||
_productname = package.Name;
|
||||
_packageid = package.PackageId;
|
||||
if (!string.IsNullOrEmpty(package.License))
|
||||
{
|
||||
_packagelicense = package.License.Replace("\n", "<br />");
|
||||
}
|
||||
_packageversion = 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, Constants.PackagesFolder);
|
||||
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 DownloadPackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(_packageid, _packageversion, Constants.PackagesFolder);
|
||||
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _packageversion);
|
||||
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
|
||||
_productname = "";
|
||||
_packagelicense = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _packageversion);
|
||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InstallModules()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Installing Module");
|
||||
}
|
||||
}
|
||||
private async Task InstallModules()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Installing Modules");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +151,7 @@
|
||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||
_minversion = template.Version;
|
||||
}
|
||||
GetLocation();
|
||||
GetLocation();
|
||||
}
|
||||
|
||||
private void GetLocation()
|
||||
|
@ -1,6 +1,7 @@
|
||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||
@inherits ModuleBase
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IPackageService PackageService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@ -43,7 +44,13 @@
|
||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed" ResourceKey="PackageName">Package Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||
@ -74,7 +81,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
</Section>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@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>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
||||
<div class="container">
|
||||
@ -82,24 +95,100 @@
|
||||
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Translations" ResourceKey="Translations">
|
||||
@if (_packages != null)
|
||||
{
|
||||
if (_packages.Count > 0)
|
||||
{
|
||||
<Pager Items="@_packages">
|
||||
<Row>
|
||||
<td>
|
||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3> by: <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
@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>
|
||||
</Row>
|
||||
</Pager>
|
||||
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<br />
|
||||
<div class="mx-auto text-center">
|
||||
@Localizer["Search.NoResults"]
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@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>
|
||||
|
||||
@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(_packagelicense))
|
||||
{
|
||||
@((MarkupString)_packagelicense)
|
||||
}
|
||||
else
|
||||
{
|
||||
@SharedLocalizer["License Not Specified"]
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" @onclick="DownloadTranslation">@SharedLocalizer["Accept"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _moduleDefinitionId;
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _categories;
|
||||
private string _description = "";
|
||||
private string _categories;
|
||||
private string _moduledefinitionname = "";
|
||||
private string _description = "";
|
||||
private string _owner = "";
|
||||
private string _version;
|
||||
private string _packagename = "";
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
@ -114,6 +203,12 @@
|
||||
private PermissionGrid _permissionGrid;
|
||||
#pragma warning restore 649
|
||||
|
||||
private List<Package> _packages;
|
||||
private string _productname = "";
|
||||
private string _packageid = "";
|
||||
private string _packagelicense = "";
|
||||
private string _packageversion = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@ -125,11 +220,12 @@
|
||||
if (moduleDefinition != null)
|
||||
{
|
||||
_name = moduleDefinition.Name;
|
||||
_version = moduleDefinition.Version;
|
||||
_description = moduleDefinition.Description;
|
||||
_categories = moduleDefinition.Categories;
|
||||
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
||||
_description = moduleDefinition.Description;
|
||||
_owner = moduleDefinition.Owner;
|
||||
_version = moduleDefinition.Version;
|
||||
_packagename = moduleDefinition.PackageName;
|
||||
_owner = moduleDefinition.Owner;
|
||||
_url = moduleDefinition.Url;
|
||||
_contact = moduleDefinition.Contact;
|
||||
_license = moduleDefinition.License;
|
||||
@ -139,7 +235,9 @@
|
||||
_createdon = moduleDefinition.CreatedOn;
|
||||
_modifiedby = moduleDefinition.ModifiedBy;
|
||||
_modifiedon = moduleDefinition.ModifiedOn;
|
||||
}
|
||||
|
||||
_packages = await PackageService.GetPackagesAsync("translation", "", "", moduleDefinition.PackageName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -185,4 +283,66 @@
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void HideModal()
|
||||
{
|
||||
_productname = "";
|
||||
_packagelicense = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task GetPackage(string packageid, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
var package = await PackageService.GetPackageAsync(packageid, version);
|
||||
if (package != null)
|
||||
{
|
||||
_productname = package.Name;
|
||||
_packageid = package.PackageId;
|
||||
if (!string.IsNullOrEmpty(package.License))
|
||||
{
|
||||
_packagelicense = package.License.Replace("\n", "<br />");
|
||||
}
|
||||
_packageversion = package.Version;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
|
||||
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadTranslation()
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
|
||||
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
|
||||
AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success);
|
||||
_productname = "";
|
||||
_packagelicense = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version);
|
||||
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InstallTranslations()
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.InstallPackagesAsync();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Installing Translations");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,32 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||
@((MarkupString)" ")
|
||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
||||
|
||||
<Pager Items="@_moduleDefinitions">
|
||||
<div class="container">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-sm-6">
|
||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||
@((MarkupString)" ")
|
||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == _category)
|
||||
{
|
||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@category">@category @Localizer["Modules"]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Pager Items="@_moduleDefinitions.Where(item => item.Categories.Contains(_category))">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -30,9 +51,9 @@ else
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
||||
<td>
|
||||
@if (context.AssemblyName != "Oqtane.Client")
|
||||
{
|
||||
{
|
||||
<ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete", context.Name])" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Version</td>
|
||||
@ -65,6 +86,8 @@ else
|
||||
@code {
|
||||
private List<ModuleDefinition> _moduleDefinitions;
|
||||
private List<Package> _packages;
|
||||
private List<string> _categories = new List<string>();
|
||||
private string _category = "Common";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -74,6 +97,7 @@ else
|
||||
{
|
||||
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_packages = await PackageService.GetPackagesAsync("module");
|
||||
_categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -115,38 +139,44 @@ else
|
||||
{
|
||||
return package.Version;
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private async Task DownloadModule(string packagename, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
||||
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
||||
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async Task DownloadModule(string packagename, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
||||
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
||||
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void CategoryChanged(ChangeEventArgs e)
|
||||
{
|
||||
_category = (string)e.Value;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
@ -90,116 +90,125 @@
|
||||
</form>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Module Settings";
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Module Settings";
|
||||
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private List<Theme> _themes;
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _title;
|
||||
private string _containerType;
|
||||
private string _allPages = "false";
|
||||
private string _permissionNames = "";
|
||||
private string _permissions = null;
|
||||
private string _pageId;
|
||||
private PermissionGrid _permissionGrid;
|
||||
private Type _moduleSettingsType;
|
||||
private object _moduleSettings;
|
||||
private string _moduleSettingsTitle = "Module Settings";
|
||||
private RenderFragment ModuleSettingsComponent { get; set; }
|
||||
private Type _containerSettingsType;
|
||||
private object _containerSettings;
|
||||
private RenderFragment ContainerSettingsComponent { get; set; }
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private List<Theme> _themes;
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _title;
|
||||
private string _containerType;
|
||||
private string _allPages = "false";
|
||||
private string _permissionNames = "";
|
||||
private string _permissions = null;
|
||||
private string _pageId;
|
||||
private PermissionGrid _permissionGrid;
|
||||
private Type _moduleSettingsType;
|
||||
private object _moduleSettings;
|
||||
private string _moduleSettingsTitle = "Module Settings";
|
||||
private RenderFragment ModuleSettingsComponent { get; set; }
|
||||
private Type _containerSettingsType;
|
||||
private object _containerSettings;
|
||||
private RenderFragment ContainerSettingsComponent { get; set; }
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_title = ModuleState.Title;
|
||||
_themes = await ThemeService.GetThemesAsync();
|
||||
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
|
||||
_containerType = ModuleState.ContainerType;
|
||||
_allPages = ModuleState.AllPages.ToString();
|
||||
_permissions = ModuleState.Permissions;
|
||||
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
|
||||
_pageId = ModuleState.PageId.ToString();
|
||||
createdby = ModuleState.CreatedBy;
|
||||
createdon = ModuleState.CreatedOn;
|
||||
modifiedby = ModuleState.ModifiedBy;
|
||||
modifiedon = ModuleState.ModifiedOn;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_title = ModuleState.Title;
|
||||
_themes = await ThemeService.GetThemesAsync();
|
||||
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
|
||||
_containerType = ModuleState.ContainerType;
|
||||
_allPages = ModuleState.AllPages.ToString();
|
||||
_permissions = ModuleState.Permissions;
|
||||
_pageId = ModuleState.PageId.ToString();
|
||||
createdby = ModuleState.CreatedBy;
|
||||
createdon = ModuleState.CreatedOn;
|
||||
modifiedby = ModuleState.ModifiedBy;
|
||||
modifiedon = ModuleState.ModifiedOn;
|
||||
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||
{
|
||||
// module settings type explicitly declared in IModule interface
|
||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||
}
|
||||
if (_moduleSettingsType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
||||
if (!string.IsNullOrEmpty(moduleobject.Title))
|
||||
{
|
||||
_moduleSettingsTitle = moduleobject.Title;
|
||||
}
|
||||
if (ModuleState.ModuleDefinition != null)
|
||||
{
|
||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||
|
||||
ModuleSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _moduleSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||
{
|
||||
// module settings type explicitly declared in IModule interface
|
||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||
}
|
||||
if (_moduleSettingsType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
||||
if (!string.IsNullOrEmpty(moduleobject.Title))
|
||||
{
|
||||
_moduleSettingsTitle = moduleobject.Title;
|
||||
}
|
||||
|
||||
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||
{
|
||||
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
|
||||
if (_containerSettingsType != null)
|
||||
{
|
||||
ContainerSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _containerSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
ModuleSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _moduleSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||
}
|
||||
|
||||
private async Task SaveModule()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_title))
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
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;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
pagemodule.ContainerType = string.Empty;
|
||||
}
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||
{
|
||||
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
|
||||
if (_containerSettingsType != null)
|
||||
{
|
||||
ContainerSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _containerSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var module = ModuleState;
|
||||
module.AllPages = bool.Parse(_allPages);
|
||||
module.Permissions = _permissionGrid.GetPermissions();
|
||||
await ModuleService.UpdateModuleAsync(module);
|
||||
private async Task SaveModule()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_title))
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
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;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
pagemodule.ContainerType = string.Empty;
|
||||
}
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||
|
||||
var module = ModuleState;
|
||||
module.AllPages = bool.Parse(_allPages);
|
||||
module.PageModuleId = ModuleState.PageModuleId;
|
||||
module.Permissions = _permissionGrid.GetPermissions();
|
||||
await ModuleService.UpdateModuleAsync(module);
|
||||
|
||||
if (_moduleSettingsType != null)
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ else
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||
_roles = _roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
||||
_roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -170,24 +170,54 @@
|
||||
@if (_aliases != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="aliases" HelpText="The list of aliases for this site" ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||
<Pager Items="@_aliases">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["AliasName"]</th>
|
||||
<th>@Localizer["AliasDefault"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
@if (context.AliasId != _aliasid)
|
||||
{
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.IsDefault</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
|
||||
<td>
|
||||
<input id="aliasname" class="form-control" @bind="@_aliasname" />
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultaias" class="form-select" @bind="@_defaultalias" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
}
|
||||
</Row>
|
||||
</Pager>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="defaultalias" HelpText="The default alias for the site. Requests for non-default aliases will be redirected to the default alias." ResourceKey="DefaultAlias">Default Alias: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
|
||||
@foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
<option value="@name">@name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||
@ -253,8 +283,9 @@
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _name = string.Empty;
|
||||
private List<Alias> _aliases;
|
||||
private string _defaultalias = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private int _aliasid = -1;
|
||||
private string _aliasname;
|
||||
private string _defaultalias;
|
||||
private string _runtime = "";
|
||||
private string _prerender = "";
|
||||
private int _logofileid = -1;
|
||||
@ -304,10 +335,7 @@
|
||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||
_isdeleted = site.IsDeleted.ToString();
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
await GetAliases();
|
||||
}
|
||||
await GetAliases();
|
||||
|
||||
if (site.LogoFileId != null)
|
||||
{
|
||||
@ -409,152 +437,92 @@
|
||||
{
|
||||
if (_name != string.Empty && _themetype != "-" && _containertype != "-")
|
||||
{
|
||||
var unique = true;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||
if (site != null)
|
||||
{
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); // convert line breaks to commas
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
bool refresh = false;
|
||||
bool reload = false;
|
||||
|
||||
site.Name = _name;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
var alias = aliases.Where(item => item.Name == name).FirstOrDefault();
|
||||
if (alias != null && unique)
|
||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
||||
{
|
||||
unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId);
|
||||
site.Runtime = _runtime;
|
||||
site.RenderMode = _runtime + _prerender;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
}
|
||||
if (unique && string.IsNullOrEmpty(_defaultalias)) unique = false;
|
||||
}
|
||||
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||
|
||||
if (unique)
|
||||
{
|
||||
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||
if (site != null)
|
||||
site.LogoFileId = null;
|
||||
var logofileid = _logofilemanager.GetFileId();
|
||||
if (logofileid != -1)
|
||||
{
|
||||
bool refresh = false;
|
||||
bool reload = false;
|
||||
|
||||
site.Name = _name;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
||||
{
|
||||
site.Runtime = _runtime;
|
||||
site.RenderMode = _runtime + _prerender;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
}
|
||||
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||
|
||||
site.LogoFileId = null;
|
||||
var logofileid = _logofilemanager.GetFileId();
|
||||
if (logofileid != -1)
|
||||
{
|
||||
site.LogoFileId = logofileid;
|
||||
}
|
||||
int? faviconFieldId = _faviconfilemanager.GetFileId();
|
||||
if (faviconFieldId == -1) faviconFieldId = null;
|
||||
if (site.FaviconFileId != faviconFieldId)
|
||||
{
|
||||
site.FaviconFileId = faviconFieldId;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
if (site.DefaultThemeType != _themetype)
|
||||
{
|
||||
site.DefaultThemeType = _themetype;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
if (site.DefaultContainerType != _containertype)
|
||||
{
|
||||
site.DefaultContainerType = _containertype;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
site.AdminContainerType = _admincontainertype;
|
||||
|
||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||
{
|
||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||
{
|
||||
site.PwaAppIconFileId = pwaappiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||
{
|
||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
|
||||
site = await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(sValue => sValue.Trim()).ToArray();
|
||||
foreach (Alias alias in _aliases)
|
||||
{
|
||||
if (!names.Contains(alias.Name.Trim()))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string name in names)
|
||||
{
|
||||
var alias = _aliases.Find(item => item.Name.Trim() == name);
|
||||
if (alias == null)
|
||||
{
|
||||
alias = new Alias();
|
||||
alias.Name = name;
|
||||
alias.TenantId = site.TenantId;
|
||||
alias.SiteId = site.SiteId;
|
||||
alias.IsDefault = (name == _defaultalias);
|
||||
await AliasService.AddAliasAsync(alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alias.Name != name || alias.IsDefault != (alias.Name.Trim() == _defaultalias))
|
||||
{
|
||||
alias.Name = name;
|
||||
alias.IsDefault = (name == _defaultalias);
|
||||
await AliasService.UpdateAliasAsync(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
await GetAliases();
|
||||
}
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
if (refresh || reload)
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
site.LogoFileId = logofileid;
|
||||
}
|
||||
int? faviconFieldId = _faviconfilemanager.GetFileId();
|
||||
if (faviconFieldId == -1) faviconFieldId = null;
|
||||
if (site.FaviconFileId != faviconFieldId)
|
||||
{
|
||||
site.FaviconFileId = faviconFieldId;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
if (site.DefaultThemeType != _themetype)
|
||||
{
|
||||
site.DefaultThemeType = _themetype;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
if (site.DefaultContainerType != _containertype)
|
||||
{
|
||||
site.DefaultContainerType = _containertype;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
site.AdminContainerType = _admincontainertype;
|
||||
|
||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||
{
|
||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||
{
|
||||
site.PwaAppIconFileId = pwaappiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||
{
|
||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
|
||||
site = await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
if (refresh || reload)
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
}
|
||||
else // deuplicate alias or default alias not specified
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -578,19 +546,19 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
var sites = await SiteService.GetSitesAsync();
|
||||
if (sites.Count > 1)
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||
{
|
||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(a.AliasId);
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
|
||||
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -637,20 +605,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetAliases()
|
||||
{
|
||||
_urls = string.Empty;
|
||||
_defaultalias = string.Empty;
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
foreach (Alias alias in _aliases)
|
||||
{
|
||||
_urls += (_urls == string.Empty) ? alias.Name.Trim() : ", " + alias.Name.Trim();
|
||||
if (alias.IsDefault && string.IsNullOrEmpty(_defaultalias)) _defaultalias = alias.Name.Trim();
|
||||
}
|
||||
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
||||
}
|
||||
|
||||
private void ToggleSMTPPassword()
|
||||
{
|
||||
if (_smtppasswordtype == "password")
|
||||
@ -664,4 +618,84 @@
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetAliases()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAlias()
|
||||
{
|
||||
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||
_aliasid = 0;
|
||||
_aliasname = "";
|
||||
_defaultalias = "False";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void EditAlias(Alias alias)
|
||||
{
|
||||
_aliasid = alias.AliasId;
|
||||
_aliasname = alias.Name;
|
||||
_defaultalias = alias.IsDefault.ToString();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeleteAlias(Alias alias)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
await GetAliases();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveAlias()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_aliasname))
|
||||
{
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
var alias = aliases.Where(item => item.Name == _aliasname).FirstOrDefault();
|
||||
bool unique = (alias == null || alias.AliasId == _aliasid);
|
||||
if (unique)
|
||||
{
|
||||
if (_aliasid == 0)
|
||||
{
|
||||
alias = new Alias { SiteId = PageState.Site.SiteId, TenantId = PageState.Site.TenantId, Name = _aliasname, IsDefault = bool.Parse(_defaultalias) };
|
||||
await AliasService.AddAliasAsync(alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias = _aliases.Single(item => item.AliasId == _aliasid);
|
||||
alias.Name = _aliasname;
|
||||
alias.IsDefault = bool.Parse(_defaultalias);
|
||||
await AliasService.UpdateAliasAsync(alias);
|
||||
}
|
||||
}
|
||||
else // duplicate alias
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CancelAlias()
|
||||
{
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<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>
|
||||
<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)." ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||
</div>
|
||||
@ -128,25 +128,43 @@ else
|
||||
<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>
|
||||
@if (_databases != null)
|
||||
{
|
||||
<div class="input-group">
|
||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||
@foreach (var database in _databases)
|
||||
{
|
||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||
}
|
||||
</select>
|
||||
@if (!_showConnectionString)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
if (_databaseConfigType != null)
|
||||
{
|
||||
@DatabaseConfigComponent;
|
||||
}
|
||||
@if (!_showConnectionString)
|
||||
{
|
||||
if (_databaseConfigType != null)
|
||||
{
|
||||
@DatabaseConfigComponent
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">String:</Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -169,153 +187,171 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Database> _databases;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _databaseName = "LocalDB";
|
||||
private Type _databaseConfigType;
|
||||
private object _databaseConfig;
|
||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||
private List<Database> _databases;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _databaseName;
|
||||
private Type _databaseConfigType;
|
||||
private object _databaseConfig;
|
||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||
private bool _showConnectionString = false;
|
||||
private string _connectionString = string.Empty;
|
||||
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<SiteTemplate> _siteTemplates;
|
||||
private List<Tenant> _tenants;
|
||||
private string _tenantid = "-";
|
||||
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<SiteTemplate> _siteTemplates;
|
||||
private List<Tenant> _tenants;
|
||||
private string _tenantid = "-";
|
||||
private string _tenantName = string.Empty;
|
||||
|
||||
private string _tenantName = string.Empty;
|
||||
private string _hostusername = string.Empty;
|
||||
private string _hostpassword = string.Empty;
|
||||
|
||||
private string _hostusername = string.Empty;
|
||||
private string _hostpassword = string.Empty;
|
||||
private string _name = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "";
|
||||
private string _sitetemplatetype = "-";
|
||||
private string _runtime = "Server";
|
||||
private string _prerender = "Prerendered";
|
||||
|
||||
private string _name = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "";
|
||||
private string _sitetemplatetype = "-";
|
||||
private string _runtime = "Server";
|
||||
private string _prerender = "Prerendered";
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_tenants = await TenantService.GetTenantsAsync();
|
||||
_urls = PageState.Alias.Name;
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_tenants = await TenantService.GetTenantsAsync();
|
||||
_urls = PageState.Alias.Name;
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
{
|
||||
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseName = "LocalDB";
|
||||
}
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
|
||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
_databaseName = (string)eventArgs.Value;
|
||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
_databaseName = (string)eventArgs.Value;
|
||||
_showConnectionString = false;
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private void LoadDatabaseConfigComponent()
|
||||
{
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
if (database != null)
|
||||
{
|
||||
_databaseConfigType = Type.GetType(database.ControlType);
|
||||
DatabaseConfigComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _databaseConfigType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadDatabaseConfigComponent()
|
||||
{
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
if (database != null)
|
||||
{
|
||||
_databaseConfigType = Type.GetType(database.ControlType);
|
||||
DatabaseConfigComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _databaseConfigType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
private void TenantChanged(ChangeEventArgs e)
|
||||
{
|
||||
_tenantid = (string)e.Value;
|
||||
if (string.IsNullOrEmpty(_tenantName))
|
||||
{
|
||||
_tenantName = _name;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void TenantChanged(ChangeEventArgs e)
|
||||
{
|
||||
_tenantid = (string)e.Value;
|
||||
if (string.IsNullOrEmpty(_tenantName))
|
||||
{
|
||||
_tenantName = _name;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (_themetype != "-")
|
||||
{
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
}
|
||||
else
|
||||
{
|
||||
_containers = new List<ThemeControl>();
|
||||
}
|
||||
_containertype = "-";
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (_themetype != "-")
|
||||
{
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
}
|
||||
else
|
||||
{
|
||||
_containers = new List<ThemeControl>();
|
||||
}
|
||||
_containertype = "-";
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveSite()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||
{
|
||||
private async Task SaveSite()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||
{
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||
var duplicates = new List<string>();
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (aliases.Exists(item => item.Name == name))
|
||||
{
|
||||
duplicates.Add(name);
|
||||
}
|
||||
}
|
||||
var duplicates = new List<string>();
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (aliases.Exists(item => item.Name == name))
|
||||
{
|
||||
duplicates.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicates.Count == 0)
|
||||
{
|
||||
InstallConfig config = new InstallConfig();
|
||||
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 = _hostusername;
|
||||
user.Password = _hostpassword;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
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 = _hostusername;
|
||||
user.Password = _hostpassword;
|
||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
var connectionString = String.Empty;
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
var connectionString = String.Empty;
|
||||
if (_showConnectionString)
|
||||
{
|
||||
connectionString = _connectionString;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionString != "")
|
||||
{
|
||||
@ -396,4 +432,13 @@ else
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleConnectionString()
|
||||
{
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
_connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
_showConnectionString = !_showConnectionString;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,13 @@
|
||||
<input id="ipaddress" class="form-control" @bind="@_ipaddress" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="environment" HelpText="Environment name" ResourceKey="Environment">Environment: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="environment" class="form-control" @bind="@_environment" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="contentrootpath" class="form-control" @bind="@_contentrootpath" readonly />
|
||||
@ -142,6 +148,7 @@
|
||||
<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>
|
||||
</TabStrip>
|
||||
<br /><br />
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
@ -151,6 +158,7 @@
|
||||
private string _osversion = string.Empty;
|
||||
private string _machinename = string.Empty;
|
||||
private string _ipaddress = string.Empty;
|
||||
private string _environment = string.Empty;
|
||||
private string _contentrootpath = string.Empty;
|
||||
private string _webrootpath = string.Empty;
|
||||
private string _servertime = string.Empty;
|
||||
@ -175,6 +183,7 @@
|
||||
_osversion = systeminfo["OSVersion"].ToString();
|
||||
_machinename = systeminfo["MachineName"].ToString();
|
||||
_ipaddress = systeminfo["IPAddress"].ToString();
|
||||
_environment = systeminfo["Environment"].ToString();
|
||||
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
||||
_webrootpath = systeminfo["WebRootPath"].ToString();
|
||||
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
||||
|
@ -35,6 +35,7 @@
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
|
@ -25,7 +25,13 @@
|
||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed" ResourceKey="PackageName">Package Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||
@ -53,28 +59,30 @@
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private string _themeName = "";
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
private string _themeName = "";
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _packagename;
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
||||
if (theme != null)
|
||||
{
|
||||
_name = theme.Name;
|
||||
_version = theme.Version;
|
||||
_owner = theme.Owner;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
||||
if (theme != null)
|
||||
{
|
||||
_name = theme.Name;
|
||||
_version = theme.Version;
|
||||
_packagename = theme.PackageName;
|
||||
_owner = theme.Owner;
|
||||
_url = theme.Url;
|
||||
_contact = theme.Contact;
|
||||
_license = theme.License;
|
||||
|
@ -160,7 +160,7 @@ else
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
<td>@context.FromDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@context.CreatedOn</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
@ -193,7 +193,7 @@ else
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
<td>@context.ToDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@context.CreatedOn</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
|
@ -72,8 +72,19 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lastlogin" class="form-control" @bind="@lastlogin" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lastipaddress" HelpText="The IP Address of the user recorded during their last login" ResourceKey="LastIPAddress"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lastipaddress" class="form-control" @bind="@lastipaddress" readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
@ -137,63 +148,70 @@ else
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
|
||||
|
||||
@code {
|
||||
private int userid;
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private int userid;
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string deletedby;
|
||||
private DateTime? deletedon;
|
||||
private string isdeleted;
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private string isdeleted;
|
||||
private string lastlogin;
|
||||
private string lastipaddress;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
username = user.Username;
|
||||
email = user.Email;
|
||||
displayname = user.DisplayName;
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string deletedby;
|
||||
private DateTime? deletedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
username = user.Username;
|
||||
email = user.Email;
|
||||
displayname = user.DisplayName;
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
|
||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||
createdby = user.CreatedBy;
|
||||
createdon = user.CreatedOn;
|
||||
modifiedby = user.ModifiedBy;
|
||||
modifiedon = user.ModifiedOn;
|
||||
deletedby = user.DeletedBy;
|
||||
deletedon = user.DeletedOn;
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,14 +33,12 @@ else
|
||||
</div>
|
||||
<Pager Items="@users" RowClass="align-middle">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@SharedLocalizer["Username"]</th>
|
||||
<th>@Localizer["LastLoginOn"]</th>
|
||||
<th>@Localizer["LastIPAddress"]</th>
|
||||
<th>@Localizer["CreatedOn"]</th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Username"]</th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["LastLoginOn"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
@ -52,12 +50,9 @@ else
|
||||
<td>
|
||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
||||
</td>
|
||||
<td>@context.User.DisplayName</td>
|
||||
<td>@context.User.Username</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}",context.User.LastLoginOn)</td>
|
||||
<td>@context.User.LastIPAddress</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}",context.User.CreatedOn)</td>
|
||||
|
||||
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn)</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
@ -259,6 +254,12 @@ else
|
||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -380,6 +381,7 @@ else
|
||||
private string _clientsecrettype = "password";
|
||||
private string _toggleclientsecret = string.Empty;
|
||||
private string _scopes;
|
||||
private string _parameters;
|
||||
private string _pkce;
|
||||
private string _redirecturl;
|
||||
private string _identifierclaimtype;
|
||||
@ -432,6 +434,7 @@ else
|
||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
||||
@ -549,6 +552,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||
|
@ -88,15 +88,17 @@ else
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
name = user.DisplayName;
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId, true);
|
||||
roles = roles.Where(item => item.Name != RoleNames.Everyone).ToList();
|
||||
roles.RemoveAll(item => item.Name == RoleNames.Everyone || item.Name == RoleNames.Unauthenticated);
|
||||
}
|
||||
else
|
||||
{
|
||||
roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
|
||||
}
|
||||
|
||||
await GetUserRoles();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -86,279 +86,296 @@
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _id;
|
||||
private List<Folder> _folders;
|
||||
private List<File> _files = new List<File>();
|
||||
private string _fileinputid = string.Empty;
|
||||
private string _progressinfoid = string.Empty;
|
||||
private string _progressbarid = string.Empty;
|
||||
private string _filter = "*";
|
||||
private bool _haseditpermission = false;
|
||||
private string _image = string.Empty;
|
||||
private File _file = null;
|
||||
private string _guid;
|
||||
private string _message = string.Empty;
|
||||
private MessageType _messagetype;
|
||||
private string _id;
|
||||
private List<Folder> _folders;
|
||||
private List<File> _files = new List<File>();
|
||||
private string _fileinputid = string.Empty;
|
||||
private string _progressinfoid = string.Empty;
|
||||
private string _progressbarid = string.Empty;
|
||||
private string _filter = "*";
|
||||
private bool _haseditpermission = false;
|
||||
private string _image = string.Empty;
|
||||
private File _file = null;
|
||||
private string _guid;
|
||||
private string _message = string.Empty;
|
||||
private MessageType _messagetype;
|
||||
|
||||
[Parameter]
|
||||
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
|
||||
[Parameter]
|
||||
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
|
||||
|
||||
[Parameter]
|
||||
public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid
|
||||
[Parameter]
|
||||
public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid
|
||||
|
||||
[Parameter]
|
||||
public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path
|
||||
[Parameter]
|
||||
public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path
|
||||
|
||||
[Parameter]
|
||||
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default
|
||||
[Parameter]
|
||||
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default
|
||||
|
||||
[Parameter]
|
||||
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
|
||||
[Parameter]
|
||||
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
|
||||
|
||||
[Parameter]
|
||||
public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
|
||||
[Parameter]
|
||||
public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
|
||||
[Parameter]
|
||||
public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
|
||||
[Parameter]
|
||||
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
|
||||
[Parameter]
|
||||
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false
|
||||
[Parameter]
|
||||
public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false
|
||||
|
||||
[Parameter]
|
||||
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
|
||||
[Parameter]
|
||||
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> 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> 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
|
||||
[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()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Id))
|
||||
{
|
||||
_id = Id;
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Id))
|
||||
{
|
||||
_id = Id;
|
||||
}
|
||||
|
||||
// packages folder is a framework folder for uploading installable nuget packages
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
ShowFiles = false;
|
||||
ShowFolders = false;
|
||||
Filter = "nupkg";
|
||||
ShowSuccess = true;
|
||||
}
|
||||
// packages folder is a framework folder for uploading installable nuget packages
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
ShowFiles = false;
|
||||
ShowFolders = false;
|
||||
Filter = "nupkg";
|
||||
ShowSuccess = true;
|
||||
}
|
||||
|
||||
if (!ShowFiles)
|
||||
{
|
||||
ShowImage = false;
|
||||
}
|
||||
if (!ShowFiles)
|
||||
{
|
||||
ShowImage = false;
|
||||
}
|
||||
|
||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||
|
||||
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
|
||||
{
|
||||
Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
|
||||
if (folder != null)
|
||||
{
|
||||
FolderId = folder.FolderId;
|
||||
}
|
||||
else
|
||||
{
|
||||
FolderId = -1;
|
||||
_message = "Folder Path " + Folder + "Does Not Exist";
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
|
||||
{
|
||||
Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
|
||||
if (folder != null)
|
||||
{
|
||||
FolderId = folder.FolderId;
|
||||
}
|
||||
else
|
||||
{
|
||||
FolderId = -1;
|
||||
_message = "Folder Path " + Folder + "Does Not Exist";
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
|
||||
if (FileId != -1)
|
||||
{
|
||||
File file = await FileService.GetFileAsync(FileId);
|
||||
if (file != null)
|
||||
{
|
||||
FolderId = file.FolderId;
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileId = -1; // file does not exist
|
||||
_message = "FileId " + FileId.ToString() + "Does Not Exist";
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
if (FileId != -1)
|
||||
{
|
||||
File file = await FileService.GetFileAsync(FileId);
|
||||
if (file != null)
|
||||
{
|
||||
FolderId = file.FolderId;
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileId = -1; // file does not exist
|
||||
_message = "FileId " + FileId.ToString() + "Does Not Exist";
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
|
||||
await SetImage();
|
||||
await SetImage();
|
||||
|
||||
if (!string.IsNullOrEmpty(Filter))
|
||||
{
|
||||
_filter = "." + Filter.Replace(",", ",.");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Filter))
|
||||
{
|
||||
_filter = "." + Filter.Replace(",", ",.");
|
||||
}
|
||||
|
||||
await GetFiles();
|
||||
await GetFiles();
|
||||
|
||||
// create unique id for component
|
||||
_guid = Guid.NewGuid().ToString("N");
|
||||
_fileinputid = _guid + "FileInput";
|
||||
_progressinfoid = _guid + "ProgressInfo";
|
||||
_progressbarid = _guid + "ProgressBar";
|
||||
}
|
||||
// create unique id for component
|
||||
_guid = Guid.NewGuid().ToString("N");
|
||||
_fileinputid = _guid + "FileInput";
|
||||
_progressinfoid = _guid + "ProgressInfo";
|
||||
_progressbarid = _guid + "ProgressBar";
|
||||
}
|
||||
|
||||
private async Task GetFiles()
|
||||
{
|
||||
_haseditpermission = false;
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
|
||||
_files = new List<File>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
|
||||
if (folder != null)
|
||||
{
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
|
||||
_files = await FileService.GetFilesAsync(FolderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_haseditpermission = false;
|
||||
_files = new List<File>();
|
||||
}
|
||||
}
|
||||
if (_filter != "*")
|
||||
{
|
||||
List<File> filtered = new List<File>();
|
||||
foreach (File file in _files)
|
||||
{
|
||||
if (_filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1)
|
||||
{
|
||||
filtered.Add(file);
|
||||
}
|
||||
}
|
||||
_files = filtered;
|
||||
}
|
||||
}
|
||||
private async Task GetFiles()
|
||||
{
|
||||
_haseditpermission = false;
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
|
||||
_files = new List<File>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
|
||||
if (folder != null)
|
||||
{
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
|
||||
_files = await FileService.GetFilesAsync(FolderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_haseditpermission = false;
|
||||
_files = new List<File>();
|
||||
}
|
||||
}
|
||||
if (_filter != "*")
|
||||
{
|
||||
List<File> filtered = new List<File>();
|
||||
foreach (File file in _files)
|
||||
{
|
||||
if (_filter.ToUpper().IndexOf("." + file.Extension.ToUpper()) != -1)
|
||||
{
|
||||
filtered.Add(file);
|
||||
}
|
||||
}
|
||||
_files = filtered;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FolderChanged(ChangeEventArgs e)
|
||||
{
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
FolderId = int.Parse((string)e.Value);
|
||||
await GetFiles();
|
||||
FileId = -1;
|
||||
_file = null;
|
||||
_image = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
|
||||
_message = Localizer["Error.File.Load"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
private async Task FolderChanged(ChangeEventArgs e)
|
||||
{
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
FolderId = int.Parse((string)e.Value);
|
||||
await GetFiles();
|
||||
FileId = -1;
|
||||
_file = null;
|
||||
_image = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
|
||||
_message = Localizer["Error.File.Load"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FileChanged(ChangeEventArgs e)
|
||||
{
|
||||
_message = string.Empty;
|
||||
FileId = int.Parse((string)e.Value);
|
||||
if (FileId != -1)
|
||||
{
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
}
|
||||
private async Task FileChanged(ChangeEventArgs e)
|
||||
{
|
||||
_message = string.Empty;
|
||||
FileId = int.Parse((string)e.Value);
|
||||
if (FileId != -1)
|
||||
{
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
}
|
||||
|
||||
await SetImage();
|
||||
StateHasChanged();
|
||||
}
|
||||
await SetImage();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task SetImage()
|
||||
{
|
||||
_image = string.Empty;
|
||||
_file = null;
|
||||
if (FileId != -1)
|
||||
{
|
||||
_file = await FileService.GetFileAsync(FileId);
|
||||
if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0)
|
||||
{
|
||||
var maxwidth = 200;
|
||||
var maxheight = 200;
|
||||
private async Task SetImage()
|
||||
{
|
||||
_image = string.Empty;
|
||||
_file = null;
|
||||
if (FileId != -1)
|
||||
{
|
||||
_file = await FileService.GetFileAsync(FileId);
|
||||
if (_file != null && ShowImage && _file.ImageHeight != 0 && _file.ImageWidth != 0)
|
||||
{
|
||||
var maxwidth = 200;
|
||||
var maxheight = 200;
|
||||
|
||||
var ratioX = (double)maxwidth / (double)_file.ImageWidth;
|
||||
var ratioY = (double)maxheight / (double)_file.ImageHeight;
|
||||
var ratio = ratioX < ratioY ? ratioX : ratioY;
|
||||
var ratioX = (double)maxwidth / (double)_file.ImageWidth;
|
||||
var ratioY = (double)maxheight / (double)_file.ImageHeight;
|
||||
var ratio = ratioX < ratioY ? ratioX : ratioY;
|
||||
|
||||
_image = "<img src=\"" + _file.Url + "\" alt=\"" + _file.Name +
|
||||
"\" width=\"" + Convert.ToInt32(_file.ImageWidth * ratio).ToString() +
|
||||
"\" height=\"" + Convert.ToInt32(_file.ImageHeight * ratio).ToString() + "\" />";
|
||||
}
|
||||
}
|
||||
}
|
||||
_image = "<img src=\"" + _file.Url + "\" alt=\"" + _file.Name +
|
||||
"\" width=\"" + Convert.ToInt32(_file.ImageWidth * ratio).ToString() +
|
||||
"\" height=\"" + Convert.ToInt32(_file.ImageHeight * ratio).ToString() + "\" />";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UploadFile()
|
||||
{
|
||||
_message = string.Empty;
|
||||
var interop = new Interop(JSRuntime);
|
||||
var upload = await interop.GetFiles(_fileinputid);
|
||||
if (upload.Length > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
string result;
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
result = await FileService.UploadFilesAsync(Folder, upload, _guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
||||
}
|
||||
private async Task UploadFile()
|
||||
{
|
||||
_message = string.Empty;
|
||||
var interop = new Interop(JSRuntime);
|
||||
var upload = await interop.GetFiles(_fileinputid);
|
||||
if (upload.Length > 0)
|
||||
{
|
||||
string restricted = "";
|
||||
foreach (var file in upload)
|
||||
{
|
||||
var extension = (file.LastIndexOf(".") != -1) ? file.Substring(file.LastIndexOf(".") + 1) : "";
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
{
|
||||
restricted += (restricted == "" ? "" : ",") + extension;
|
||||
}
|
||||
}
|
||||
if (restricted == "")
|
||||
{
|
||||
try
|
||||
{
|
||||
string result;
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
result = await FileService.UploadFilesAsync(Folder, upload, _guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
||||
}
|
||||
|
||||
if (result == string.Empty)
|
||||
{
|
||||
await logger.LogInformation("File Upload Succeeded {Files}", upload);
|
||||
if (ShowSuccess)
|
||||
{
|
||||
_message = Localizer["Success.File.Upload"];
|
||||
_messagetype = MessageType.Success;
|
||||
}
|
||||
if (result == string.Empty)
|
||||
{
|
||||
await logger.LogInformation("File Upload Succeeded {Files}", upload);
|
||||
if (ShowSuccess)
|
||||
{
|
||||
_message = Localizer["Success.File.Upload"];
|
||||
_messagetype = MessageType.Success;
|
||||
}
|
||||
|
||||
// set FileId to first file in upload collection
|
||||
await GetFiles();
|
||||
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
|
||||
if (file != null)
|
||||
{
|
||||
FileId = file.FileId;
|
||||
await SetImage();
|
||||
await OnUpload.InvokeAsync(FileId);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
|
||||
// set FileId to first file in upload collection
|
||||
await GetFiles();
|
||||
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
|
||||
if (file != null)
|
||||
{
|
||||
FileId = file.FileId;
|
||||
await SetImage();
|
||||
await OnUpload.InvokeAsync(FileId);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
|
||||
|
||||
_message = Localizer["Error.File.Upload"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
||||
_message = Localizer["Error.File.Upload"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
||||
|
||||
_message = Localizer["Error.File.Upload"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
_message = Localizer["Error.File.Upload"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.File.NotSelected"];
|
||||
|
@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Modules.Controls
|
||||
{
|
||||
public class LocalizableComponent : ModuleControlBase
|
||||
{
|
||||
[Inject] public IStringLocalizerFactory LocalizerFactory { get; set; }
|
||||
|
||||
private IStringLocalizer _localizer;
|
||||
|
||||
[Parameter]
|
||||
@ -30,22 +30,15 @@ namespace Oqtane.Modules.Controls
|
||||
var key = $"{ResourceKey}.{propertyName}";
|
||||
var value = Localize(key);
|
||||
|
||||
if (value == key)
|
||||
if (value == key || value == String.Empty)
|
||||
{
|
||||
// Returns default property value (English version) instead of ResourceKey.PropertyName
|
||||
// return default property value if key does not exist in resource file or value is empty
|
||||
return propertyValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value == String.Empty)
|
||||
{
|
||||
// Returns default property value (English version)
|
||||
return propertyValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
// return localized value
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,24 +46,15 @@ namespace Oqtane.Modules.Controls
|
||||
{
|
||||
IsLocalizable = false;
|
||||
|
||||
if (string.IsNullOrEmpty(ResourceType))
|
||||
if (String.IsNullOrEmpty(ResourceType))
|
||||
{
|
||||
ResourceType = ModuleState?.ModuleType;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType))
|
||||
if (!String.IsNullOrEmpty(ResourceKey) && !String.IsNullOrEmpty(ResourceType))
|
||||
{
|
||||
var moduleType = Type.GetType(ResourceType);
|
||||
if (moduleType != null)
|
||||
{
|
||||
using (var scope = ServiceActivator.GetScope())
|
||||
{
|
||||
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
|
||||
_localizer = localizerFactory.Create(moduleType);
|
||||
|
||||
IsLocalizable = true;
|
||||
}
|
||||
}
|
||||
_localizer = LocalizerFactory.Create(ResourceType);
|
||||
IsLocalizable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@inject IStringLocalizerFactory LocalizerFactory
|
||||
@typeparam TableItem
|
||||
|
||||
@if (ItemList != null)
|
||||
@ -48,7 +49,7 @@
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@ -156,67 +157,73 @@
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private int _pages = 0;
|
||||
private int _page = 1;
|
||||
private int _maxItems = 10;
|
||||
private int _displayPages = 5;
|
||||
private int _startPage = 0;
|
||||
private int _endPage = 0;
|
||||
private int _columns = 0;
|
||||
|
||||
[Parameter]
|
||||
public string Format { get; set; } // Table or Grid
|
||||
|
||||
[Parameter]
|
||||
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
|
||||
|
||||
[Parameter]
|
||||
public string PageSize { get; set; } // number of items to display on a page
|
||||
|
||||
[Parameter]
|
||||
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||
|
||||
[Parameter]
|
||||
public string CurrentPage { get; set; } // sets the initial page to display
|
||||
|
||||
[Parameter]
|
||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||
private IStringLocalizer Localizer;
|
||||
private int _pages = 0;
|
||||
private int _page = 1;
|
||||
private int _maxItems = 10;
|
||||
private int _displayPages = 5;
|
||||
private int _startPage = 0;
|
||||
private int _endPage = 0;
|
||||
private int _columns = 0;
|
||||
|
||||
[Parameter]
|
||||
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||
public string Format { get; set; } // Table or Grid
|
||||
|
||||
[Parameter]
|
||||
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||
[Parameter]
|
||||
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
[Parameter]
|
||||
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
|
||||
|
||||
[Parameter]
|
||||
public string PageSize { get; set; } // number of items to display on a page
|
||||
|
||||
[Parameter]
|
||||
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||
|
||||
[Parameter]
|
||||
public string CurrentPage { get; set; } // sets the initial page to display
|
||||
|
||||
[Parameter]
|
||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||
|
||||
[Parameter]
|
||||
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||
|
||||
[Parameter]
|
||||
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Localizer = LocalizerFactory.Create(GetType().FullName);
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Format))
|
||||
{
|
||||
Format = "Table";
|
||||
|
@ -127,11 +127,10 @@
|
||||
_permissionnames = PermissionNames;
|
||||
}
|
||||
|
||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
|
||||
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
|
||||
if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_roles.Add(new Role { Name = RoleNames.Host });
|
||||
_roles.RemoveAll(item => item.Name == RoleNames.Host);
|
||||
}
|
||||
|
||||
_permissions = new List<PermissionString>();
|
||||
@ -254,6 +253,7 @@
|
||||
permission = _permissions[i];
|
||||
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
|
||||
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
|
@ -150,10 +150,6 @@
|
||||
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseFileManager()
|
||||
|
@ -5,7 +5,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.1.1</Version>
|
||||
<Version>3.1.4</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -13,7 +13,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -114,6 +114,7 @@ namespace Oqtane.Client
|
||||
|
||||
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
|
||||
{
|
||||
// dynamically register module scoped services
|
||||
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
|
@ -121,7 +121,7 @@
|
||||
<value>Server:</value>
|
||||
</data>
|
||||
<data name="Server.HelpText" xml:space="preserve">
|
||||
<value>Enter the database server</value>
|
||||
<value>Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) </value>
|
||||
</data>
|
||||
<data name="Database.Text" xml:space="preserve">
|
||||
<value>Database:</value>
|
||||
@ -133,7 +133,7 @@
|
||||
<value>Integrated Security:</value>
|
||||
</data>
|
||||
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
|
||||
<value>Select if you want integrated security or not</value>
|
||||
<value>Select if you are using integrated security</value>
|
||||
</data>
|
||||
<data name="Uid.Text" xml:space="preserve">
|
||||
<value>User Id:</value>
|
||||
@ -163,7 +163,7 @@
|
||||
<value>Self Signed</value>
|
||||
</data>
|
||||
<data name="TrustServerCertificate.HelpText" xml:space="preserve">
|
||||
<value>Specify the type of certificate you are using for encryption</value>
|
||||
<value>Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True.</value>
|
||||
</data>
|
||||
<data name="TrustServerCertificate.Text" xml:space="preserve">
|
||||
<value>Trust Server Certificate:</value>
|
||||
|
@ -135,10 +135,10 @@
|
||||
<data name="Message.Require.DbInfo" xml:space="preserve">
|
||||
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
||||
</data>
|
||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||
<value>The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements.</value>
|
||||
</data>
|
||||
<data name="Register" xml:space="preserve">
|
||||
<data name="Register" xml:space="preserve">
|
||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||
</data>
|
||||
<data name="Confirm.HelpText" xml:space="preserve">
|
||||
@ -168,4 +168,16 @@
|
||||
<data name="Username.Text" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||
</data>
|
||||
<data name="ConnectionString.Text" xml:space="preserve">
|
||||
<value>String:</value>
|
||||
</data>
|
||||
<data name="EnterConnectionParameters" xml:space="preserve">
|
||||
<value>Enter Connection Parameters</value>
|
||||
</data>
|
||||
<data name="EnterConnectionString" xml:space="preserve">
|
||||
<value>Enter Connection String</value>
|
||||
</data>
|
||||
</root>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -118,6 +118,6 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Error.Module.Load" xml:space="preserve">
|
||||
<value>A Problem Was Encountered Loading Module {0}</value>
|
||||
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||
</data>
|
||||
</root>
|
@ -192,4 +192,7 @@
|
||||
<data name="Once" xml:space="preserve">
|
||||
<value>Execute Once</value>
|
||||
</data>
|
||||
<data name="Message.NoJobs" xml:space="preserve">
|
||||
<value>Please Note That After An Initial Installation You Must &lt;a href={0}&gt;Restart&lt;/a&gt; The Application In Order To Activate The Default Scheduled Jobs.</value>
|
||||
</data>
|
||||
</root>
|
@ -154,13 +154,13 @@
|
||||
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
||||
</data>
|
||||
<data name="Download.Heading" xml:space="preserve">
|
||||
<value>Download</value>
|
||||
<value>Translations</value>
|
||||
</data>
|
||||
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||
<value>Upload one or more translations. Once they are uploaded click Install to complete the installation.</value>
|
||||
<value>Upload one or more translation packages. Once they are uploaded click Install to complete the installation.</value>
|
||||
</data>
|
||||
<data name="LanguageUpload.Text" xml:space="preserve">
|
||||
<value>Upload Language</value>
|
||||
<value>Translation</value>
|
||||
</data>
|
||||
<data name="Manage.Heading" xml:space="preserve">
|
||||
<value>Manage</value>
|
||||
|
@ -144,4 +144,7 @@
|
||||
<data name="DeleteLanguage.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="Translation" xml:space="preserve">
|
||||
<value>Translation</value>
|
||||
</data>
|
||||
</root>
|
@ -195,4 +195,25 @@
|
||||
<data name="Information.Text" xml:space="preserve">
|
||||
<value>Information</value>
|
||||
</data>
|
||||
<data name="PackageName.HelpText" xml:space="preserve">
|
||||
<value>The unique name of the package from which this module was installed</value>
|
||||
</data>
|
||||
<data name="PackageName.Text" xml:space="preserve">
|
||||
<value>Package Name:</value>
|
||||
</data>
|
||||
<data name="Error.Translation.Download" xml:space="preserve">
|
||||
<value>Error Downloading Translation</value>
|
||||
</data>
|
||||
<data name="Search.NoResults" xml:space="preserve">
|
||||
<value>No Translations Exist For This Module Or Package Service Is Disabled</value>
|
||||
</data>
|
||||
<data name="Success.Translation.Download" xml:space="preserve">
|
||||
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
|
||||
</data>
|
||||
<data name="Success.Translation.Install" xml:space="preserve">
|
||||
<value>Translation Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
||||
</data>
|
||||
<data name="Translations.Heading" xml:space="preserve">
|
||||
<value>Translations</value>
|
||||
</data>
|
||||
</root>
|
@ -150,4 +150,7 @@
|
||||
<data name="EditModule.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<data name="Modules" xml:space="preserve">
|
||||
<value>Modules</value>
|
||||
</data>
|
||||
</root>
|
@ -147,4 +147,7 @@
|
||||
<data name="Message.Required.Title" xml:space="preserve">
|
||||
<value>You Must Provide A Title For The Module</value>
|
||||
</data>
|
||||
<data name="Error.Module.Load" xml:space="preserve">
|
||||
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||
</data>
|
||||
</root>
|
@ -142,7 +142,7 @@
|
||||
<value>Site Settings Saved</value>
|
||||
</data>
|
||||
<data name="Message.Aliases.Taken" xml:space="preserve">
|
||||
<value>The Default Alias Has Not Been Specified Or An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||
<value>An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||
</data>
|
||||
<data name="Message.Required.SiteName" xml:space="preserve">
|
||||
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
||||
@ -166,7 +166,7 @@
|
||||
<value>Enter the tenant for the site</value>
|
||||
</data>
|
||||
<data name="Aliases.HelpText" xml:space="preserve">
|
||||
<value>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.</value>
|
||||
<value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder).</value>
|
||||
</data>
|
||||
<data name="IsDeleted.HelpText" xml:space="preserve">
|
||||
<value>Is this site deleted?</value>
|
||||
@ -324,4 +324,13 @@
|
||||
<data name="Aliases.Heading" xml:space="preserve">
|
||||
<value>Aliases</value>
|
||||
</data>
|
||||
<data name="AliasName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="AliasDefault" xml:space="preserve">
|
||||
<value>Default?</value>
|
||||
</data>
|
||||
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete {0}?</value>
|
||||
</data>
|
||||
</root>
|
@ -270,4 +270,16 @@
|
||||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Runtime: </value>
|
||||
</data>
|
||||
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||
</data>
|
||||
<data name="ConnectionString.Text" xml:space="preserve">
|
||||
<value>String:</value>
|
||||
</data>
|
||||
<data name="EnterConnectionParameters" xml:space="preserve">
|
||||
<value>Enter Connection Parameters</value>
|
||||
</data>
|
||||
<data name="EnterConnectionString" xml:space="preserve">
|
||||
<value>Enter Connection String</value>
|
||||
</data>
|
||||
</root>
|
@ -270,4 +270,10 @@
|
||||
<data name="WorkingSet.Text" xml:space="preserve">
|
||||
<value>Memory Allocation:</value>
|
||||
</data>
|
||||
<data name="Environment.HelpText" xml:space="preserve">
|
||||
<value>Environment Name</value>
|
||||
</data>
|
||||
<data name="Environment.Text" xml:space="preserve">
|
||||
<value>Environment:</value>
|
||||
</data>
|
||||
</root>
|
@ -162,4 +162,10 @@
|
||||
<data name="License.HelpText" xml:space="preserve">
|
||||
<value>The license of the theme</value>
|
||||
</data>
|
||||
<data name="PackageName.HelpText" xml:space="preserve">
|
||||
<value>The unique name of the package from which this module was installed</value>
|
||||
</data>
|
||||
<data name="PackageName.Text" xml:space="preserve">
|
||||
<value>Package Name:</value>
|
||||
</data>
|
||||
</root>
|
@ -219,4 +219,10 @@
|
||||
<data name="DeleteAllNotifications.Text" xml:space="preserve">
|
||||
<value>Delete ALL Notifications</value>
|
||||
</data>
|
||||
<data name="Notifications.Heading" xml:space="preserve">
|
||||
<value>Notifications</value>
|
||||
</data>
|
||||
<data name="Profile.Heading" xml:space="preserve">
|
||||
<value>Profile</value>
|
||||
</data>
|
||||
</root>
|
@ -186,4 +186,16 @@
|
||||
<data name="Password.Placeholder" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="LastIPAddress.HelpText" xml:space="preserve">
|
||||
<value>The IP Address of the user recorded during their last login</value>
|
||||
</data>
|
||||
<data name="LastIPAddress.Text" xml:space="preserve">
|
||||
<value>Last IP Address: </value>
|
||||
</data>
|
||||
<data name="LastLogin.HelpText" xml:space="preserve">
|
||||
<value>The date and time when the user last signed in</value>
|
||||
</data>
|
||||
<data name="LastLogin.Text" xml:space="preserve">
|
||||
<value>Last Login:</value>
|
||||
</data>
|
||||
</root>
|
@ -369,12 +369,6 @@
|
||||
<data name="Required" xml:space="preserve">
|
||||
<value>Required</value>
|
||||
</data>
|
||||
<data name="CreatedOn" xml:space="preserve">
|
||||
<value>Created On</value>
|
||||
</data>
|
||||
<data name="LastIPAddress" xml:space="preserve">
|
||||
<value>Last IP Address</value>
|
||||
</data>
|
||||
<data name="LastLoginOn" xml:space="preserve">
|
||||
<value>Last Login</value>
|
||||
</data>
|
||||
@ -384,4 +378,10 @@
|
||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||
<value>Identifier Claim:</value>
|
||||
</data>
|
||||
<data name="Parameters.HelpText" xml:space="preserve">
|
||||
<value>Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple).</value>
|
||||
</data>
|
||||
<data name="Parameters.Text" xml:space="preserve">
|
||||
<value>Parameters:</value>
|
||||
</data>
|
||||
</root>
|
@ -141,4 +141,7 @@
|
||||
<data name="Success.File.Upload" xml:space="preserve">
|
||||
<value>File Upload Succeeded</value>
|
||||
</data>
|
||||
<data name="Message.File.Restricted" xml:space="preserve">
|
||||
<value>Files With Extension Of {0} Are Restricted From Upload. Please Contact Your Administrator For More Information.</value>
|
||||
</data>
|
||||
</root>
|
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
123
Oqtane.Client/Resources/Modules/Controls/Pager.resx
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
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.
|
||||
|
||||
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 id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="PageOfPages" xml:space="preserve">
|
||||
<value>Page {0} of {1}</value>
|
||||
</data>
|
||||
</root>
|
@ -327,4 +327,13 @@
|
||||
<data name="ShowPassword" xml:space="preserve">
|
||||
<value>Show</value>
|
||||
</data>
|
||||
<data name="PageOfPages" xml:space="preserve">
|
||||
<value>Page {0} of {1}</value>
|
||||
</data>
|
||||
<data name="Url Mappings" xml:space="preserve">
|
||||
<value>Url Mappings</value>
|
||||
</data>
|
||||
<data name="Visitor Management" xml:space="preserve">
|
||||
<value>Visitor Management</value>
|
||||
</data>
|
||||
</root>
|
@ -138,6 +138,12 @@
|
||||
<data name="Register.Text" xml:space="preserve">
|
||||
<value>Show Register?</value>
|
||||
</data>
|
||||
<data name="Scope.HelpText" xml:space="preserve">
|
||||
<value>Specify if the settings are applicable to this page or the entire site.</value>
|
||||
</data>
|
||||
<data name="Scope.Text" xml:space="preserve">
|
||||
<value>Setting Scope:</value>
|
||||
</data>
|
||||
<data name="Site" xml:space="preserve">
|
||||
<value>Site</value>
|
||||
</data>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
@ -14,10 +15,12 @@ namespace Oqtane.Services
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class FileService : ServiceBase, IFileService
|
||||
{
|
||||
private readonly SiteState _siteState;
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
||||
{
|
||||
_siteState = siteState;
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
@ -30,7 +33,8 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<List<File>> GetFilesAsync(string folder)
|
||||
{
|
||||
return await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
||||
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}?folder={folder}");
|
||||
return files.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<File>> GetFilesAsync(int siteId, string folderPath)
|
||||
@ -42,7 +46,8 @@ namespace Oqtane.Services
|
||||
|
||||
var path = WebUtility.UrlEncode(folderPath);
|
||||
|
||||
return await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
||||
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
||||
return files?.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
public async Task<File> GetFileAsync(int fileId)
|
||||
@ -80,7 +85,7 @@ namespace Oqtane.Services
|
||||
string result = "";
|
||||
|
||||
var interop = new Interop(_jsRuntime);
|
||||
await interop.UploadFiles($"{Apiurl}/upload", folder, id);
|
||||
await interop.UploadFiles($"{Apiurl}/upload", folder, id, _siteState.AntiForgeryToken);
|
||||
|
||||
// uploading files is asynchronous so we need to wait for the upload to complete
|
||||
bool success = false;
|
||||
|
@ -17,6 +17,14 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<List<Language>> GetLanguagesAsync(int siteId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all available languages for the given <see cref="Site" /> and package
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="packageName"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<Language>> GetLanguagesAsync(int siteId, string packageName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the given language
|
||||
/// </summary>
|
||||
|
@ -17,18 +17,27 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
||||
{
|
||||
var languages = await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}");
|
||||
return await GetLanguagesAsync(siteId, "");
|
||||
}
|
||||
|
||||
return languages?.OrderBy(l => l.Name).ToList() ?? Enumerable.Empty<Language>().ToList();
|
||||
public async Task<List<Language>> GetLanguagesAsync(int siteId, string packageName)
|
||||
{
|
||||
return await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}&packagename={packageName}");
|
||||
}
|
||||
|
||||
public async Task<Language> GetLanguageAsync(int languageId)
|
||||
=> await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
||||
{
|
||||
return await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
||||
}
|
||||
|
||||
public async Task<Language> AddLanguageAsync(Language language)
|
||||
=> await PostJsonAsync<Language>(Apiurl, language);
|
||||
{
|
||||
return await PostJsonAsync<Language>(Apiurl, language);
|
||||
}
|
||||
|
||||
public async Task DeleteLanguageAsync(int languageId)
|
||||
=> await DeleteAsync($"{Apiurl}/{languageId}");
|
||||
{
|
||||
await DeleteAsync($"{Apiurl}/{languageId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<LanguageSwitcher />
|
||||
}
|
||||
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||
{
|
||||
if (PageState.EditMode)
|
||||
{
|
||||
@ -218,6 +218,7 @@
|
||||
}
|
||||
|
||||
@code{
|
||||
private bool _showEditMode = false;
|
||||
private bool _deleteConfirmation = false;
|
||||
private List<string> _categories = new List<string>();
|
||||
private List<ModuleDefinition> _allModuleDefinitions;
|
||||
@ -285,8 +286,10 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_showEditMode = false;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||
{
|
||||
_showEditMode = true;
|
||||
_pages?.Clear();
|
||||
|
||||
foreach (Page p in PageState.Pages)
|
||||
@ -305,6 +308,17 @@
|
||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
|
||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions))
|
||||
{
|
||||
_showEditMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CategoryChanged(ChangeEventArgs e)
|
||||
@ -405,21 +419,21 @@
|
||||
Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
||||
Title = "";
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = $"<div class=\"alert alert-warning mt-2 text-center\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = $"<div class=\"alert alert-warning mt-2 text-center\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ToggleEditMode(bool EditMode)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||
if (_showEditMode)
|
||||
{
|
||||
if (EditMode)
|
||||
{
|
||||
@ -445,7 +459,6 @@
|
||||
|
||||
private void Navigate(string location)
|
||||
{
|
||||
//HideControlPanel();
|
||||
Module module;
|
||||
switch (location)
|
||||
{
|
||||
|
@ -24,13 +24,9 @@
|
||||
@code{
|
||||
private IEnumerable<Culture> _supportedCultures;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
|
||||
|
||||
languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName });
|
||||
|
||||
var languages = PageState.Languages;
|
||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||
}
|
||||
|
||||
|
@ -9,15 +9,19 @@
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<a class="nav-link active px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="nav-link px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
@ -34,8 +38,10 @@ else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -43,8 +49,10 @@ else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
@ -9,8 +9,10 @@
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -18,8 +20,10 @@
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -38,8 +42,10 @@ else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@ -47,8 +53,10 @@ else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
@ -189,13 +189,13 @@ namespace Oqtane.UI
|
||||
}
|
||||
}
|
||||
|
||||
public Task UploadFiles(string posturl, string folder, string id)
|
||||
public Task UploadFiles(string posturl, string folder, string id, string antiforgerytoken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_jsRuntime.InvokeVoidAsync(
|
||||
"Oqtane.Interop.uploadFiles",
|
||||
posturl, folder, id);
|
||||
posturl, folder, id, antiforgerytoken);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
|
@ -8,6 +8,7 @@ namespace Oqtane.UI
|
||||
{
|
||||
public Alias Alias { get; set; }
|
||||
public Site Site { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public List<Page> Pages { get; set; }
|
||||
public Page Page { get; set; }
|
||||
public User User { get; set; }
|
||||
|
@ -6,6 +6,7 @@
|
||||
@inject INavigationInterception NavigationInterception
|
||||
@inject ISyncService SyncService
|
||||
@inject ISiteService SiteService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject IPageService PageService
|
||||
@inject IUserService UserService
|
||||
@inject IModuleService ModuleService
|
||||
@ -70,6 +71,7 @@
|
||||
private async Task Refresh()
|
||||
{
|
||||
Site site;
|
||||
List<Language> languages;
|
||||
List<Page> pages;
|
||||
Page page;
|
||||
User user = null;
|
||||
@ -102,7 +104,7 @@
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// the refresh parameter is used to refresh the client-side PageState
|
||||
if (querystring.ContainsKey("refresh"))
|
||||
{
|
||||
@ -173,11 +175,13 @@
|
||||
|
||||
if (PageState == null || refresh == UI.Refresh.Site)
|
||||
{
|
||||
languages = await LanguageService.GetLanguagesAsync(site.SiteId);
|
||||
pages = await PageService.GetPagesAsync(site.SiteId);
|
||||
pages = pages.Where(item => !item.IsDeleted).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
languages = PageState.Languages;
|
||||
pages = PageState.Pages;
|
||||
}
|
||||
|
||||
@ -230,6 +234,7 @@
|
||||
{
|
||||
Alias = SiteState.Alias,
|
||||
Site = site,
|
||||
Languages = languages,
|
||||
Pages = pages,
|
||||
Page = page,
|
||||
User = user,
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.1</Version>
|
||||
<Version>3.1.4</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.1</Version>
|
||||
<Version>3.1.4</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.1</Version>
|
||||
<Version>3.1.4</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -32,6 +32,21 @@ namespace Oqtane.Database.SqlServer
|
||||
return table.Column<int>(name: name, nullable: false).Annotation("SqlServer:Identity", "1, 1");
|
||||
}
|
||||
|
||||
public override void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index)
|
||||
{
|
||||
var elements = index.Split(':', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (elements.Length != 0)
|
||||
{
|
||||
builder.DropIndex(elements[0], table);
|
||||
}
|
||||
builder.AlterColumn<string>(name, table, maxLength: length, nullable: nullable, unicode: unicode);
|
||||
if (elements.Length != 0)
|
||||
{
|
||||
var columns = elements[1].Split(',');
|
||||
builder.CreateIndex(elements[0], table, columns, null, bool.Parse(elements[2]), null);
|
||||
}
|
||||
}
|
||||
|
||||
public override int ExecuteNonQuery(string connectionString, string query)
|
||||
{
|
||||
var conn = new SqlConnection(FormatConnectionString(connectionString));
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.1</Version>
|
||||
<Version>3.1.4</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -35,7 +35,7 @@ namespace Oqtane.Database.Sqlite
|
||||
// not implemented as SQLite does not support dropping columns
|
||||
}
|
||||
|
||||
public override void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode)
|
||||
public override void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index)
|
||||
{
|
||||
// not implemented as SQLite does not support altering columns
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.1.1/Oqtane.Framework.3.1.1.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.1.4/Oqtane.Framework.3.1.2.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.4</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.1.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.4.Install.zip" -Force
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.1.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.1.4.Upgrade.zip" -Force
|
@ -16,12 +16,14 @@ namespace Oqtane.Controllers
|
||||
public class AliasController : Controller
|
||||
{
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ITenantRepository _tenants;
|
||||
private readonly ILogManager _logger;
|
||||
private readonly Alias _alias;
|
||||
|
||||
public AliasController(IAliasRepository aliases, ILogManager logger, ITenantManager tenantManager)
|
||||
public AliasController(IAliasRepository aliases, ITenantRepository tenants, ILogManager logger, ITenantManager tenantManager)
|
||||
{
|
||||
_aliases = aliases;
|
||||
_tenants = tenants;
|
||||
_logger = logger;
|
||||
_alias = tenantManager.GetAlias();
|
||||
}
|
||||
@ -95,6 +97,13 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
_aliases.DeleteAlias(id);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Alias Deleted {AliasId}", id);
|
||||
|
||||
var aliases = _aliases.GetAliases();
|
||||
if (!aliases.Any(item => item.TenantId == alias.TenantId))
|
||||
{
|
||||
_tenants.DeleteTenant(alias.TenantId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Tenant Deleted {TenantId}", alias.TenantId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -255,7 +255,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url} {Error}", url, ex.Message);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "File Could Not Be Downloaded From Url {Url} {Error}", url, ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -276,9 +276,17 @@ namespace Oqtane.Controllers
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formfile.FileName.IsPathOrFileValid())
|
||||
// ensure filename is valid
|
||||
string token = ".part_";
|
||||
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check for allowable file extensions (ignore token)
|
||||
var extension = Path.GetExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))).Replace(".", "");
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -331,9 +339,9 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
string merged = "";
|
||||
|
||||
// parse the filename which is in the format of filename.ext.part_x_y
|
||||
// parse the filename which is in the format of filename.ext.part_001_999
|
||||
string token = ".part_";
|
||||
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "x_y"
|
||||
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "001_999"
|
||||
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
|
||||
|
||||
filename = Path.GetFileNameWithoutExtension(filename); // base filename
|
||||
@ -370,23 +378,15 @@ namespace Oqtane.Controllers
|
||||
System.IO.File.Delete(filepart);
|
||||
}
|
||||
|
||||
// check for allowable file extensions
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename)?.ToLower().Replace(".", "")))
|
||||
// remove file if it already exists
|
||||
if (System.IO.File.Exists(Path.Combine(folder, filename)))
|
||||
{
|
||||
System.IO.File.Delete(Path.Combine(folder, filename + ".tmp"));
|
||||
System.IO.File.Delete(Path.Combine(folder, filename));
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove file if it already exists
|
||||
if (System.IO.File.Exists(Path.Combine(folder, filename)))
|
||||
{
|
||||
System.IO.File.Delete(Path.Combine(folder, filename));
|
||||
}
|
||||
|
||||
// rename file now that the entire process is completed
|
||||
System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename));
|
||||
}
|
||||
// rename file now that the entire process is completed
|
||||
System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename));
|
||||
|
||||
merged = filename;
|
||||
}
|
||||
@ -394,8 +394,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed )
|
||||
var cleanupFiles = Directory.EnumerateFiles(folder, "*" + token + "*")
|
||||
.Where(f => Path.GetExtension(f).StartsWith(token));
|
||||
|
||||
.Where(f => Path.GetExtension(f).StartsWith(token) && !Path.GetFileName(f).StartsWith(filename));
|
||||
foreach (var file in cleanupFiles)
|
||||
{
|
||||
var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime();
|
||||
@ -601,9 +600,9 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, ex, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message);
|
||||
imagepath = "";
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oqtane.Enums;
|
||||
@ -7,6 +9,10 @@ using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -14,23 +20,40 @@ namespace Oqtane.Controllers
|
||||
public class LanguageController : Controller
|
||||
{
|
||||
private readonly ILanguageRepository _languages;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
private readonly Alias _alias;
|
||||
|
||||
public LanguageController(ILanguageRepository language, ILogManager logger, ITenantManager tenantManager)
|
||||
public LanguageController(ILanguageRepository language, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||
{
|
||||
_languages = language;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
_alias = tenantManager.GetAlias();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<Language> Get(string siteid)
|
||||
public IEnumerable<Language> Get(string siteid, string packagename)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
return _languages.GetLanguages(SiteId);
|
||||
if (string.IsNullOrEmpty(packagename))
|
||||
{
|
||||
packagename = "Oqtane";
|
||||
}
|
||||
var languages = _languages.GetLanguages(SiteId).ToList();
|
||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}.*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||
{
|
||||
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
||||
if (languages.Any(item => item.Code == code))
|
||||
{
|
||||
languages.Single(item => item.Code == code).Version = FileVersionInfo.GetVersionInfo(file).FileVersion;
|
||||
}
|
||||
}
|
||||
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
|
||||
languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !languages.Any(l => l.IsDefault) });
|
||||
return languages.OrderBy(item => item.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -63,6 +86,7 @@ namespace Oqtane.Controllers
|
||||
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
||||
{
|
||||
language = _languages.AddLanguage(language);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
||||
}
|
||||
else
|
||||
@ -82,6 +106,7 @@ namespace Oqtane.Controllers
|
||||
if (language != null && language.SiteId == _alias.SiteId)
|
||||
{
|
||||
_languages.DeleteLanguage(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
||||
}
|
||||
else
|
||||
@ -89,7 +114,6 @@ namespace Oqtane.Controllers
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Delete Attempt {LanguageId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,15 @@ namespace Oqtane.Controllers
|
||||
// GET: api/localization
|
||||
[HttpGet()]
|
||||
public IEnumerable<Culture> Get()
|
||||
=> _localizationManager.GetSupportedCultures().Select(c => new Culture {
|
||||
Name = CultureInfo.GetCultureInfo(c).Name,
|
||||
DisplayName = CultureInfo.GetCultureInfo(c).DisplayName,
|
||||
IsDefault = _localizationManager.GetDefaultCulture()
|
||||
{
|
||||
var cultures = _localizationManager.GetSupportedCultures().Select(c => new Culture
|
||||
{
|
||||
Name = CultureInfo.GetCultureInfo(c).Name,
|
||||
DisplayName = CultureInfo.GetCultureInfo(c).DisplayName,
|
||||
IsDefault = _localizationManager.GetDefaultCulture()
|
||||
.Equals(CultureInfo.GetCultureInfo(c).Name, StringComparison.OrdinalIgnoreCase)
|
||||
});
|
||||
});
|
||||
return cultures.OrderBy(item => item.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ namespace Oqtane.Controllers
|
||||
module.CreatedOn = pagemodule.Module.CreatedOn;
|
||||
module.ModifiedBy = pagemodule.Module.ModifiedBy;
|
||||
module.ModifiedOn = pagemodule.Module.ModifiedOn;
|
||||
module.DeletedBy = pagemodule.DeletedBy;
|
||||
module.DeletedOn = pagemodule.DeletedOn;
|
||||
module.IsDeleted = pagemodule.IsDeleted;
|
||||
|
||||
module.PageModuleId = pagemodule.PageModuleId;
|
||||
@ -139,24 +141,41 @@ namespace Oqtane.Controllers
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public Module Put(int id, [FromBody] Module module)
|
||||
{
|
||||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _modules.GetModule(module.ModuleId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||
var _module = _modules.GetModule(module.ModuleId, false);
|
||||
|
||||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _module != null && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||
{
|
||||
module = _modules.UpdateModule(module);
|
||||
if (module.AllPages)
|
||||
{
|
||||
var pageModule = _pageModules.GetPageModules(module.SiteId).FirstOrDefault(item => item.ModuleId == module.ModuleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
||||
|
||||
var pages = _pages.GetPages(module.SiteId).ToList();
|
||||
foreach (Page page in pages)
|
||||
if (_module.AllPages != module.AllPages)
|
||||
{
|
||||
var pageModules = _pageModules.GetPageModules(module.SiteId).ToList();
|
||||
if (module.AllPages)
|
||||
{
|
||||
if (page.PageId != pageModule.PageId && !page.Path.StartsWith("admin/"))
|
||||
var pageModule = _pageModules.GetPageModule(module.PageModuleId);
|
||||
var pages = _pages.GetPages(module.SiteId).ToList();
|
||||
foreach (Page page in pages)
|
||||
{
|
||||
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
|
||||
if (!pageModules.Exists(item => item.ModuleId == module.ModuleId && item.PageId == page.PageId) && !page.Path.StartsWith("admin/"))
|
||||
{
|
||||
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var pageModule in pageModules)
|
||||
{
|
||||
if (pageModule.ModuleId == module.ModuleId && pageModule.PageModuleId != module.PageModuleId)
|
||||
{
|
||||
_pageModules.DeletePageModule(pageModule.PageModuleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -307,9 +307,9 @@ namespace Oqtane.Controllers
|
||||
if (moduleDefinition.Version == "local")
|
||||
{
|
||||
text = text.Replace("[FrameworkVersion]", Constants.Version);
|
||||
text = text.Replace("[ClientReference]", "<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Client.dll</HintPath></Reference>");
|
||||
text = text.Replace("[ServerReference]", "<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Server.dll</HintPath></Reference>");
|
||||
text = text.Replace("[SharedReference]", "<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Shared.dll</HintPath></Reference>");
|
||||
text = text.Replace("[ClientReference]", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Client.dll</HintPath></Reference>");
|
||||
text = text.Replace("[ServerReference]", $"<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Server.dll</HintPath></Reference>");
|
||||
text = text.Replace("[SharedReference]", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Shared.dll</HintPath></Reference>");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ namespace Oqtane.Controllers
|
||||
catch (Exception ex)
|
||||
{
|
||||
results.Add(new Dictionary<string, string>() { { "Error", ex.Message } });
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Other, ex, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
|
||||
}
|
||||
sqlquery.Results = results;
|
||||
return sqlquery;
|
||||
|
@ -38,6 +38,7 @@ namespace Oqtane.Controllers
|
||||
systeminfo.Add("TickCount", Environment.TickCount64.ToString());
|
||||
systeminfo.Add("ContentRootPath", _environment.ContentRootPath);
|
||||
systeminfo.Add("WebRootPath", _environment.WebRootPath);
|
||||
systeminfo.Add("Environment", _environment.EnvironmentName);
|
||||
systeminfo.Add("ServerTime", DateTime.UtcNow.ToString());
|
||||
var feature = HttpContext.Features.Get<IHttpConnectionFeature>();
|
||||
systeminfo.Add("IPAddress", feature?.LocalIpAddress?.ToString());
|
||||
|
@ -188,8 +188,8 @@ namespace Oqtane.Controllers
|
||||
if (theme.Version == "local")
|
||||
{
|
||||
text = text.Replace("[FrameworkVersion]", Constants.Version);
|
||||
text = text.Replace("[ClientReference]", "<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Client.dll</HintPath></Reference>");
|
||||
text = text.Replace("[SharedReference]", "<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Shared.dll</HintPath></Reference>");
|
||||
text = text.Replace("[ClientReference]", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Client.dll</HintPath></Reference>");
|
||||
text = text.Replace("[SharedReference]", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Shared.dll</HintPath></Reference>");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -327,6 +327,8 @@ namespace Oqtane.Controllers
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var LastIPAddress = user.LastIPAddress ?? "";
|
||||
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user.TwoFactorRequired)
|
||||
{
|
||||
@ -353,7 +355,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
loginUser.IsAuthenticated = true;
|
||||
loginUser.LastLoginOn = DateTime.UtcNow;
|
||||
loginUser.LastIPAddress = HttpContext.Connection.RemoteIpAddress.ToString();
|
||||
loginUser.LastIPAddress = LastIPAddress;
|
||||
_users.UpdateUser(loginUser);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
||||
}
|
||||
|
@ -39,32 +39,24 @@ namespace Oqtane.Controllers
|
||||
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && (userid != null || rolename != null))
|
||||
{
|
||||
int UserId = (int.TryParse(userid, out UserId)) ? UserId : -1;
|
||||
if (User.IsInRole(RoleNames.Admin) || ((userid == null || _userPermissions.GetUser().UserId == UserId) && (rolename == null || (User.IsInRole(rolename) && rolename != RoleNames.Registered))))
|
||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||
if (userid != null)
|
||||
{
|
||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||
if (userid != null)
|
||||
{
|
||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||
}
|
||||
if (rolename != null)
|
||||
{
|
||||
userroles = userroles.Where(item => item.Role.Name == rolename).ToList();
|
||||
}
|
||||
for (int i = 0; i < userroles.Count(); i++)
|
||||
{
|
||||
userroles[i] = Filter(userroles[i]);
|
||||
}
|
||||
return userroles.OrderBy(u => u.User.DisplayName);
|
||||
int UserId = int.TryParse(userid, out UserId) ? UserId : -1;
|
||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||
}
|
||||
else
|
||||
if (rolename != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt For Site {SiteId} User {UserId} Role {RoleName}", siteid, userid, rolename);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
userroles = userroles.Where(item => item.Role.Name == rolename).ToList();
|
||||
}
|
||||
var user = _userPermissions.GetUser();
|
||||
for (int i = 0; i < userroles.Count(); i++)
|
||||
{
|
||||
userroles[i] = Filter(userroles[i], user.UserId);
|
||||
}
|
||||
return userroles.OrderBy(u => u.User.DisplayName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -82,16 +74,7 @@ namespace Oqtane.Controllers
|
||||
var userrole = _userRoles.GetUserRole(id);
|
||||
if (userrole != null && SiteValid(userrole.Role.SiteId))
|
||||
{
|
||||
if (User.IsInRole(RoleNames.Admin) || User.Identity.Name?.ToLower() != userrole.User.Username.ToLower() || User.IsInRole(userrole.Role.Name))
|
||||
{
|
||||
return Filter(userrole);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
return Filter(userrole, _userPermissions.GetUser().UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -101,7 +84,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private UserRole Filter(UserRole userrole)
|
||||
private UserRole Filter(UserRole userrole, int userid)
|
||||
{
|
||||
if (userrole != null)
|
||||
{
|
||||
@ -110,7 +93,7 @@ namespace Oqtane.Controllers
|
||||
userrole.User.TwoFactorCode = "";
|
||||
userrole.User.TwoFactorExpiry = null;
|
||||
|
||||
if (!User.IsInRole(RoleNames.Admin) && User.Identity.Name?.ToLower() != userrole.User.Username.ToLower())
|
||||
if (!User.IsInRole(RoleNames.Admin) && userid != userrole.User.UserId)
|
||||
{
|
||||
userrole.User.Email = "";
|
||||
userrole.User.PhotoFileId = null;
|
||||
|
@ -81,7 +81,7 @@ namespace Oqtane.Databases
|
||||
builder.DropColumn(name, table);
|
||||
}
|
||||
|
||||
public virtual void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode)
|
||||
public virtual void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index)
|
||||
{
|
||||
builder.AlterColumn<string>(RewriteName(name), RewriteName(table), maxLength: length, nullable: nullable, unicode: unicode);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Oqtane.Databases.Interfaces
|
||||
|
||||
public void DropColumn(MigrationBuilder builder, string name, string table);
|
||||
|
||||
public void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode);
|
||||
public void AlterStringColumn(MigrationBuilder builder, string name, string table, int length, bool nullable, bool unicode, string index);
|
||||
|
||||
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
@ -17,6 +18,7 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
@ -248,7 +250,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// dynamically register module services, contexts, and repository classes
|
||||
// dynamically register module scoped services (ie. client service classes)
|
||||
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
@ -259,6 +261,17 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
}
|
||||
}
|
||||
|
||||
// dynamically register module transient services (ie. server DBContext, repository classes)
|
||||
implementationTypes = assembly.GetInterfaces<ITransientService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
if (implementationType.AssemblyQualifiedName != null)
|
||||
{
|
||||
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
|
||||
services.AddTransient(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
// dynamically register hosted services
|
||||
var serviceTypes = assembly.GetTypes(hostedServiceType);
|
||||
foreach (var serviceType in serviceTypes)
|
||||
@ -315,52 +328,26 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
|
||||
private static void LoadSatelliteAssemblies(string[] supportedCultures)
|
||||
{
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
if (assemblyPath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AssemblyLoadContext.Default.Resolving += ResolveDependencies;
|
||||
|
||||
foreach (var culture in supportedCultures)
|
||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||
{
|
||||
if (culture == Constants.DefaultCulture)
|
||||
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
||||
if (supportedCultures.Contains(code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var assembliesFolder = new DirectoryInfo(Path.Combine(assemblyPath, culture));
|
||||
if (assembliesFolder.Exists)
|
||||
{
|
||||
foreach (var assemblyFile in assembliesFolder.EnumerateFiles($"*{Constants.SatelliteAssemblyExtension}"))
|
||||
try
|
||||
{
|
||||
AssemblyName assemblyName;
|
||||
try
|
||||
{
|
||||
assemblyName = AssemblyName.GetAssemblyName(assemblyFile.FullName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.WriteLine($"Oqtane Error: Cannot Get Satellite Assembly Name For {assemblyFile.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(System.IO.File.ReadAllBytes(assemblyFile.FullName)));
|
||||
Debug.WriteLine($"Oqtane Info: Loaded Assembly {assemblyName}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Oqtane Error: Unable To Load Assembly {assemblyName} - {ex}");
|
||||
}
|
||||
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(System.IO.File.ReadAllBytes(file)));
|
||||
Debug.WriteLine($"Oqtane Info: Loaded Satellite Assembly {file}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Oqtane Error: Unable To Load Satellite Assembly {file} - {ex}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"Oqtane Error: The Satellite Assembly Folder For {culture} Does Not Exist");
|
||||
Debug.WriteLine($"Oqtane Error: Culture Not Supported For Satellite Assembly {file}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,20 @@ namespace Oqtane.Extensions
|
||||
options.Events.OnTokenValidated = OnTokenValidated;
|
||||
options.Events.OnAccessDenied = OnAccessDenied;
|
||||
options.Events.OnRemoteFailure = OnRemoteFailure;
|
||||
if (sitesettings.GetValue("ExternalLogin:Parameters", "") != "")
|
||||
{
|
||||
options.Events = new OpenIdConnectEvents
|
||||
{
|
||||
OnRedirectToIdentityProvider = context =>
|
||||
{
|
||||
foreach(var parameter in sitesettings.GetValue("ExternalLogin:Parameters", "").Split(","))
|
||||
{
|
||||
context.ProtocolMessage.SetParameter(parameter.Split("=")[0], parameter.Split("=")[1]);
|
||||
}
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -100,6 +114,22 @@ namespace Oqtane.Extensions
|
||||
options.Events.OnTicketReceived = OnTicketReceived;
|
||||
options.Events.OnAccessDenied = OnAccessDenied;
|
||||
options.Events.OnRemoteFailure = OnRemoteFailure;
|
||||
if (sitesettings.GetValue("ExternalLogin:Parameters", "") != "")
|
||||
{
|
||||
options.Events = new OAuthEvents
|
||||
{
|
||||
OnRedirectToAuthorizationEndpoint = context =>
|
||||
{
|
||||
var url = context.RedirectUri;
|
||||
foreach (var parameter in sitesettings.GetValue("ExternalLogin:Parameters", "").Split(","))
|
||||
{
|
||||
url += (!url.Contains("?")) ? "?" + parameter : "&" + parameter;
|
||||
}
|
||||
context.Response.Redirect(url);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -111,6 +141,7 @@ namespace Oqtane.Extensions
|
||||
// OAuth 2.0
|
||||
var email = "";
|
||||
var id = "";
|
||||
var claims = "";
|
||||
|
||||
if (context.Options.UserInformationEndpoint != "")
|
||||
{
|
||||
@ -123,16 +154,16 @@ namespace Oqtane.Extensions
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
|
||||
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var output = await response.Content.ReadAsStringAsync();
|
||||
claims = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// parse json output
|
||||
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||
if (!output.StartsWith("[") && !output.EndsWith("]"))
|
||||
if (!claims.StartsWith("[") && !claims.EndsWith("]"))
|
||||
{
|
||||
output = "[" + output + "]"; // convert to json array
|
||||
claims = "[" + claims + "]"; // convert to json array
|
||||
}
|
||||
JsonNode items = JsonNode.Parse(output)!;
|
||||
JsonNode items = JsonNode.Parse(claims)!;
|
||||
foreach (var item in items.AsArray())
|
||||
{
|
||||
if (item[emailClaimType] != null)
|
||||
@ -156,12 +187,12 @@ namespace Oqtane.Extensions
|
||||
catch (Exception ex)
|
||||
{
|
||||
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "An Error Occurred Accessing The User Info Endpoint - {Error}", ex.Message);
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, ex, "An Error Occurred Accessing The User Info Endpoint - {Error}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// validate user
|
||||
var identity = await ValidateUser(email, id, context.HttpContext);
|
||||
var identity = await ValidateUser(email, id, claims, context.HttpContext);
|
||||
if (identity.Label == ExternalLoginStatus.Success)
|
||||
{
|
||||
identity.AddClaim(new Claim("access_token", context.AccessToken));
|
||||
@ -193,9 +224,10 @@ namespace Oqtane.Extensions
|
||||
var id = context.Principal.FindFirstValue(idClaimType);
|
||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||
var email = context.Principal.FindFirstValue(emailClaimType);
|
||||
var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray());
|
||||
|
||||
// validate user
|
||||
var identity = await ValidateUser(email, id, context.HttpContext);
|
||||
var identity = await ValidateUser(email, id, claims, context.HttpContext);
|
||||
if (identity.Label == ExternalLoginStatus.Success)
|
||||
{
|
||||
identity.AddClaim(new Claim("access_token", context.SecurityToken.RawData));
|
||||
@ -229,7 +261,7 @@ namespace Oqtane.Extensions
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, HttpContext httpContext)
|
||||
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext)
|
||||
{
|
||||
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
|
||||
@ -241,144 +273,151 @@ namespace Oqtane.Extensions
|
||||
var _users = httpContext.RequestServices.GetRequiredService<IUserRepository>();
|
||||
User user = null;
|
||||
|
||||
// verify if external user is already registerd for this site
|
||||
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
||||
var identityuser = await _identityUserManager.FindByLoginAsync(providerType + ":" + alias.SiteId.ToString(), id);
|
||||
if (identityuser != null)
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||
// verify if external user is already registered for this site
|
||||
var _identityUserManager = httpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
|
||||
var identityuser = await _identityUserManager.FindByLoginAsync(providerType + ":" + alias.SiteId.ToString(), id);
|
||||
if (identityuser != null)
|
||||
{
|
||||
bool duplicates = false;
|
||||
try
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||
{
|
||||
identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// FindByEmailAsync will throw an error if the email matches multiple user accounts
|
||||
duplicates = true;
|
||||
}
|
||||
if (identityuser == null)
|
||||
{
|
||||
if (duplicates)
|
||||
bool duplicates = false;
|
||||
try
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.DuplicateEmail;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
||||
identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
|
||||
// FindByEmailAsync will throw an error if the email matches multiple user accounts
|
||||
duplicates = true;
|
||||
}
|
||||
if (identityuser == null)
|
||||
{
|
||||
if (duplicates)
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = email;
|
||||
identityuser.Email = email;
|
||||
identityuser.EmailConfirmed = true;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss"));
|
||||
if (result.Succeeded)
|
||||
identity.Label = ExternalLoginStatus.DuplicateEmail;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
|
||||
{
|
||||
user = new User
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = email;
|
||||
identityuser.Email = email;
|
||||
identityuser.EmailConfirmed = true;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss"));
|
||||
if (result.Succeeded)
|
||||
{
|
||||
SiteId = alias.SiteId,
|
||||
Username = email,
|
||||
DisplayName = email,
|
||||
Email = email,
|
||||
LastLoginOn = null,
|
||||
LastIPAddress = ""
|
||||
};
|
||||
user = _users.AddUser(user);
|
||||
user = new User
|
||||
{
|
||||
SiteId = alias.SiteId,
|
||||
Username = email,
|
||||
DisplayName = email,
|
||||
Email = email,
|
||||
LastLoginOn = null,
|
||||
LastIPAddress = ""
|
||||
};
|
||||
user = _users.AddUser(user);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
if (user != null)
|
||||
{
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + alias.SiteId.ToString(), id, providerName));
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + alias.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
||||
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
||||
identity.Label = ExternalLoginStatus.UserDoesNotExist;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.UserDoesNotExist;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||
if (login == null)
|
||||
{
|
||||
// new external login using existing user account - verification required
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(id)}";
|
||||
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
||||
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
identity.Label = ExternalLoginStatus.VerificationRequired;
|
||||
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
||||
}
|
||||
else
|
||||
{
|
||||
// provider keys do not match
|
||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||
if (login == null)
|
||||
{
|
||||
// new external login using existing user account - verification required
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(id)}";
|
||||
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
||||
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
identity.Label = ExternalLoginStatus.VerificationRequired;
|
||||
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
||||
}
|
||||
else
|
||||
{
|
||||
// provider keys do not match
|
||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // email invalid
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.InvalidEmail;
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email Address To Uniquely Identify The User. The Email Claim Specified Was {EmailCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", ""), claims);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // email invalid
|
||||
|
||||
// manage user
|
||||
if (user != null)
|
||||
{
|
||||
identity.Label = ExternalLoginStatus.InvalidEmail;
|
||||
if (!string.IsNullOrEmpty(email))
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email To Uniquely Identify The User.");
|
||||
}
|
||||
// create claims identity
|
||||
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
||||
identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList());
|
||||
identity.Label = ExternalLoginStatus.Success;
|
||||
|
||||
// update user
|
||||
user.LastLoginOn = DateTime.UtcNow;
|
||||
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
|
||||
_users.UpdateUser(user);
|
||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
||||
}
|
||||
}
|
||||
|
||||
// manage user
|
||||
if (user != null)
|
||||
else // id invalid
|
||||
{
|
||||
// create claims identity
|
||||
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
||||
identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList());
|
||||
identity.Label = ExternalLoginStatus.Success;
|
||||
|
||||
// update user
|
||||
user.LastLoginOn = DateTime.UtcNow;
|
||||
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
|
||||
_users.UpdateUser(user);
|
||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Identifier To Uniquely Identify The User. The Identifier Claim Specified Was {IdentifierCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", ""), claims);
|
||||
}
|
||||
|
||||
|
||||
return identity;
|
||||
}
|
||||
|
||||
|
@ -48,26 +48,35 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
|
||||
// move packages to secure /Packages folder
|
||||
foreach (var folder in "Modules,Themes,Packages".Split(","))
|
||||
foreach (var folderName in "Modules,Themes,Packages".Split(","))
|
||||
{
|
||||
foreach(var file in Directory.GetFiles(Path.Combine(webRootPath, folder), "*.nupkg*"))
|
||||
string folder = Path.Combine(webRootPath, folderName);
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
var destinationFile = Path.Combine(sourceFolder, Path.GetFileName(file));
|
||||
if (File.Exists(destinationFile))
|
||||
foreach (var file in Directory.GetFiles(folder, "*.nupkg*"))
|
||||
{
|
||||
File.Delete(destinationFile);
|
||||
}
|
||||
if (destinationFile.ToLower().EndsWith(".nupkg.bak"))
|
||||
{
|
||||
// leave a copy in the current folder as it is distributed with the core framework
|
||||
File.Copy(file, destinationFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to destination
|
||||
File.Move(file, destinationFile);
|
||||
var destinationFile = Path.Combine(sourceFolder, Path.GetFileName(file));
|
||||
if (File.Exists(destinationFile))
|
||||
{
|
||||
File.Delete(destinationFile);
|
||||
}
|
||||
|
||||
if (destinationFile.ToLower().EndsWith(".nupkg.bak"))
|
||||
{
|
||||
// leave a copy in the current folder as it is distributed with the core framework
|
||||
File.Copy(file, destinationFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to destination
|
||||
File.Move(file, destinationFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
}
|
||||
|
||||
// iterate through Nuget packages in source folder
|
||||
@ -200,7 +209,7 @@ namespace Oqtane.Infrastructure
|
||||
string[] packages = Directory.GetFiles(Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder), PackageName + "*.log");
|
||||
if (packages.Length > 0)
|
||||
{
|
||||
packagename = packages[packages.Length - 1]; // use highest version
|
||||
packagename = packages[packages.Length - 1]; // use highest version
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(packagename))
|
||||
@ -245,7 +254,7 @@ namespace Oqtane.Infrastructure
|
||||
string[] packages = Directory.GetFiles(folder, Constants.PackageId + ".*.nupkg");
|
||||
if (packages.Length > 0)
|
||||
{
|
||||
packagename = packages[packages.Length - 1]; // use highest version
|
||||
packagename = packages[packages.Length - 1]; // use highest version
|
||||
}
|
||||
|
||||
if (packagename != "")
|
||||
|
@ -120,6 +120,10 @@ namespace Oqtane.Infrastructure
|
||||
mailMessage.Body += "Subject: " + notification.Subject + "\n\n";
|
||||
mailMessage.Body += notification.Body;
|
||||
|
||||
// set encoding
|
||||
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
|
||||
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
|
||||
|
||||
// send mail
|
||||
try
|
||||
{
|
||||
|
@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Oqtane.Shared;
|
||||
|
||||
@ -11,7 +8,6 @@ namespace Oqtane.Infrastructure
|
||||
public class LocalizationManager : ILocalizationManager
|
||||
{
|
||||
private static readonly string DefaultCulture = Constants.DefaultCulture;
|
||||
private static readonly string[] DefaultSupportedCultures = new[] { DefaultCulture };
|
||||
|
||||
private readonly LocalizationOptions _localizationOptions;
|
||||
|
||||
@ -21,19 +17,20 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
|
||||
public string GetDefaultCulture()
|
||||
=> String.IsNullOrEmpty(_localizationOptions.DefaultCulture)
|
||||
? DefaultCulture
|
||||
: _localizationOptions.DefaultCulture;
|
||||
{
|
||||
if (string.IsNullOrEmpty(_localizationOptions.DefaultCulture))
|
||||
{
|
||||
return DefaultCulture;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _localizationOptions.DefaultCulture;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] GetSupportedCultures()
|
||||
{
|
||||
var cultures = new List<string>(DefaultSupportedCultures);
|
||||
foreach(var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Oqtane.Client.resources.dll", SearchOption.AllDirectories))
|
||||
{
|
||||
cultures.Add(Path.GetFileName(Path.GetDirectoryName(file)));
|
||||
}
|
||||
|
||||
return cultures.OrderBy(c => c).ToArray();
|
||||
return CultureInfo.GetCultures(CultureTypes.AllCultures).Select(item => item.Name).OrderBy(c => c).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,12 +203,15 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
if (Enum.Parse<LogLevel>(log.Level) >= notifylevel)
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
foreach (var userrole in _userRoles.GetUserRoles(log.SiteId.Value))
|
||||
{
|
||||
if (userrole.Role.Name == RoleNames.Host)
|
||||
{
|
||||
var url = _accessor.HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/admin/log";
|
||||
var notification = new Notification(log.SiteId.Value, userrole.User, "Site " + log.Level + " Notification", "Please visit " + url + " for more information");
|
||||
var subject = $"{alias.Name} Site {log.Level} Notification";
|
||||
var url = $"{_accessor.HttpContext.Request.Scheme}://{alias.Name}/admin/log?id={log.LogId}";
|
||||
string body = $"Log Message: {log.Message}\n\nPlease visit {url} for more information";
|
||||
var notification = new Notification(log.SiteId.Value, userrole.User, subject, body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ namespace Oqtane.SiteTemplates
|
||||
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Blazor Server uses SignalR to host your application on a web server and provide a responsive and robust development experience. Blazor applications work in all modern web browsers, including mobile browsers.</p>" +
|
||||
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">.NET Core</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
|
||||
},
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content",
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Admin,
|
||||
ModulePermissions = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
|
@ -1,7 +1,5 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
@ -50,6 +48,12 @@ namespace Oqtane.Infrastructure
|
||||
case "3.0.1":
|
||||
Upgrade_3_0_1(tenant, scope);
|
||||
break;
|
||||
case "3.1.3":
|
||||
Upgrade_3_1_3(tenant, scope);
|
||||
break;
|
||||
case "3.1.4":
|
||||
Upgrade_3_1_4(tenant, scope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,5 +186,57 @@ namespace Oqtane.Infrastructure
|
||||
sites.CreatePages(site, pageTemplates);
|
||||
}
|
||||
}
|
||||
|
||||
private void Upgrade_3_1_3(Tenant tenant, IServiceScope scope)
|
||||
{
|
||||
var roles = scope.ServiceProvider.GetRequiredService<IRoleRepository>();
|
||||
if (!roles.GetRoles(-1, true).ToList().Where(item => item.Name == RoleNames.Unauthenticated).Any())
|
||||
{
|
||||
roles.AddRole(new Role { SiteId = null, Name = RoleNames.Unauthenticated, Description = RoleNames.Unauthenticated, IsAutoAssigned = false, IsSystem = true });
|
||||
}
|
||||
}
|
||||
|
||||
private void Upgrade_3_1_4(Tenant tenant, IServiceScope scope)
|
||||
{
|
||||
var pageTemplates = new List<PageTemplate>();
|
||||
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Not Found",
|
||||
Parent = "",
|
||||
Path = "404",
|
||||
Icon = Icons.X,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PagePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
}.EncodePermissions(),
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Not Found", Pane = PaneNames.Admin,
|
||||
ModulePermissions = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
}.EncodePermissions(),
|
||||
Content = "<p>The page you requested does not exist.</p>"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var pages = scope.ServiceProvider.GetRequiredService<IPageRepository>();
|
||||
|
||||
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
|
||||
foreach (Site site in sites.GetSites().ToList())
|
||||
{
|
||||
if (!pages.GetPages(site.SiteId).ToList().Where(item => item.Path == "404").Any())
|
||||
{
|
||||
sites.CreatePages(site, pageTemplates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user