@ -1,141 +0,0 @@
|
||||
@namespace Oqtane.Modules.Admin.Jobs
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IJobService JobService
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="Enter the job name" ResourceKey="Name">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="name" class="form-control" @bind="@_name" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="type" HelpText="Enter the job type" ResourceKey="Type">Type: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="type" class="form-control" @bind="@_jobType" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="enabled" HelpText="Select whether you want the job enabled or not" ResourceKey="Enabled">Enabled? </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="enabled" class="form-control" @bind="@_isEnabled">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="runs-every" class="form-control" @bind="@_interval" />
|
||||
<select id="runs-every" class="form-control" @bind="@_frequency">
|
||||
<option value="m">@Localizer["Minute(s)"]</option>
|
||||
<option value="H">@Localizer["Hour(s)"]</option>
|
||||
<option value="d">@Localizer["Day(s)"]</option>
|
||||
<option value="M">@Localizer["Month(s)"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="starting" HelpText="What time do you want the job to start" ResourceKey="Starting">Starting: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="starting" class="form-control" @bind="@_startDate" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="ending" HelpText="When do you want the job to end" ResourceKey="Ending">Ending: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="ending" class="form-control" @bind="@_endDate" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="retention-log" HelpText="What items do you want in the retention log" ResourceKey="RetentionLog">Retention Log (Items): </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="retention-log" class="form-control" @bind="@_retentionHistory" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveJob">@Localizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private string _name = string.Empty;
|
||||
private string _jobType = string.Empty;
|
||||
private string _isEnabled = "True";
|
||||
private string _interval = string.Empty;
|
||||
private string _frequency = string.Empty;
|
||||
private string _startDate = string.Empty;
|
||||
private string _endDate = string.Empty;
|
||||
private string _retentionHistory = "10";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
private async Task SaveJob()
|
||||
{
|
||||
if (_name != string.Empty && !string.IsNullOrEmpty(_jobType) && _frequency != string.Empty && _interval != string.Empty && _retentionHistory != string.Empty)
|
||||
{
|
||||
var job = new Job();
|
||||
job.Name = _name;
|
||||
job.JobType = _jobType;
|
||||
job.IsEnabled = Boolean.Parse(_isEnabled);
|
||||
job.Frequency = _frequency;
|
||||
job.Interval = int.Parse(_interval);
|
||||
|
||||
if (_startDate == string.Empty)
|
||||
{
|
||||
job.StartDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.StartDate = DateTime.Parse(_startDate);
|
||||
}
|
||||
|
||||
if (_endDate == string.Empty)
|
||||
{
|
||||
job.EndDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.EndDate = DateTime.Parse(_endDate);
|
||||
}
|
||||
|
||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||
job.IsStarted = false;
|
||||
job.IsExecuting = false;
|
||||
job.NextExecution = null;
|
||||
|
||||
try
|
||||
{
|
||||
job = await JobService.AddJobAsync(job);
|
||||
await logger.LogInformation("Job Added {Job}", job);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Adding Job {Job} {Error}", job, ex.Message);
|
||||
AddModuleMessage(Localizer["Error Adding Job"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["You Must Provide The Job Name, Type, Frequency, and Retention"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -15,10 +15,10 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="type" HelpText="Enter the job type" ResourceKey="Type">Type: </Label>
|
||||
<Label For="type" HelpText="The fully qualified job type name" ResourceKey="Type">Type: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="type" class="form-control" @bind="@_jobType" />
|
||||
<input id="type" class="form-control" @bind="@_jobType" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -9,7 +9,6 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Job" ResourceKey="AddJob" />
|
||||
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" ResourceKey="ViewJobs" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">Refresh</button>
|
||||
<br />
|
||||
|
107
Oqtane.Client/Modules/Admin/Languages/Add.razor
Normal file
107
Oqtane.Client/Modules/Admin/Languages/Add.razor
Normal file
@ -0,0 +1,107 @@
|
||||
@namespace Oqtane.Modules.Admin.Languages
|
||||
@inherits ModuleBase
|
||||
@using System.Globalization
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ILocalizationService LocalizationService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
|
||||
@if (_supportedCultures == null)
|
||||
{
|
||||
<p><em>@Localizer["Loading..."]</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (_supportedCultures?.Count() > 1)
|
||||
{
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="Name Of The Langauage" ResourceKey="Name">Name:</Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="_code" class="form-control" @bind="@_code">
|
||||
@foreach (var culture in _supportedCultures)
|
||||
{
|
||||
<option value="@culture.Name">@culture.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="default" class="form-control" @bind="@_isDefault">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@Localizer["Save"]</button>
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _code = string.Empty;
|
||||
private string _isDefault = "False";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
private IEnumerable<Culture> _supportedCultures;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
||||
if (_supportedCultures.Count() <= 1)
|
||||
{
|
||||
AddModuleMessage(Localizer["The Only Supported Culture That Has Been Defined Is English"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveLanguage()
|
||||
{
|
||||
var language = new Language
|
||||
{
|
||||
SiteId = PageState.Page.SiteId,
|
||||
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
||||
Code = _code,
|
||||
IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault))
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
language = await LanguageService.AddLanguageAsync(language);
|
||||
|
||||
if (language.IsDefault)
|
||||
{
|
||||
await SetCultureAsync(language.Code);
|
||||
}
|
||||
|
||||
await logger.LogInformation("Language Added {Language}", language);
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message);
|
||||
AddModuleMessage(Localizer["Error Adding Language"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetCultureAsync(string culture)
|
||||
{
|
||||
if (culture != CultureInfo.CurrentUICulture.Name)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
|
||||
}
|
||||
}
|
||||
}
|
56
Oqtane.Client/Modules/Admin/Languages/Index.razor
Normal file
56
Oqtane.Client/Modules/Admin/Languages/Index.razor
Normal file
@ -0,0 +1,56 @@
|
||||
@namespace Oqtane.Modules.Admin.Languages
|
||||
@inherits ModuleBase
|
||||
@inject ILanguageService LanguageService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@if (_languages == null)
|
||||
{
|
||||
<p><em>@Localizer["Loading..."]</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Language" ResourceKey="AddLanguage" />
|
||||
|
||||
<Pager Items="@_languages">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["Name"]</th>
|
||||
<th>@Localizer["Code"]</th>
|
||||
<th>@Localizer["Default?"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionDialog Header="Delete Langauge" Message="@Localizer["Are You Sure You Wish To Delete The {0} Language?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@(context.IsDefault)" ResourceKey="DeleteLanguage" /></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Code</td>
|
||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Language> _languages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||
}
|
||||
|
||||
private async Task DeleteLanguage(Language language)
|
||||
{
|
||||
try
|
||||
{
|
||||
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
||||
await logger.LogInformation("Language Deleted {Language}", language);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Language {Language} {Error}", language, ex.Message);
|
||||
|
||||
AddModuleMessage(Localizer["Error Deleting Language"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@using System.Text.RegularExpressions
|
||||
@using System.IO;
|
||||
|
||||
@if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||
{
|
||||
@ -113,7 +114,7 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsValid(_owner) && IsValid(_module) && _template != "-")
|
||||
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);
|
||||
@ -126,7 +127,7 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name, Module Name, And Template"], MessageType.Warning);
|
||||
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -171,14 +172,18 @@ else
|
||||
Dictionary<string, string> systeminfo = await SystemService.GetSystemInfoAsync();
|
||||
if (systeminfo != null)
|
||||
{
|
||||
string[] path = systeminfo["serverpath"].Split('\\');
|
||||
string[] path = systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
|
||||
if (_template == "internal")
|
||||
{
|
||||
_location = string.Join("\\", path, 0, path.Length - 1) + "\\Oqtane.Client\\Modules\\" + _owner + "." + _module;
|
||||
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 1) +
|
||||
Path.DirectorySeparatorChar + "Oqtane.Client" +
|
||||
Path.DirectorySeparatorChar + "Modules" +
|
||||
Path.DirectorySeparatorChar + _owner + "." + _module;
|
||||
}
|
||||
else
|
||||
{
|
||||
_location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module;
|
||||
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
|
||||
Path.DirectorySeparatorChar + _owner + "." + _module;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="url" HelpText="The reference url of the module" resource="ReferenceUrl">Reference Url: </Label>
|
||||
<Label For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="url" class="form-control" @bind="@_url" disabled />
|
||||
|
@ -47,7 +47,7 @@ else
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -100,7 +100,8 @@ else
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||
AddModuleMessage(Localizer["Module Deleted Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
|
||||
AddModuleMessage(Localizer["Module Deleted Successfully"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Admin.Modules
|
||||
@using Oqtane.Interfaces
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IThemeService ThemeService
|
||||
@ -162,15 +163,16 @@
|
||||
module.Permissions = _permissionGrid.GetPermissions();
|
||||
await ModuleService.UpdateModuleAsync(module);
|
||||
|
||||
if (_settingsModuleType != null)
|
||||
{
|
||||
var moduleType = Type.GetType(ModuleState.ModuleType);
|
||||
if (moduleType != null)
|
||||
{
|
||||
moduleType.GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component
|
||||
}
|
||||
}
|
||||
|
||||
if (_settings is ISettingsControl control)
|
||||
{
|
||||
await control.UpdateSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compatibility 2.0 fallback
|
||||
_settings?.GetType().GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component
|
||||
}
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@Localizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
|
||||
|
||||
@code {
|
||||
private List<Theme> _themeList;
|
||||
@ -386,8 +386,15 @@
|
||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||
|
||||
await logger.LogInformation("Page Added {Page}", page);
|
||||
if (PageState.QueryString.ContainsKey("cp"))
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(page.Path));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["You Must Provide Page Name And Theme/Layout"], MessageType.Warning);
|
||||
@ -401,6 +408,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("cp"))
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
|
||||
{
|
||||
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
|
||||
|
@ -205,7 +205,7 @@
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@Localizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
|
||||
|
||||
@code {
|
||||
private List<Theme> _themeList;
|
||||
@ -493,8 +493,15 @@
|
||||
}
|
||||
|
||||
await logger.LogInformation("Page Saved {Page}", page);
|
||||
if (PageState.QueryString.ContainsKey("cp"))
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(page.Path));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["You Must Provide Page Name"], MessageType.Warning);
|
||||
@ -507,6 +514,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("cp"))
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
|
||||
{
|
||||
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
|
||||
|
@ -38,7 +38,7 @@ else
|
||||
<Label For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="effectiveDate" class="form-control" @bind="@effectivedate" />
|
||||
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -46,7 +46,7 @@ else
|
||||
<Label For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="expiryDate" class="form-control" @bind="@expirydate" />
|
||||
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@ -75,8 +75,8 @@ else
|
||||
private string name = string.Empty;
|
||||
private List<UserRole> users;
|
||||
private int userid = -1;
|
||||
private string effectivedate = string.Empty;
|
||||
private string expirydate = string.Empty;
|
||||
private DateTime? effectivedate = null;
|
||||
private DateTime? expirydate = null;
|
||||
private List<UserRole> userroles;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
@ -89,7 +89,10 @@ else
|
||||
Role role = await RoleService.GetRoleAsync(roleid);
|
||||
name = role.Name;
|
||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
users = users.Where(item => item.Role.Name == RoleNames.Registered).ToList();
|
||||
users = users
|
||||
.Where(u => u.Role.Name == RoleNames.Registered)
|
||||
.OrderBy(u => u.User.DisplayName)
|
||||
.ToList();
|
||||
await GetUserRoles();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -122,23 +125,8 @@ else
|
||||
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
||||
if (userrole != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(effectivedate))
|
||||
{
|
||||
userrole.EffectiveDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.EffectiveDate = DateTime.Parse(effectivedate);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(expirydate))
|
||||
{
|
||||
userrole.ExpiryDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.ExpiryDate = DateTime.Parse(expirydate);
|
||||
}
|
||||
userrole.EffectiveDate = effectivedate;
|
||||
userrole.ExpiryDate = expirydate;
|
||||
await UserRoleService.UpdateUserRoleAsync(userrole);
|
||||
}
|
||||
else
|
||||
@ -146,24 +134,8 @@ else
|
||||
userrole = new UserRole();
|
||||
userrole.UserId = userid;
|
||||
userrole.RoleId = roleid;
|
||||
|
||||
if (string.IsNullOrEmpty(effectivedate))
|
||||
{
|
||||
userrole.EffectiveDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.EffectiveDate = DateTime.Parse(effectivedate);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(expirydate))
|
||||
{
|
||||
userrole.ExpiryDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.ExpiryDate = DateTime.Parse(expirydate);
|
||||
}
|
||||
userrole.EffectiveDate = effectivedate;
|
||||
userrole.ExpiryDate = expirydate;
|
||||
|
||||
await UserRoleService.AddUserRoleAsync(userrole);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
<table class="table table-borderless">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="Enter the site name" ResourceKey="Name">Name: </Label>
|
||||
@ -103,6 +103,21 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype">
|
||||
<option value="-"><@Localizer["Select Container"]></option>
|
||||
<option value=""><@Localizer["Default Admin Container"]></option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
|
||||
@ -125,13 +140,18 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
||||
|
||||
<Section Name="SMTP" ResourceKey="SMTPSettings">
|
||||
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
@Localizer["Please Note That SMTP Requires The Notification Job To Be Enabled In the Scheduled Jobs"]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="host" HelpText="Enter the host name of the server" ResourceKey="Host">Host: </Label>
|
||||
<Label For="host" HelpText="Enter the host name of the SMTP server" ResourceKey="Host">Host: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="host" class="form-control" @bind="@_smtphost" />
|
||||
@ -139,7 +159,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="port" HelpText="Enter the port number for the server" ResourceKey="Port">Port: </Label>
|
||||
<Label For="port" HelpText="Enter the port number for the SMTP server. Please note this field is required if you provide a host name." ResourceKey="Port">Port: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="port" class="form-control" @bind="@_smtpport" />
|
||||
@ -147,15 +167,18 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="enabledSSl" HelpText="Specifiy if SSL is enabled for your server" ResourceKey="UseSsl">SSL Enabled: </Label>
|
||||
<Label For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="enabledSSl" class="form-control" @bind="@_smtpssl" />
|
||||
<select id="enabledSSl" class="form-control" @bind="@_smtpssl">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="username" HelpText="Enter the username for the server" ResourceKey="SmptUsername">Username: </Label>
|
||||
<Label For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="username" class="form-control" @bind="@_smtpusername" />
|
||||
@ -163,12 +186,20 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="password" HelpText="Enter the password for the server" ResourceKey="SmtpPassword">Password: </Label>
|
||||
<Label For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="password" type="password" class="form-control" @bind="@_smtppassword" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmptSender">Email Sender: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</Section>
|
||||
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
|
||||
@ -205,7 +236,6 @@
|
||||
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||
@ -229,12 +259,14 @@
|
||||
private string _themetype = "-";
|
||||
private string _layouttype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "-";
|
||||
private string _allowregistration;
|
||||
private string _smtphost = string.Empty;
|
||||
private string _smtpport = string.Empty;
|
||||
private string _smtpssl = string.Empty;
|
||||
private string _smtpssl = "False";
|
||||
private string _smtpusername = string.Empty;
|
||||
private string _smtppassword = string.Empty;
|
||||
private string _smtpsender = string.Empty;
|
||||
private string _pwaisenabled;
|
||||
private int _pwaappiconfileid = -1;
|
||||
private FileManager _pwaappiconfilemanager;
|
||||
@ -282,14 +314,16 @@
|
||||
_layouttype = site.DefaultLayoutType;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = site.DefaultContainerType;
|
||||
_admincontainertype = site.AdminContainerType;
|
||||
_allowregistration = site.AllowRegistration.ToString();
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
|
||||
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
|
||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", string.Empty);
|
||||
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
|
||||
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
|
||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||
|
||||
_pwaisenabled = site.PwaIsEnabled.ToString();
|
||||
|
||||
@ -348,6 +382,7 @@
|
||||
}
|
||||
_layouttype = "-";
|
||||
_containertype = "-";
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -388,6 +423,7 @@
|
||||
site.DefaultThemeType = _themetype;
|
||||
site.DefaultLayoutType = (_layouttype == "-" ? string.Empty : _layouttype);
|
||||
site.DefaultContainerType = _containertype;
|
||||
site.AdminContainerType = _admincontainertype;
|
||||
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
|
||||
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||
|
||||
@ -435,11 +471,12 @@
|
||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
|
||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
|
||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
|
||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
await logger.LogInformation("Site Saved {Site}", site);
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
AddModuleMessage(Localizer["Site Settings Saved"], MessageType.Success);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -78,6 +78,21 @@ else
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="adminContainer" class="form-control" @bind="@_admincontainertype">
|
||||
<option value="-"><@Localizer["Select Container"]></option>
|
||||
<option value=""><@Localizer["Default Admin Container"]></option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
|
||||
@ -225,6 +240,7 @@ else
|
||||
private string _themetype = "-";
|
||||
private string _layouttype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "";
|
||||
private string _sitetemplatetype = "-";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
@ -278,6 +294,7 @@ else
|
||||
}
|
||||
_layouttype = "-";
|
||||
_containertype = "-";
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -378,6 +395,7 @@ else
|
||||
config.DefaultTheme = _themetype;
|
||||
config.DefaultLayout = _layouttype;
|
||||
config.DefaultContainer = _containertype;
|
||||
config.DefaultAdminContainer = _admincontainertype;
|
||||
config.SiteTemplate = _sitetemplatetype;
|
||||
|
||||
ShowProgressIndicator();
|
||||
|
@ -18,14 +18,6 @@
|
||||
<input id="name" class="form-control" @bind="@_name" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="tenant" HelpText="Enter the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label>
|
||||
@ -86,6 +78,21 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype">
|
||||
<option value="-"><@Localizer["Select Container"]></option>
|
||||
<option value=""><@Localizer["Default Admin Container"]></option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="isDeleted" HelpText="Has this site been deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
|
||||
@ -97,6 +104,23 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="connectionstring" HelpText="The database connection string" ResourceKey="ConnectionString">Connection String: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="3" readonly></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
|
||||
@ -114,13 +138,12 @@
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private Alias _alias;
|
||||
private string _name = string.Empty;
|
||||
private List<Tenant> _tenantList;
|
||||
private string _tenant = string.Empty;
|
||||
private List<Alias> _aliasList;
|
||||
private string _urls = string.Empty;
|
||||
private string _themetype;
|
||||
private string _layouttype;
|
||||
private string _containertype;
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "-";
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
@ -128,6 +151,8 @@
|
||||
private string _deletedby;
|
||||
private DateTime? _deletedon;
|
||||
private string _isdeleted;
|
||||
private string _tenant = string.Empty;
|
||||
private string _connectionstring = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -144,8 +169,6 @@
|
||||
if (site != null)
|
||||
{
|
||||
_name = site.Name;
|
||||
_tenantList = await TenantService.GetTenantsAsync();
|
||||
_tenant = _tenantList.Find(item => item.TenantId == site.TenantId).Name;
|
||||
|
||||
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
|
||||
{
|
||||
@ -158,6 +181,7 @@
|
||||
_layouttype = site.DefaultLayoutType;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = site.DefaultContainerType;
|
||||
_admincontainertype = site.AdminContainerType;
|
||||
_createdby = site.CreatedBy;
|
||||
_createdon = site.CreatedOn;
|
||||
_modifiedby = site.ModifiedBy;
|
||||
@ -166,6 +190,14 @@
|
||||
_deletedon = site.DeletedOn;
|
||||
_isdeleted = site.IsDeleted.ToString();
|
||||
|
||||
List<Tenant> tenants = await TenantService.GetTenantsAsync();
|
||||
Tenant tenant = tenants.Find(item => item.TenantId == site.TenantId);
|
||||
if (tenant != null)
|
||||
{
|
||||
_tenant = tenant.Name;
|
||||
_connectionstring = tenant.DBConnectionString;
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
@ -193,6 +225,7 @@
|
||||
}
|
||||
_layouttype = "-";
|
||||
_containertype = "-";
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -228,6 +261,7 @@
|
||||
site.DefaultThemeType = _themetype;
|
||||
site.DefaultLayoutType = _layouttype ?? string.Empty;
|
||||
site.DefaultContainerType = _containertype;
|
||||
site.AdminContainerType = _admincontainertype;
|
||||
site.IsDeleted = (_isdeleted == null || Boolean.Parse(_isdeleted));
|
||||
|
||||
site = await SiteService.UpdateSiteAsync(site);
|
||||
|
@ -1,86 +0,0 @@
|
||||
@namespace Oqtane.Modules.Admin.Tenants
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ITenantService TenantService
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="The name of the tenant" ResourceKey="Name">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
@if (name == TenantNames.Master)
|
||||
{
|
||||
<input id="name" class="form-control" @bind="@name" readonly />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="name" class="form-control" @bind="@name" />
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="connectionstring" HelpText="The database connection string" ResourceKey="ConnectionString">Connection String: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="connectionstring" class="form-control" @bind="@connectionstring" rows="3" readonly></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveTenant">@Localizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private int tenantid;
|
||||
private string name = string.Empty;
|
||||
private string connectionstring = string.Empty;
|
||||
private string schema = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
tenantid = Int32.Parse(PageState.QueryString["id"]);
|
||||
var tenant = await TenantService.GetTenantAsync(tenantid);
|
||||
if (tenant != null)
|
||||
{
|
||||
name = tenant.Name;
|
||||
connectionstring = tenant.DBConnectionString;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", tenantid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error Loading Tenant"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveTenant()
|
||||
{
|
||||
try
|
||||
{
|
||||
connectionstring = connectionstring.Replace("\\\\", "\\");
|
||||
var tenant = await TenantService.GetTenantAsync(tenantid);
|
||||
if (tenant != null)
|
||||
{
|
||||
tenant.Name = name;
|
||||
tenant.DBConnectionString = connectionstring;
|
||||
|
||||
await TenantService.UpdateTenantAsync(tenant);
|
||||
await logger.LogInformation("Tenant Saved {TenantId}", tenantid);
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Tenant {TenantId} {Error}", tenantid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error Saving Tenant"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
@namespace Oqtane.Modules.Admin.Tenants
|
||||
@inherits ModuleBase
|
||||
@inject ITenantService TenantService
|
||||
@inject IAliasService AliasService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@if (tenants == null)
|
||||
{
|
||||
<p><em>@Localizer["Loading..."]</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Pager Items="@tenants">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["Name"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.TenantId.ToString())" ResourceKey="EditTenant" /></td>
|
||||
<td><ActionDialog Header="Delete Tenant" Message="@Localizer["Are You Sure You Wish To Delete The {0} Tenant?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTenant(context))" Disabled="@(context.Name == TenantNames.Master)" ResourceKey="DeleteTenant" /></td>
|
||||
<td>@context.Name</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Tenant> tenants;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
tenants = await TenantService.GetTenantsAsync();
|
||||
}
|
||||
|
||||
private async Task DeleteTenant(Tenant Tenant)
|
||||
{
|
||||
try
|
||||
{
|
||||
string message = string.Empty;
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (var alias in aliases)
|
||||
{
|
||||
if (alias.TenantId == Tenant.TenantId)
|
||||
{
|
||||
message += ", " + alias.Name;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
await TenantService.DeleteTenantAsync(Tenant.TenantId);
|
||||
await logger.LogInformation("Tenant Deleted {Tenant}", Tenant);
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Tenant Cannot Be Deleted Until The Following Sites Are Deleted: {0}", message.Substring(2)], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Tenant {Tenant} {Error}", Tenant, ex.Message);
|
||||
AddModuleMessage(Localizer["Error Deleting Tenant"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ else
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -101,7 +101,8 @@ else
|
||||
try
|
||||
{
|
||||
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
|
||||
AddModuleMessage(Localizer["Theme Deleted Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
|
||||
AddModuleMessage(Localizer["Theme Deleted Successfully"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -48,26 +48,12 @@
|
||||
|
||||
private async Task Send()
|
||||
{
|
||||
var notification = new Notification();
|
||||
try
|
||||
{
|
||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
notification.SiteId = PageState.Site.SiteId;
|
||||
notification.FromUserId = PageState.User.UserId;
|
||||
notification.FromDisplayName = PageState.User.DisplayName;
|
||||
notification.FromEmail = PageState.User.Email;
|
||||
notification.ToUserId = user.UserId;
|
||||
notification.ToDisplayName = user.DisplayName;
|
||||
notification.ToEmail = user.Email;
|
||||
notification.Subject = subject;
|
||||
notification.Body = body;
|
||||
notification.ParentId = null;
|
||||
notification.CreatedOn = DateTime.UtcNow;
|
||||
notification.IsDelivered = false;
|
||||
notification.DeliveredOn = null;
|
||||
notification.SendOn = DateTime.UtcNow;
|
||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, null);
|
||||
notification = await NotificationService.AddNotificationAsync(notification);
|
||||
await logger.LogInformation("Notification Created {Notification}", notification);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
@ -79,7 +65,7 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Adding Notification {Notification} {Error}", notification, ex.Message);
|
||||
await logger.LogError(ex, "Error Adding Notification {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error Adding Notification"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
@ -176,26 +176,12 @@
|
||||
|
||||
private async Task Send()
|
||||
{
|
||||
var notification = new Notification();
|
||||
try
|
||||
{
|
||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
notification.SiteId = PageState.Site.SiteId;
|
||||
notification.FromUserId = PageState.User.UserId;
|
||||
notification.FromDisplayName = PageState.User.DisplayName;
|
||||
notification.FromEmail = PageState.User.Email;
|
||||
notification.ToUserId = user.UserId;
|
||||
notification.ToDisplayName = user.DisplayName;
|
||||
notification.ToEmail = user.Email;
|
||||
notification.Subject = subject;
|
||||
notification.Body = body;
|
||||
notification.ParentId = notificationid;
|
||||
notification.CreatedOn = DateTime.UtcNow;
|
||||
notification.IsDelivered = false;
|
||||
notification.DeliveredOn = null;
|
||||
notification.SendOn = DateTime.UtcNow;
|
||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, notificationid);
|
||||
notification = await NotificationService.AddNotificationAsync(notification);
|
||||
await logger.LogInformation("Notification Created {Notification}", notification);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
@ -207,7 +193,7 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Adding Notification {Notification} {Error}", notification, ex.Message);
|
||||
await logger.LogError(ex, "Error Adding Notification {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error Adding Notification"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,43 @@
|
||||
@typeparam TableItem
|
||||
|
||||
<p>
|
||||
@if (Toolbar == "Top")
|
||||
{
|
||||
<div class="mx-auto text-center">
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_page > _maxPages)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))>
|
||||
@pager
|
||||
</button>
|
||||
}
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage < _pages)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<span class="btn btn-link disabled">Page @_page of @_pages</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (Format == "Table")
|
||||
{
|
||||
<table class="@Class">
|
||||
@ -35,14 +72,20 @@
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (Toolbar == "Bottom")
|
||||
{
|
||||
<div class="mx-auto text-center">
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_page > _maxPages)
|
||||
{
|
||||
<button class="btn btn-secondary" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<button class="btn btn-secondary" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
@ -50,17 +93,22 @@
|
||||
@pager
|
||||
</button>
|
||||
}
|
||||
<button class="btn btn-secondary" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage < _pages)
|
||||
{
|
||||
<button class="btn btn-secondary" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button>
|
||||
}
|
||||
@if (_endPage > 1)
|
||||
{
|
||||
<span class="btn btn-link disabled">Page @_page of @_pages</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</p>
|
||||
|
||||
@code {
|
||||
@ -74,6 +122,9 @@
|
||||
[Parameter]
|
||||
public string Format { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Toolbar { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Header { get; set; }
|
||||
|
||||
@ -104,6 +155,11 @@
|
||||
Format = "Table";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Toolbar))
|
||||
{
|
||||
Toolbar = "Top";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Class))
|
||||
{
|
||||
if (Format == "Table")
|
||||
|
@ -16,13 +16,14 @@
|
||||
<div class="d-flex">
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<div class="collapse" id="@Name">
|
||||
<div class="collapse @_show" id="@Name">
|
||||
@ChildContent
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string _heading = string.Empty;
|
||||
private string _expanded = string.Empty;
|
||||
private string _show = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
@ -40,6 +41,7 @@
|
||||
{
|
||||
_heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name;
|
||||
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded : "false";
|
||||
if (_expanded == "true") { _show = "show"; }
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
|
@ -1,7 +1,7 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
|
||||
<CascadingValue Value="this">
|
||||
<CascadingValue Value="this" IsFixed="true">
|
||||
<div class="container-fluid">
|
||||
<div class="form-group">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
|
@ -67,6 +67,7 @@ namespace Oqtane.Client
|
||||
builder.Services.AddScoped<ISqlService, SqlService>();
|
||||
builder.Services.AddScoped<ISystemService, SystemService>();
|
||||
builder.Services.AddScoped<ILocalizationService, LocalizationService>();
|
||||
builder.Services.AddScoped<ILanguageService, LanguageService>();
|
||||
|
||||
await LoadClientAssemblies(httpClient);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
@ -37,7 +37,7 @@ namespace Oqtane.Services
|
||||
{
|
||||
if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar)))
|
||||
{
|
||||
folderPath = Utilities.PathCombine(folderPath,"\\");
|
||||
folderPath = Utilities.PathCombine(folderPath, System.IO.Path.DirectorySeparatorChar.ToString());
|
||||
}
|
||||
|
||||
var path = WebUtility.UrlEncode(folderPath);
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
@ -37,7 +37,7 @@ namespace Oqtane.Services
|
||||
{
|
||||
if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar)))
|
||||
{
|
||||
folderPath = Utilities.PathCombine(folderPath, "\\");
|
||||
folderPath = Utilities.PathCombine(folderPath, System.IO.Path.DirectorySeparatorChar.ToString());
|
||||
}
|
||||
|
||||
var path = WebUtility.UrlEncode(folderPath);
|
||||
|
17
Oqtane.Client/Services/Interfaces/ILanguageService.cs
Normal file
17
Oqtane.Client/Services/Interfaces/ILanguageService.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Oqtane.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
public interface ILanguageService
|
||||
{
|
||||
Task<List<Language>> GetLanguagesAsync(int siteId);
|
||||
|
||||
Task<Language> GetLanguageAsync(int languageId);
|
||||
|
||||
Task<Language> AddLanguageAsync(Language language);
|
||||
|
||||
Task DeleteLanguageAsync(int languageId);
|
||||
}
|
||||
}
|
38
Oqtane.Client/Services/LanguageService.cs
Normal file
38
Oqtane.Client/Services/LanguageService.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
public class LanguageService : ServiceBase, ILanguageService
|
||||
{
|
||||
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public LanguageService(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "Language");
|
||||
|
||||
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
||||
{
|
||||
var languages = await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}");
|
||||
|
||||
return languages?.OrderBy(l => l.Name).ToList() ?? Enumerable.Empty<Language>().ToList();
|
||||
}
|
||||
|
||||
public async Task<Language> GetLanguageAsync(int languageId)
|
||||
=> await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
||||
|
||||
public async Task<Language> AddLanguageAsync(Language language)
|
||||
=> await PostJsonAsync<Language>(Apiurl, language);
|
||||
|
||||
public async Task DeleteLanguageAsync(int languageId)
|
||||
=> await DeleteAsync($"{Apiurl}/{languageId}");
|
||||
}
|
||||
}
|
@ -296,7 +296,7 @@
|
||||
public bool ShowLanguageSwitcher { get; set; } = true;
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||
{
|
||||
@ -485,10 +485,10 @@
|
||||
switch (location)
|
||||
{
|
||||
case "Add":
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "");
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "cp=" + PageState.Page.PageId);
|
||||
break;
|
||||
case "Edit":
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString());
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString() + "&cp=" + PageState.Page.PageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
12
Oqtane.Client/Themes/Controls/FontIcon.razor
Normal file
12
Oqtane.Client/Themes/Controls/FontIcon.razor
Normal file
@ -0,0 +1,12 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(Value))
|
||||
{
|
||||
<span class="@Value" aria-hidden="true"></span>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter()]
|
||||
public string Value { get; set; }
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@using System.Globalization
|
||||
@using Microsoft.AspNetCore.Localization;
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@using Oqtane.Models
|
||||
@inject ILocalizationService LocalizationService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@if (_supportedCultures?.Count() > 1)
|
||||
@ -26,7 +26,8 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||
}
|
||||
|
||||
private async Task SetCultureAsync(string culture)
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
|
||||
@inherits MenuBase
|
||||
|
||||
@if (MenuPages.Any())
|
||||
@ -10,35 +11,7 @@
|
||||
</span>
|
||||
<div class="app-menu">
|
||||
<div class="collapse navbar-collapse" id="Menu">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
@foreach (var p in MenuPages)
|
||||
{
|
||||
if (p.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="@GetUrl(p)" target="@GetTarget(p)" >
|
||||
@if (p.Icon != string.Empty)
|
||||
{
|
||||
<span class="@p.Icon" aria-hidden="true"></span>
|
||||
}
|
||||
@p.Name<span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@GetUrl(p)" target="@GetTarget(p)" >
|
||||
@if (p.Icon != string.Empty)
|
||||
{
|
||||
<span class="@p.Icon" aria-hidden="true"></span>
|
||||
}
|
||||
@p.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
<MenuItemsHorizontal ParentPage="null" Pages="MenuPages" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
27
Oqtane.Client/Themes/Controls/MenuItemsBase.cs
Normal file
27
Oqtane.Client/Themes/Controls/MenuItemsBase.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using Oqtane.Models;
|
||||
using Oqtane.UI;
|
||||
|
||||
namespace Oqtane.Themes.Controls
|
||||
{
|
||||
public abstract class MenuItemsBase : MenuBase
|
||||
{
|
||||
[Parameter()]
|
||||
public Page ParentPage { get; set; }
|
||||
|
||||
[Parameter()]
|
||||
public IEnumerable<Page> Pages { get; set; }
|
||||
|
||||
protected IEnumerable<Page> GetChildPages()
|
||||
{
|
||||
return Pages
|
||||
.Where(e => e.ParentId == ParentPage?.PageId)
|
||||
.OrderBy(e => e.Order)
|
||||
.AsEnumerable();
|
||||
}
|
||||
}
|
||||
}
|
77
Oqtane.Client/Themes/Controls/MenuItemsHorizontal.razor
Normal file
77
Oqtane.Client/Themes/Controls/MenuItemsHorizontal.razor
Normal file
@ -0,0 +1,77 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits MenuItemsBase
|
||||
|
||||
@if (ParentPage != null)
|
||||
{
|
||||
<div class="dropdown-menu" aria-labelledby="@($"navbarDropdown{ParentPage.PageId}")">
|
||||
@foreach (var childPage in GetChildPages())
|
||||
{
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<a class="dropdown-item active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="dropdown-item" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="navbar-nav mr-auto">
|
||||
@foreach (var childPage in GetChildPages())
|
||||
{
|
||||
if (!Pages.Any(e => e.ParentId == childPage.PageId))
|
||||
{
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item dropdown active">
|
||||
<a class="nav-link dropdown-toggle" href="@GetUrl(childPage)" target="@GetTarget(childPage)" id="@($"navbarDropdown{childPage.PageId}")" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
<MenuItemsHorizontal ParentPage="childPage" Pages="Pages" />
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="@GetUrl(childPage)" target="@GetTarget(childPage)" id="@($"navbarDropdown{childPage.PageId}")" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
<MenuItemsHorizontal ParentPage="childPage" Pages="Pages" />
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
}
|
61
Oqtane.Client/Themes/Controls/MenuItemsVertical.razor
Normal file
61
Oqtane.Client/Themes/Controls/MenuItemsVertical.razor
Normal file
@ -0,0 +1,61 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits MenuItemsBase
|
||||
|
||||
@if (ParentPage != null)
|
||||
{
|
||||
foreach (var childPage in GetChildPages())
|
||||
{
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
if (Pages.Any(e => e.ParentId == childPage.PageId))
|
||||
{
|
||||
<MenuItemsVertical ParentPage="childPage" Pages="Pages" />
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="nav flex-column">
|
||||
@foreach (var childPage in GetChildPages())
|
||||
{
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
if (Pages.Any(e => e.ParentId == childPage.PageId))
|
||||
{
|
||||
<MenuItemsVertical ParentPage="childPage" Pages="Pages" />
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
}
|
@ -10,25 +10,7 @@
|
||||
</span>
|
||||
<div class="app-menu">
|
||||
<div class="collapse navbar-collapse" id="Menu">
|
||||
<ul class="nav flex-column">
|
||||
@foreach (var p in MenuPages)
|
||||
{
|
||||
<li class="nav-item px-3">
|
||||
<a href="@GetUrl(p)" class="nav-link" style="padding-left:@((p.Level + 1) * 15)px !important;" target="@GetTarget(p)">
|
||||
|
||||
@if (p.HasChildren)
|
||||
{
|
||||
<i class="oi oi-chevron-right"></i>
|
||||
}
|
||||
@if (p.Icon != string.Empty)
|
||||
{
|
||||
<span class="@p.Icon" aria-hidden="true"></span>
|
||||
}
|
||||
@p.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<MenuItemsVertical ParentPage="null" Pages="MenuPages" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -88,5 +88,10 @@ namespace Oqtane.Themes
|
||||
{
|
||||
return Utilities.ContentUrl(PageState.Alias, fileid);
|
||||
}
|
||||
|
||||
public string ContentUrl(int fileid, bool asAttachment)
|
||||
{
|
||||
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
@namespace Oqtane.UI
|
||||
@namespace Oqtane.UI
|
||||
|
||||
<CascadingValue Value="@_moduleState">
|
||||
<CascadingValue Value="@_moduleState" IsFixed="true">
|
||||
@DynamicComponent
|
||||
</CascadingValue>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
string container = _moduleState.ContainerType;
|
||||
if (PageState.ModuleId != -1 && _moduleState.UseAdminContainer)
|
||||
{
|
||||
container = Constants.DefaultAdminContainer;
|
||||
container = (!string.IsNullOrEmpty(PageState.Site.AdminContainerType)) ? PageState.Site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||
}
|
||||
|
||||
DynamicComponent = builder =>
|
||||
|
@ -2,7 +2,7 @@
|
||||
@inject IStringLocalizer<ModuleInstance> Localizer
|
||||
|
||||
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
||||
<CascadingValue Value="this">
|
||||
<CascadingValue Value="this" IsFixed="true">
|
||||
@DynamicComponent
|
||||
</CascadingValue>
|
||||
@if (_progressindicator)
|
||||
|
@ -64,21 +64,24 @@
|
||||
|
||||
private async Task InitializePwa(Interop interop)
|
||||
{
|
||||
string url = NavigationManager.BaseUri;
|
||||
url = url.Substring(0, url.Length - 1);
|
||||
|
||||
// dynamically create manifest.json and add to page
|
||||
string manifest = "setTimeout(() => { " +
|
||||
"var manifest = { " +
|
||||
"\"name\": \"" + PageState.Site.Name + "\", " +
|
||||
"\"short_name\": \"" + PageState.Site.Name + "\", " +
|
||||
"\"start_url\": \"/\", " +
|
||||
"\"start_url\": \"" + url + "/\", " +
|
||||
"\"display\": \"standalone\", " +
|
||||
"\"background_color\": \"#fff\", " +
|
||||
"\"description\": \"" + PageState.Site.Name + "\", " +
|
||||
"\"icons\": [{ " +
|
||||
"\"src\": \"" + Utilities.ContentUrl(PageState.Alias, PageState.Site.PwaAppIconFileId.Value) + "\", " +
|
||||
"\"src\": \"" + url + Utilities.ContentUrl(PageState.Alias, PageState.Site.PwaAppIconFileId.Value) + "\", " +
|
||||
"\"sizes\": \"192x192\", " +
|
||||
"\"type\": \"image/png\" " +
|
||||
"}, { " +
|
||||
"\"src\": \"" + Utilities.ContentUrl(PageState.Alias, PageState.Site.PwaSplashIconFileId.Value) + "\", " +
|
||||
"\"src\": \"" + url + Utilities.ContentUrl(PageState.Alias, PageState.Site.PwaSplashIconFileId.Value) + "\", " +
|
||||
"\"sizes\": \"512x512\", " +
|
||||
"\"type\": \"image/png\" " +
|
||||
"}] " +
|
||||
|
@ -50,29 +50,13 @@ namespace Oqtane.Controllers
|
||||
[HttpGet("name/{**name}")]
|
||||
public Alias Get(string name, string sync)
|
||||
{
|
||||
List<Alias> aliases = _aliases.GetAliases().ToList(); // cached
|
||||
Alias alias = null;
|
||||
|
||||
if (_accessor.HttpContext != null)
|
||||
{
|
||||
name = (name == "~") ? "" : name;
|
||||
name = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(name);
|
||||
var segments = name.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// iterate segments in reverse order
|
||||
for (int i = segments.Length; i > 0; i--)
|
||||
{
|
||||
name = string.Join("/", segments, 0, i);
|
||||
alias = aliases.Find(item => item.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
if (alias != null)
|
||||
{
|
||||
break; // found a matching alias
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alias == null && aliases.Any())
|
||||
{
|
||||
// use first alias if name does not exist
|
||||
alias = aliases.FirstOrDefault();
|
||||
alias = _aliases.GetAlias(name);
|
||||
}
|
||||
|
||||
// get sync events
|
||||
@ -81,6 +65,7 @@ namespace Oqtane.Controllers
|
||||
alias.SyncDate = DateTime.UtcNow;
|
||||
alias.SyncEvents = _syncManager.GetSyncEvents(alias.TenantId, DateTime.ParseExact(sync, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
|
@ -142,13 +142,13 @@ namespace Oqtane.Controllers
|
||||
Models.File _file = _files.GetFile(id, false);
|
||||
if (_file.Name != file.Name || _file.FolderId != file.FolderId)
|
||||
{
|
||||
string folderpath = GetFolderPath(file.Folder);
|
||||
string folderpath = _folders.GetFolderPath(file.Folder);
|
||||
if (!Directory.Exists(folderpath))
|
||||
{
|
||||
Directory.CreateDirectory(folderpath);
|
||||
}
|
||||
|
||||
System.IO.File.Move(Path.Combine(GetFolderPath(_file.Folder), _file.Name), Path.Combine(folderpath, file.Name));
|
||||
System.IO.File.Move(_files.GetFilePath(_file), Path.Combine(folderpath, file.Name));
|
||||
}
|
||||
|
||||
file.Extension = Path.GetExtension(file.Name).ToLower().Replace(".", "");
|
||||
@ -177,7 +177,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
_files.DeleteFile(id);
|
||||
|
||||
string filepath = Path.Combine(GetFolderPath(file.Folder), file.Name);
|
||||
string filepath = _files.GetFilePath(file);
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
System.IO.File.Delete(filepath);
|
||||
@ -213,7 +213,7 @@ namespace Oqtane.Controllers
|
||||
return file;
|
||||
}
|
||||
|
||||
string folderPath = GetFolderPath(folder);
|
||||
string folderPath = _folders.GetFolderPath(folder);
|
||||
CreateDirectory(folderPath);
|
||||
|
||||
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||
@ -280,7 +280,7 @@ namespace Oqtane.Controllers
|
||||
if (virtualFolder != null &&
|
||||
_userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions))
|
||||
{
|
||||
folderPath = GetFolderPath(virtualFolder);
|
||||
folderPath = _folders.GetFolderPath(virtualFolder);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -291,7 +291,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
if (folderPath != "")
|
||||
if (!String.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
CreateDirectory(folderPath);
|
||||
using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create))
|
||||
@ -472,7 +472,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
|
||||
{
|
||||
var filepath = Path.Combine(GetFolderPath(file.Folder), file.Name);
|
||||
var filepath = _files.GetFilePath(file);
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
var result = asAttachment
|
||||
@ -500,11 +500,6 @@ namespace Oqtane.Controllers
|
||||
return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
|
||||
}
|
||||
|
||||
private string GetFolderPath(Folder folder)
|
||||
{
|
||||
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
|
||||
}
|
||||
|
||||
private string GetFolderPath(string folder)
|
||||
{
|
||||
return Utilities.PathCombine(_environment.WebRootPath, folder);
|
||||
|
50
Oqtane.Server/Controllers/LanguageController.cs
Normal file
50
Oqtane.Server/Controllers/LanguageController.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
[Route(ControllerRoutes.Default)]
|
||||
public class LanguageController : Controller
|
||||
{
|
||||
private readonly ILanguageRepository _languages;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public LanguageController(ILanguageRepository language, ILogManager logger)
|
||||
{
|
||||
_languages = language;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<Language> Get(string siteid) => _languages.GetLanguages(int.Parse(siteid));
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public Language Get(int id) => _languages.GetLanguage(id);
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public Language Post([FromBody] Language language)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
language = _languages.AddLanguage(language);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public void Delete(int id)
|
||||
{
|
||||
_languages.DeleteLanguage(id);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
||||
}
|
||||
}
|
||||
}
|
@ -101,13 +101,12 @@ namespace Oqtane.Controllers
|
||||
public void Delete(int id, int siteid)
|
||||
{
|
||||
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid);
|
||||
if (moduledefinition != null )
|
||||
if (moduledefinition != null && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server")
|
||||
// execute uninstall logic or scripts
|
||||
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType))
|
||||
{
|
||||
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
|
||||
|
||||
// execute uninstall logic
|
||||
foreach (Tenant tenant in _tenants.GetTenants())
|
||||
{
|
||||
try
|
||||
@ -128,36 +127,47 @@ namespace Oqtane.Controllers
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} For Tenant {Tenant} {Error}", moduledefinition.ModuleDefinitionName, tenant.Name, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use assets.json to clean up file resources
|
||||
string assetfilepath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName), "assets.json");
|
||||
if (System.IO.File.Exists(assetfilepath))
|
||||
// remove module assets
|
||||
string assetpath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName));
|
||||
if (System.IO.File.Exists(Path.Combine(assetpath, "assets.json")))
|
||||
{
|
||||
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(assetfilepath));
|
||||
// use assets.json to clean up file resources
|
||||
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(Path.Combine(assetpath, "assets.json")));
|
||||
foreach(string asset in assets)
|
||||
{
|
||||
if (System.IO.File.Exists(asset))
|
||||
// legacy support for assets that were stored as absolute paths
|
||||
string filepath = asset.StartsWith("\\") ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
System.IO.File.Delete(asset);
|
||||
System.IO.File.Delete(filepath);
|
||||
}
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to delete assemblies based on naming convention
|
||||
foreach(string asset in Directory.GetFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "*.*"))
|
||||
{
|
||||
System.IO.File.Delete(asset);
|
||||
}
|
||||
_logger.Log(LogLevel.Warning, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}. Please Note That Some Assets May Have Been Missed Due To A Missing Asset Manifest. An Asset Manifest Is Only Created If A Module Is Installed From A Nuget Package.", moduledefinition.Name);
|
||||
}
|
||||
|
||||
// clean up module static resource folder
|
||||
string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName)));
|
||||
if (Directory.Exists(folder))
|
||||
if (Directory.Exists(assetpath))
|
||||
{
|
||||
Directory.Delete(folder, true);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
|
||||
Directory.Delete(assetpath, true);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
|
||||
}
|
||||
|
||||
// remove module definition
|
||||
_moduleDefinitions.DeleteModuleDefinition(id, siteid);
|
||||
_moduleDefinitions.DeleteModuleDefinition(id);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// POST api/<controller>?moduleid=x
|
||||
[HttpPost]
|
||||
|
@ -57,28 +57,44 @@ namespace Oqtane.Controllers
|
||||
Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault();
|
||||
if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client")
|
||||
{
|
||||
// use assets.json to clean up file resources
|
||||
string assetfilepath = Path.Combine(_environment.WebRootPath, "Themes", Utilities.GetTypeName(theme.ThemeName), "assets.json");
|
||||
if (System.IO.File.Exists(assetfilepath))
|
||||
// remove theme assets
|
||||
string assetpath = Path.Combine(_environment.WebRootPath, "Themes", Utilities.GetTypeName(theme.ThemeName));
|
||||
if (System.IO.File.Exists(Path.Combine(assetpath, "assets.json")))
|
||||
{
|
||||
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(assetfilepath));
|
||||
// use assets.json to clean up file resources
|
||||
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(Path.Combine(assetpath, "assets.json")));
|
||||
foreach (string asset in assets)
|
||||
{
|
||||
if (System.IO.File.Exists(asset))
|
||||
// legacy support for assets that were stored as absolute paths
|
||||
string filepath = (asset.StartsWith("\\")) ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
|
||||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
System.IO.File.Delete(asset);
|
||||
System.IO.File.Delete(filepath);
|
||||
}
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assets Removed For {ThemeName}", theme.ThemeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to delete assemblies based on naming convention
|
||||
foreach (string asset in Directory.GetFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), Utilities.GetTypeName(theme.ThemeName) + "*.*"))
|
||||
{
|
||||
System.IO.File.Delete(asset);
|
||||
}
|
||||
_logger.Log(LogLevel.Warning, this, LogFunction.Delete, "Theme Assets Removed For {ThemeName}. Please Note That Some Assets May Have Been Missed Due To A Missing Asset Manifest. An Asset Manifest Is Only Created If A Theme Is Installed From A Nuget Package.", theme.ThemeName);
|
||||
}
|
||||
|
||||
// clean up theme static resource folder
|
||||
string folder = Path.Combine(_environment.WebRootPath, "Themes" , Utilities.GetTypeName(theme.ThemeName));
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
Directory.Delete(folder, true);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Resource Folder Removed For {ThemeName}", theme.ThemeName);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Static Resource Folder Removed For {ThemeName}", theme.ThemeName);
|
||||
}
|
||||
|
||||
// remove theme
|
||||
_themes.DeleteTheme(theme.ThemeName);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Removed For {ThemeName}", theme.ThemeName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -146,20 +146,10 @@ namespace Oqtane.Controllers
|
||||
newUser = _users.AddUser(user);
|
||||
if (!verified)
|
||||
{
|
||||
Notification notification = new Notification();
|
||||
notification.SiteId = user.SiteId;
|
||||
notification.FromUserId = null;
|
||||
notification.ToUserId = newUser.UserId;
|
||||
notification.ToEmail = newUser.Email;
|
||||
notification.Subject = "User Account Verification";
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
notification.Body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
notification.ParentId = null;
|
||||
notification.CreatedOn = DateTime.UtcNow;
|
||||
notification.IsDelivered = false;
|
||||
notification.DeliveredOn = null;
|
||||
notification.SendOn = DateTime.UtcNow;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, null, newUser, "User Account Verification", body, null);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
||||
@ -379,20 +369,10 @@ namespace Oqtane.Controllers
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
Notification notification = new Notification();
|
||||
notification.SiteId = user.SiteId;
|
||||
notification.FromUserId = null;
|
||||
notification.ToUserId = user.UserId;
|
||||
notification.ToEmail = "";
|
||||
notification.Subject = "User Password Reset";
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
notification.Body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!";
|
||||
notification.ParentId = null;
|
||||
notification.CreatedOn = DateTime.UtcNow;
|
||||
notification.IsDelivered = false;
|
||||
notification.DeliveredOn = null;
|
||||
notification.SendOn = DateTime.UtcNow;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, null, user, "User Password Reset", body, null);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
|
||||
private static Assembly ResolveDependencies(AssemblyLoadContext context, AssemblyName name)
|
||||
{
|
||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) + "\\" + name.Name + ".dll";
|
||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) + Path.DirectorySeparatorChar + name.Name + ".dll";
|
||||
if (File.Exists(assemblyPath))
|
||||
{
|
||||
return context.LoadFromStream(new MemoryStream(File.ReadAllBytes(assemblyPath)));
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
@ -244,14 +244,6 @@ namespace Oqtane.Infrastructure
|
||||
db.Tenant.Add(tenant);
|
||||
db.SaveChanges();
|
||||
_cache.Remove("tenants");
|
||||
|
||||
if (install.TenantName == TenantNames.Master)
|
||||
{
|
||||
var job = new Job { Name = "Notification Job", JobType = "Oqtane.Infrastructure.NotificationJob, Oqtane.Server", Frequency = "m", Interval = 1, StartDate = null, EndDate = null, IsEnabled = false, IsStarted = false, IsExecuting = false, NextExecution = null, RetentionHistory = 10, CreatedBy = "", CreatedOn = DateTime.UtcNow, ModifiedBy = "", ModifiedOn = DateTime.UtcNow };
|
||||
db.Job.Add(job);
|
||||
db.SaveChanges();
|
||||
_cache.Remove("jobs");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -28,13 +28,13 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
public void InstallPackages(string folders)
|
||||
{
|
||||
if (!InstallPackages(folders, _environment.WebRootPath))
|
||||
if (!InstallPackages(folders, _environment.WebRootPath, _environment.ContentRootPath))
|
||||
{
|
||||
// error installing packages
|
||||
}
|
||||
}
|
||||
|
||||
public static bool InstallPackages(string folders, string webRootPath)
|
||||
public static bool InstallPackages(string folders, string webRootPath, string contentRootPath)
|
||||
{
|
||||
bool install = false;
|
||||
string binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||
@ -79,6 +79,7 @@ namespace Oqtane.Infrastructure
|
||||
if (frameworkversion == "" || Version.Parse(Constants.Version).CompareTo(Version.Parse(frameworkversion)) >= 0)
|
||||
{
|
||||
List<string> assets = new List<string>();
|
||||
bool manifest = false;
|
||||
|
||||
// module and theme packages must be in form of name.1.0.0.nupkg
|
||||
string name = Path.GetFileNameWithoutExtension(packagename);
|
||||
@ -91,36 +92,41 @@ namespace Oqtane.Infrastructure
|
||||
string foldername = Path.GetDirectoryName(entry.FullName).Split(Path.DirectorySeparatorChar)[0];
|
||||
string filename = Path.GetFileName(entry.FullName);
|
||||
|
||||
if (!manifest && filename == "assets.json")
|
||||
{
|
||||
manifest = true;
|
||||
}
|
||||
|
||||
switch (foldername)
|
||||
{
|
||||
case "lib":
|
||||
filename = Path.Combine(binFolder, filename);
|
||||
ExtractFile(entry, filename);
|
||||
assets.Add(filename);
|
||||
assets.Add(filename.Replace(contentRootPath, ""));
|
||||
break;
|
||||
case "wwwroot":
|
||||
filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("wwwroot/", "").Split('/')));
|
||||
ExtractFile(entry, filename);
|
||||
assets.Add(filename);
|
||||
assets.Add(filename.Replace(contentRootPath, ""));
|
||||
break;
|
||||
case "runtimes":
|
||||
var destSubFolder = Path.GetDirectoryName(entry.FullName);
|
||||
filename = Path.Combine(binFolder, destSubFolder, filename);
|
||||
ExtractFile(entry, filename);
|
||||
assets.Add(filename);
|
||||
assets.Add(filename.Replace(contentRootPath, ""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// save list of assets
|
||||
if (assets.Count != 0)
|
||||
// save dynamic list of assets
|
||||
if (!manifest && assets.Count != 0)
|
||||
{
|
||||
string assetfilepath = Path.Combine(webRootPath, folder, name, "assets.json");
|
||||
if (File.Exists(assetfilepath))
|
||||
string manifestpath = Path.Combine(webRootPath, folder, name, "assets.json");
|
||||
if (File.Exists(manifestpath))
|
||||
{
|
||||
File.Delete(assetfilepath);
|
||||
File.Delete(manifestpath);
|
||||
}
|
||||
File.WriteAllText(assetfilepath, JsonSerializer.Serialize(assets));
|
||||
File.WriteAllText(manifestpath, JsonSerializer.Serialize(assets));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@ -25,6 +25,15 @@ namespace Oqtane.Infrastructure
|
||||
// abstract method must be overridden
|
||||
public abstract string ExecuteJob(IServiceProvider provider);
|
||||
|
||||
// public properties which can be overridden and are used during auto registration of job
|
||||
public string Name { get; set; } = "";
|
||||
public string Frequency { get; set; } = "d"; // day
|
||||
public int Interval { get; set; } = 1;
|
||||
public DateTime? StartDate { get; set; } = null;
|
||||
public DateTime? EndDate { get; set; } = null;
|
||||
public int RetentionHistory { get; set; } = 10;
|
||||
public bool IsEnabled { get; set; } = false;
|
||||
|
||||
protected async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Yield(); // required so that this method does not block startup
|
||||
@ -82,7 +91,15 @@ namespace Oqtane.Infrastructure
|
||||
// execute the job
|
||||
try
|
||||
{
|
||||
log.Notes = ExecuteJob(scope.ServiceProvider);
|
||||
var notes = "";
|
||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
||||
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
|
||||
foreach (var tenant in tenants.GetTenants())
|
||||
{
|
||||
siteState.Alias = new Alias { TenantId = tenant.TenantId };
|
||||
notes += ExecuteJob(scope.ServiceProvider);
|
||||
}
|
||||
log.Notes = notes;
|
||||
log.Succeeded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -153,20 +170,40 @@ namespace Oqtane.Infrastructure
|
||||
// set IsExecuting to false in case this job was forcefully terminated previously
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault();
|
||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||
if (job != null)
|
||||
{
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
// can occur during the initial installation as there is no DBContext
|
||||
// auto registration - does not run on initial installation but will run after restart
|
||||
job = new Job { JobType = jobTypeName };
|
||||
// optional properties
|
||||
var jobType = Type.GetType(jobTypeName);
|
||||
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
||||
if (jobObject.Name != "")
|
||||
{
|
||||
job.Name = jobObject.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.Name = Utilities.GetTypeName(job.JobType);
|
||||
}
|
||||
job.Frequency = jobObject.Frequency;
|
||||
job.Interval = jobObject.Interval;
|
||||
job.StartDate = jobObject.StartDate;
|
||||
job.EndDate = jobObject.EndDate;
|
||||
job.RetentionHistory = jobObject.RetentionHistory;
|
||||
job.IsEnabled = jobObject.IsEnabled;
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
jobs.AddJob(job);
|
||||
}
|
||||
}
|
||||
|
||||
_executingTask = ExecuteAsync(_cancellationTokenSource.Token);
|
||||
@ -175,6 +212,11 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
return _executingTask;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// can occur during the initial installation because this method is called during startup and the database has not yet been created
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@ -14,31 +14,26 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
// JobType = "Oqtane.Infrastructure.NotificationJob, Oqtane.Server"
|
||||
|
||||
public NotificationJob(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory) {}
|
||||
public NotificationJob(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
|
||||
{
|
||||
Name = "Notification Job";
|
||||
Frequency = "m"; // minute
|
||||
Interval = 1;
|
||||
IsEnabled = false;
|
||||
}
|
||||
|
||||
// job is executed for each tenant in installation
|
||||
public override string ExecuteJob(IServiceProvider provider)
|
||||
{
|
||||
string log = "";
|
||||
|
||||
// iterate through tenants in this installation
|
||||
List<int> tenants = new List<int>();
|
||||
var aliasRepository = provider.GetRequiredService<IAliasRepository>();
|
||||
List<Alias> aliases = aliasRepository.GetAliases().ToList();
|
||||
foreach (Alias alias in aliases)
|
||||
{
|
||||
if (tenants.Contains(alias.TenantId)) continue;
|
||||
tenants.Add(alias.TenantId);
|
||||
|
||||
// use the SiteState to set the Alias explicitly so the tenant can be resolved
|
||||
var siteState = provider.GetRequiredService<SiteState>();
|
||||
siteState.Alias = alias;
|
||||
|
||||
// get services which require tenant resolution
|
||||
// get services
|
||||
var siteRepository = provider.GetRequiredService<ISiteRepository>();
|
||||
var userRepository = provider.GetRequiredService<IUserRepository>();
|
||||
var settingRepository = provider.GetRequiredService<ISettingRepository>();
|
||||
var notificationRepository = provider.GetRequiredService<INotificationRepository>();
|
||||
|
||||
// iterate through sites for this tenant
|
||||
// iterate through sites for current tenant
|
||||
List<Site> sites = siteRepository.GetSites().ToList();
|
||||
foreach (Site site in sites)
|
||||
{
|
||||
@ -47,7 +42,10 @@ namespace Oqtane.Infrastructure
|
||||
// get site settings
|
||||
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
|
||||
Dictionary<string, string> settings = GetSettings(sitesettings);
|
||||
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "")
|
||||
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
|
||||
settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
|
||||
settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
|
||||
settings.ContainsKey("SMTPSender") && settings["SMTPSender"] != "")
|
||||
{
|
||||
// construct SMTP Client
|
||||
var client = new SmtpClient()
|
||||
@ -63,15 +61,44 @@ namespace Oqtane.Infrastructure
|
||||
client.Credentials = new NetworkCredential(settings["SMTPUsername"], settings["SMTPPassword"]);
|
||||
}
|
||||
|
||||
// iterate through notifications
|
||||
// iterate through undelivered notifications
|
||||
int sent = 0;
|
||||
List<Notification> notifications = notificationRepository.GetNotifications(site.SiteId, -1, -1).ToList();
|
||||
foreach (Notification notification in notifications)
|
||||
{
|
||||
// get sender and receiver information if not provided
|
||||
if ((string.IsNullOrEmpty(notification.FromEmail) || string.IsNullOrEmpty(notification.FromDisplayName)) && notification.FromUserId != null)
|
||||
{
|
||||
var user = userRepository.GetUser(notification.FromUserId.Value);
|
||||
if (user != null)
|
||||
{
|
||||
notification.FromEmail = (string.IsNullOrEmpty(notification.FromEmail)) ? user.Email : notification.FromEmail;
|
||||
notification.FromDisplayName = (string.IsNullOrEmpty(notification.FromDisplayName)) ? user.DisplayName : notification.FromDisplayName;
|
||||
}
|
||||
}
|
||||
if ((string.IsNullOrEmpty(notification.ToEmail) || string.IsNullOrEmpty(notification.ToDisplayName)) && notification.ToUserId != null)
|
||||
{
|
||||
var user = userRepository.GetUser(notification.ToUserId.Value);
|
||||
if (user != null)
|
||||
{
|
||||
notification.ToEmail = (string.IsNullOrEmpty(notification.ToEmail)) ? user.Email : notification.ToEmail;
|
||||
notification.ToDisplayName = (string.IsNullOrEmpty(notification.ToDisplayName)) ? user.DisplayName : notification.ToDisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
// validate recipient
|
||||
if (string.IsNullOrEmpty(notification.ToEmail))
|
||||
{
|
||||
log += "Recipient Missing For NotificationId: " + notification.NotificationId + "<br />";
|
||||
notification.IsDeleted = true;
|
||||
notificationRepository.UpdateNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
MailMessage mailMessage = new MailMessage();
|
||||
mailMessage.From = new MailAddress(settings["SMTPUsername"], site.Name);
|
||||
mailMessage.From = new MailAddress(settings["SMTPSender"], site.Name);
|
||||
mailMessage.Subject = notification.Subject;
|
||||
if (notification.FromUserId != null)
|
||||
if (!string.IsNullOrEmpty(notification.FromEmail) && !string.IsNullOrEmpty(notification.FromDisplayName))
|
||||
{
|
||||
mailMessage.Body = "From: " + notification.FromDisplayName + "<" + notification.FromEmail + ">" + "\n";
|
||||
}
|
||||
@ -80,7 +107,7 @@ namespace Oqtane.Infrastructure
|
||||
mailMessage.Body = "From: " + site.Name + "\n";
|
||||
}
|
||||
mailMessage.Body += "Sent: " + notification.CreatedOn + "\n";
|
||||
if (notification.ToUserId != null)
|
||||
if (!string.IsNullOrEmpty(notification.ToEmail) && !string.IsNullOrEmpty(notification.ToDisplayName))
|
||||
{
|
||||
mailMessage.To.Add(new MailAddress(notification.ToEmail, notification.ToDisplayName));
|
||||
mailMessage.Body += "To: " + notification.ToDisplayName + "<" + notification.ToEmail + ">" + "\n";
|
||||
@ -108,12 +135,12 @@ namespace Oqtane.Infrastructure
|
||||
log += ex.Message + "<br />";
|
||||
}
|
||||
}
|
||||
}
|
||||
log += "Notifications Delivered: " + sent + "<br />";
|
||||
}
|
||||
else
|
||||
{
|
||||
log += "SMTP Not Configured" + "<br />";
|
||||
}
|
||||
log += "SMTP Not Configured Properly In Site Settings - Host, Port, SSL, And Sender Are All Required" + "<br />";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>2.0.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
@ -35,6 +34,8 @@
|
||||
<EmbeddedResource Include="Scripts\Tenant.01.00.02.01.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.02.00.00.01.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.02.00.01.01.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.02.00.01.02.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.02.00.01.03.sql" />
|
||||
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
|
||||
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
|
||||
</ItemGroup>
|
||||
|
@ -5,13 +5,38 @@ using Oqtane.Modules;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Themes;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Oqtane.Repository;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
public class HostModel : PageModel
|
||||
{
|
||||
private IConfiguration _configuration;
|
||||
private readonly SiteState _state;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ILocalizationManager _localizationManager;
|
||||
private readonly ILanguageRepository _languages;
|
||||
|
||||
public HostModel(
|
||||
IConfiguration configuration,
|
||||
SiteState state,
|
||||
IAliasRepository aliases,
|
||||
ILocalizationManager localizationManager,
|
||||
ILanguageRepository languages)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_state = state;
|
||||
_aliases = aliases;
|
||||
_localizationManager = localizationManager;
|
||||
_languages = languages;
|
||||
}
|
||||
|
||||
public string HeadResources = "";
|
||||
public string BodyResources = "";
|
||||
|
||||
@ -24,6 +49,27 @@ namespace Oqtane.Pages
|
||||
ProcessModuleControls(assembly);
|
||||
ProcessThemeControls(assembly);
|
||||
}
|
||||
|
||||
// if culture not specified and framework is installed
|
||||
if (HttpContext.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName] == null && !string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
|
||||
{
|
||||
var uri = new Uri(Request.GetDisplayUrl());
|
||||
var alias = _aliases.GetAlias(uri.Authority + "/" + uri.LocalPath.Substring(1));
|
||||
_state.Alias = alias;
|
||||
|
||||
// set default language for site if the culture is not supported
|
||||
var languages = _languages.GetLanguages(alias.SiteId);
|
||||
if (languages.Any() && languages.All(l => l.Code != CultureInfo.CurrentUICulture.Name))
|
||||
{
|
||||
var defaultLanguage = languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First();
|
||||
|
||||
SetLocalizationCookie(defaultLanguage.Code);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLocalizationCookie(_localizationManager.GetDefaultCulture());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessHostResources(Assembly assembly)
|
||||
@ -134,5 +180,12 @@ namespace Oqtane.Pages
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLocalizationCookie(string culture)
|
||||
{
|
||||
HttpContext.Response.Cookies.Append(
|
||||
CookieRequestCultureProvider.DefaultCookieName,
|
||||
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -48,6 +48,27 @@ namespace Oqtane.Repository
|
||||
return _db.Alias.Find(aliasId);
|
||||
}
|
||||
|
||||
public Alias GetAlias(string name)
|
||||
{
|
||||
Alias alias = null;
|
||||
|
||||
List<Alias> aliases = GetAliases().ToList();
|
||||
var segments = name.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// iterate segments in reverse order
|
||||
for (int i = segments.Length; i > 0; i--)
|
||||
{
|
||||
name = string.Join("/", segments, 0, i);
|
||||
alias = aliases.Find(item => item.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
if (alias != null)
|
||||
{
|
||||
break; // found a matching alias
|
||||
}
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void DeleteAlias(int aliasId)
|
||||
{
|
||||
Alias alias = _db.Alias.Find(aliasId);
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@ -10,20 +10,24 @@ namespace Oqtane.Repository
|
||||
{
|
||||
public class DBContextBase : IdentityUserContext<IdentityUser>
|
||||
{
|
||||
private Tenant _tenant;
|
||||
private ITenantResolver _tenantResolver;
|
||||
private IHttpContextAccessor _accessor;
|
||||
|
||||
public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor accessor)
|
||||
{
|
||||
_tenant = tenantResolver.GetTenant();
|
||||
_tenantResolver = tenantResolver;
|
||||
_accessor = accessor;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(_tenant.DBConnectionString
|
||||
var tenant = _tenantResolver.GetTenant();
|
||||
if (tenant != null)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(tenant.DBConnectionString
|
||||
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString())
|
||||
);
|
||||
}
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,32 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class MasterDBContext : DbContext
|
||||
{
|
||||
private IHttpContextAccessor _accessor;
|
||||
private IConfiguration _configuration;
|
||||
|
||||
public MasterDBContext(DbContextOptions<MasterDBContext> options, IHttpContextAccessor accessor) : base(options)
|
||||
public MasterDBContext(DbContextOptions<MasterDBContext> options, IHttpContextAccessor accessor, IConfiguration configuration) : base(options)
|
||||
{
|
||||
_accessor = accessor;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
|
||||
{
|
||||
optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection")
|
||||
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString())
|
||||
);
|
||||
}
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
|
||||
public virtual DbSet<Alias> Alias { get; set; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
|
||||
@ -21,6 +21,8 @@ namespace Oqtane.Repository
|
||||
public virtual DbSet<Folder> Folder { get; set; }
|
||||
public virtual DbSet<File> File { get; set; }
|
||||
|
||||
public virtual DbSet<Language> Language { get; set; }
|
||||
|
||||
public TenantDBContext(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor)
|
||||
{
|
||||
// DBContextBase handles multi-tenant database connections
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using File = Oqtane.Models.File;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
@ -11,11 +13,13 @@ namespace Oqtane.Repository
|
||||
{
|
||||
private TenantDBContext _db;
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
|
||||
public FileRepository(TenantDBContext context, IPermissionRepository permissions)
|
||||
public FileRepository(TenantDBContext context, IPermissionRepository permissions, IFolderRepository folderRepository)
|
||||
{
|
||||
_db = context;
|
||||
_permissions = permissions;
|
||||
_folderRepository = folderRepository;
|
||||
}
|
||||
|
||||
public IEnumerable<File> GetFiles(int folderId)
|
||||
@ -74,5 +78,19 @@ namespace Oqtane.Repository
|
||||
_db.File.Remove(file);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public string GetFilePath(int fileId)
|
||||
{
|
||||
var file = _db.File.Find(fileId);
|
||||
return GetFilePath(file);
|
||||
}
|
||||
|
||||
public string GetFilePath(File file)
|
||||
{
|
||||
if (file == null) return null;
|
||||
var folder = file.Folder ?? _db.Folder.Find(file.FolderId);
|
||||
var filepath = Path.Combine(_folderRepository.GetFolderPath(folder), file.Name);
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Models;
|
||||
@ -11,11 +12,15 @@ namespace Oqtane.Repository
|
||||
{
|
||||
private TenantDBContext _db;
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ITenantResolver _tenants;
|
||||
|
||||
public FolderRepository(TenantDBContext context, IPermissionRepository permissions)
|
||||
public FolderRepository(TenantDBContext context, IPermissionRepository permissions,IWebHostEnvironment environment, ITenantResolver tenants)
|
||||
{
|
||||
_db = context;
|
||||
_permissions = permissions;
|
||||
_environment = environment;
|
||||
_tenants = tenants;
|
||||
}
|
||||
|
||||
public IEnumerable<Folder> GetFolders(int siteId)
|
||||
@ -85,5 +90,17 @@ namespace Oqtane.Repository
|
||||
_db.Folder.Remove(folder);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public string GetFolderPath(int folderId)
|
||||
{
|
||||
Folder folder = _db.Folder.Find(folderId);
|
||||
return GetFolderPath(folder);
|
||||
}
|
||||
|
||||
public string GetFolderPath(Folder folder)
|
||||
{
|
||||
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
@ -9,6 +9,7 @@ namespace Oqtane.Repository
|
||||
Alias AddAlias(Alias alias);
|
||||
Alias UpdateAlias(Alias alias);
|
||||
Alias GetAlias(int aliasId);
|
||||
Alias GetAlias(string name);
|
||||
void DeleteAlias(int aliasId);
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,7 @@ namespace Oqtane.Repository
|
||||
File GetFile(int fileId);
|
||||
File GetFile(int fileId, bool tracking);
|
||||
void DeleteFile(int fileId);
|
||||
string GetFilePath(int fileId);
|
||||
string GetFilePath(File file);
|
||||
}
|
||||
}
|
||||
|
@ -12,5 +12,7 @@ namespace Oqtane.Repository
|
||||
Folder GetFolder(int folderId, bool tracking);
|
||||
Folder GetFolder(int siteId, string path);
|
||||
void DeleteFolder(int folderId);
|
||||
string GetFolderPath(int folderId);
|
||||
string GetFolderPath(Folder folder);
|
||||
}
|
||||
}
|
||||
|
16
Oqtane.Server/Repository/Interfaces/ILanguageRepository.cs
Normal file
16
Oqtane.Server/Repository/Interfaces/ILanguageRepository.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public interface ILanguageRepository
|
||||
{
|
||||
IEnumerable<Language> GetLanguages(int siteId);
|
||||
|
||||
Language AddLanguage(Language language);
|
||||
|
||||
Language GetLanguage(int languageId);
|
||||
|
||||
void DeleteLanguage(int languageId);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
@ -9,6 +9,6 @@ namespace Oqtane.Repository
|
||||
IEnumerable<ModuleDefinition> GetModuleDefinitions(int sideId);
|
||||
ModuleDefinition GetModuleDefinition(int moduleDefinitionId, int sideId);
|
||||
void UpdateModuleDefinition(ModuleDefinition moduleDefinition);
|
||||
void DeleteModuleDefinition(int moduleDefinitionId, int siteId);
|
||||
void DeleteModuleDefinition(int moduleDefinitionId);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
@ -6,5 +6,6 @@ namespace Oqtane.Repository
|
||||
public interface IThemeRepository
|
||||
{
|
||||
IEnumerable<Theme> GetThemes();
|
||||
void DeleteTheme(string ThemeName);
|
||||
}
|
||||
}
|
||||
|
44
Oqtane.Server/Repository/LanguageRepository.cs
Normal file
44
Oqtane.Server/Repository/LanguageRepository.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class LanguageRepository : ILanguageRepository
|
||||
{
|
||||
private TenantDBContext _db;
|
||||
|
||||
public LanguageRepository(TenantDBContext context)
|
||||
{
|
||||
_db = context;
|
||||
}
|
||||
|
||||
public IEnumerable<Language> GetLanguages(int siteId) => _db.Language.Where(l => l.SiteId == siteId);
|
||||
|
||||
public Language AddLanguage(Language language)
|
||||
{
|
||||
if (language.IsDefault)
|
||||
{
|
||||
// Ensure all other languages are not set to current
|
||||
_db.Language
|
||||
.Where(l => l.SiteId == language.SiteId)
|
||||
.ToList()
|
||||
.ForEach(l => l.IsDefault = false);
|
||||
}
|
||||
|
||||
_db.Language.Add(language);
|
||||
_db.SaveChanges();
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
public Language GetLanguage(int languageId) => _db.Language.Find(languageId);
|
||||
|
||||
public void DeleteLanguage(int languageId)
|
||||
{
|
||||
var language = _db.Language.Find(languageId);
|
||||
_db.Language.Remove(language);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -16,7 +17,6 @@ namespace Oqtane.Repository
|
||||
private MasterDBContext _db;
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private List<ModuleDefinition> _moduleDefinitions; // lazy load
|
||||
|
||||
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions)
|
||||
{
|
||||
@ -46,44 +46,71 @@ namespace Oqtane.Repository
|
||||
_db.Entry(moduleDefinition).State = EntityState.Modified;
|
||||
_db.SaveChanges();
|
||||
_permissions.UpdatePermissions(moduleDefinition.SiteId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, moduleDefinition.Permissions);
|
||||
_cache.Remove("moduledefinitions:" + moduleDefinition.SiteId.ToString());
|
||||
}
|
||||
|
||||
public void DeleteModuleDefinition(int moduleDefinitionId, int siteId)
|
||||
public void DeleteModuleDefinition(int moduleDefinitionId)
|
||||
{
|
||||
ModuleDefinition moduleDefinition = _db.ModuleDefinition.Find(moduleDefinitionId);
|
||||
_permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduleDefinitionId);
|
||||
_db.ModuleDefinition.Remove(moduleDefinition);
|
||||
_db.SaveChanges();
|
||||
_cache.Remove("moduledefinitions");
|
||||
}
|
||||
|
||||
public List<ModuleDefinition> LoadModuleDefinitions(int siteId)
|
||||
{
|
||||
// get module definitions for site
|
||||
List<ModuleDefinition> moduleDefinitions = _cache.GetOrCreate("moduledefinitions:" + siteId.ToString(), entry =>
|
||||
// get module definitions
|
||||
List<ModuleDefinition> moduleDefinitions;
|
||||
if (siteId != -1)
|
||||
{
|
||||
moduleDefinitions = _cache.GetOrCreate("moduledefinitions", entry =>
|
||||
{
|
||||
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
|
||||
return LoadSiteModuleDefinitions(siteId);
|
||||
return LoadModuleDefinitions();
|
||||
});
|
||||
|
||||
// get all module definition permissions for site
|
||||
List<Permission> permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
|
||||
|
||||
// populate module definition permissions
|
||||
foreach (ModuleDefinition moduledefinition in moduleDefinitions)
|
||||
{
|
||||
moduledefinition.SiteId = siteId;
|
||||
if (permissions.Count == 0)
|
||||
{
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, moduledefinition.Permissions);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (permissions.Where(item => item.EntityId == moduledefinition.ModuleDefinitionId).Any())
|
||||
{
|
||||
moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledefinition.ModuleDefinitionId).EncodePermissions();
|
||||
}
|
||||
else
|
||||
{
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, moduledefinition.Permissions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clean up any orphaned permissions
|
||||
var ids = new HashSet<int>(moduleDefinitions.Select(item => item.ModuleDefinitionId));
|
||||
foreach (var permission in permissions.Where(item => !ids.Contains(item.EntityId)))
|
||||
{
|
||||
_permissions.DeletePermission(permission.PermissionId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
moduleDefinitions = LoadModuleDefinitions();
|
||||
}
|
||||
|
||||
return moduleDefinitions;
|
||||
}
|
||||
|
||||
private List<ModuleDefinition> LoadSiteModuleDefinitions(int siteId)
|
||||
{
|
||||
if (_moduleDefinitions == null)
|
||||
private List<ModuleDefinition> LoadModuleDefinitions()
|
||||
{
|
||||
// get module assemblies
|
||||
_moduleDefinitions = LoadModuleDefinitionsFromAssemblies();
|
||||
}
|
||||
|
||||
List<ModuleDefinition> moduleDefinitions = _moduleDefinitions;
|
||||
|
||||
List<Permission> permissions = new List<Permission>();
|
||||
if (siteId != -1)
|
||||
{
|
||||
// get module definition permissions for site
|
||||
permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
|
||||
}
|
||||
List<ModuleDefinition> moduleDefinitions = LoadModuleDefinitionsFromAssemblies();
|
||||
|
||||
// get module definitions in database
|
||||
List<ModuleDefinition> moduledefs = _db.ModuleDefinition.ToList();
|
||||
@ -95,13 +122,9 @@ namespace Oqtane.Repository
|
||||
if (moduledef == null)
|
||||
{
|
||||
// new module definition
|
||||
moduledef = new ModuleDefinition {ModuleDefinitionName = moduledefinition.ModuleDefinitionName};
|
||||
moduledef = new ModuleDefinition { ModuleDefinitionName = moduledefinition.ModuleDefinitionName };
|
||||
_db.ModuleDefinition.Add(moduledef);
|
||||
_db.SaveChanges();
|
||||
if (siteId != -1)
|
||||
{
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -126,31 +149,11 @@ namespace Oqtane.Repository
|
||||
moduledefinition.Version = moduledef.Version;
|
||||
}
|
||||
|
||||
if (siteId != -1)
|
||||
{
|
||||
if (permissions.Count == 0)
|
||||
{
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).Any())
|
||||
{
|
||||
moduledefinition.Permissions = permissions.Where(item => item.EntityId == moduledef.ModuleDefinitionId).EncodePermissions();
|
||||
}
|
||||
else
|
||||
{
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.ModuleDefinition, moduledef.ModuleDefinitionId, moduledefinition.Permissions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove module definition from list as it is already synced
|
||||
moduledefs.Remove(moduledef);
|
||||
}
|
||||
|
||||
moduledefinition.ModuleDefinitionId = moduledef.ModuleDefinitionId;
|
||||
moduledefinition.SiteId = siteId;
|
||||
moduledefinition.CreatedBy = moduledef.CreatedBy;
|
||||
moduledefinition.CreatedOn = moduledef.CreatedOn;
|
||||
moduledefinition.ModifiedBy = moduledef.ModifiedBy;
|
||||
@ -160,11 +163,6 @@ namespace Oqtane.Repository
|
||||
// any remaining module definitions are orphans
|
||||
foreach (ModuleDefinition moduledefinition in moduledefs)
|
||||
{
|
||||
if (siteId != -1)
|
||||
{
|
||||
_permissions.DeletePermissions(siteId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId);
|
||||
}
|
||||
|
||||
_db.ModuleDefinition.Remove(moduledefinition); // delete
|
||||
_db.SaveChanges();
|
||||
}
|
||||
@ -175,12 +173,16 @@ namespace Oqtane.Repository
|
||||
private List<ModuleDefinition> LoadModuleDefinitionsFromAssemblies()
|
||||
{
|
||||
List<ModuleDefinition> moduleDefinitions = new List<ModuleDefinition>();
|
||||
|
||||
// iterate through Oqtane module assemblies
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
if (System.IO.File.Exists(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), Utilities.GetTypeName(assembly.FullName) + ".dll")))
|
||||
{
|
||||
moduleDefinitions = LoadModuleDefinitionsFromAssembly(moduleDefinitions, assembly);
|
||||
}
|
||||
}
|
||||
|
||||
return moduleDefinitions;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
@ -20,7 +20,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
return _db.Notification
|
||||
.Where(item => item.SiteId == siteId)
|
||||
.Where(item => item.IsDelivered == false)
|
||||
.Where(item => item.IsDelivered == false && item.IsDeleted == false)
|
||||
.Where(item => item.SendOn == null || item.SendOn < System.DateTime.UtcNow)
|
||||
.ToList();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -408,32 +408,6 @@ namespace Oqtane.Repository
|
||||
Content = ""
|
||||
}
|
||||
}
|
||||
}); pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Tenant Management",
|
||||
Parent = "Admin",
|
||||
Path = "admin/tenants",
|
||||
Icon = Icons.List,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PagePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true)
|
||||
}.EncodePermissions(),
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule
|
||||
{
|
||||
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Tenants.Index).ToModuleDefinitionName(), Title = "Tenant Management", Pane = "Content",
|
||||
ModulePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true)
|
||||
}.EncodePermissions(),
|
||||
Content = ""
|
||||
}
|
||||
}
|
||||
});
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
@ -502,6 +476,37 @@ namespace Oqtane.Repository
|
||||
}
|
||||
});
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Language Management",
|
||||
Parent = "Admin",
|
||||
Path = "admin/languages",
|
||||
Icon = Icons.Text,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PagePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
}.EncodePermissions(),
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule
|
||||
{
|
||||
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Languages.Index).ToModuleDefinitionName(), Title = "Language Management", Pane = "Content",
|
||||
ModulePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
}.EncodePermissions(),
|
||||
Content = ""
|
||||
}
|
||||
}
|
||||
});
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = Icons.Timer, IsNavigation = false, IsPersonalizable = false,
|
||||
PagePermissions = new List<Permission>
|
||||
@ -525,12 +530,7 @@ namespace Oqtane.Repository
|
||||
});
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Sql Management",
|
||||
Parent = "Admin",
|
||||
Path = "admin/sql",
|
||||
Icon = "spreadsheet",
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
Name = "Sql Management", Parent = "Admin", Path = "admin/sql", Icon = Icons.Spreadsheet, IsNavigation = false, IsPersonalizable = false,
|
||||
PagePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
@ -552,12 +552,7 @@ namespace Oqtane.Repository
|
||||
});
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "System Info",
|
||||
Parent = "Admin",
|
||||
Path = "admin/system",
|
||||
Icon = "medical-cross",
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
Name = "System Info", Parent = "Admin", Path = "admin/system", Icon = Icons.MedicalCross, IsNavigation = false, IsPersonalizable = false,
|
||||
PagePermissions = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -9,24 +9,49 @@ namespace Oqtane.Repository
|
||||
{
|
||||
public class TenantResolver : ITenantResolver
|
||||
{
|
||||
private readonly Alias _alias;
|
||||
private readonly Tenant _tenant;
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly IAliasRepository _aliasRepository;
|
||||
private readonly ITenantRepository _tenantRepository;
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
private Alias _alias;
|
||||
private Tenant _tenant;
|
||||
|
||||
public TenantResolver(IHttpContextAccessor accessor, IAliasRepository aliasRepository, ITenantRepository tenantRepository, SiteState siteState)
|
||||
{
|
||||
int aliasId = -1;
|
||||
_accessor = accessor;
|
||||
_aliasRepository = aliasRepository;
|
||||
_tenantRepository = tenantRepository;
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
if (siteState != null && siteState.Alias != null)
|
||||
public Alias GetAlias()
|
||||
{
|
||||
if (_alias == null) ResolveTenant();
|
||||
return _alias;
|
||||
}
|
||||
|
||||
public Tenant GetTenant()
|
||||
{
|
||||
if (_tenant == null) ResolveTenant();
|
||||
return _tenant;
|
||||
}
|
||||
|
||||
private void ResolveTenant()
|
||||
{
|
||||
if (_siteState != null && _siteState.Alias != null)
|
||||
{
|
||||
// background processes can pass in an alias using the SiteState service
|
||||
_alias = siteState.Alias;
|
||||
_alias = _siteState.Alias;
|
||||
}
|
||||
else
|
||||
{
|
||||
int aliasId = -1;
|
||||
|
||||
// get aliasid identifier based on request
|
||||
if (accessor.HttpContext != null)
|
||||
if (_accessor.HttpContext != null)
|
||||
{
|
||||
string[] segments = accessor.HttpContext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] segments = _accessor.HttpContext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (segments.Length > 1 && (segments[1] == "api" || segments[1] == "pages") && segments[0] != "~")
|
||||
{
|
||||
aliasId = int.Parse(segments[0]);
|
||||
@ -34,7 +59,7 @@ namespace Oqtane.Repository
|
||||
}
|
||||
|
||||
// get the alias
|
||||
IEnumerable<Alias> aliases = aliasRepository.GetAliases().ToList(); // cached
|
||||
IEnumerable<Alias> aliases = _aliasRepository.GetAliases().ToList(); // cached
|
||||
if (aliasId != -1)
|
||||
{
|
||||
_alias = aliases.FirstOrDefault(item => item.AliasId == aliasId);
|
||||
@ -44,19 +69,10 @@ namespace Oqtane.Repository
|
||||
if (_alias != null)
|
||||
{
|
||||
// get the tenant
|
||||
IEnumerable<Tenant> tenants = tenantRepository.GetTenants(); // cached
|
||||
IEnumerable<Tenant> tenants = _tenantRepository.GetTenants(); // cached
|
||||
_tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId);
|
||||
}
|
||||
}
|
||||
|
||||
public Alias GetAlias()
|
||||
{
|
||||
return _alias;
|
||||
}
|
||||
|
||||
public Tenant GetTenant()
|
||||
{
|
||||
return _tenant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Themes;
|
||||
@ -10,7 +12,12 @@ namespace Oqtane.Repository
|
||||
{
|
||||
public class ThemeRepository : IThemeRepository
|
||||
{
|
||||
private List<Theme> _themes; // lazy load
|
||||
private readonly IMemoryCache _cache;
|
||||
|
||||
public ThemeRepository(IMemoryCache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public IEnumerable<Theme> GetThemes()
|
||||
{
|
||||
@ -19,12 +26,14 @@ namespace Oqtane.Repository
|
||||
|
||||
private List<Theme> LoadThemes()
|
||||
{
|
||||
if (_themes == null)
|
||||
// get module definitions
|
||||
List<Theme> themes = _cache.GetOrCreate("themes", entry =>
|
||||
{
|
||||
// get themes
|
||||
_themes = LoadThemesFromAssemblies();
|
||||
}
|
||||
return _themes;
|
||||
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
|
||||
return LoadThemesFromAssemblies();
|
||||
});
|
||||
|
||||
return themes;
|
||||
}
|
||||
|
||||
private List<Theme> LoadThemesFromAssemblies()
|
||||
@ -34,9 +43,12 @@ namespace Oqtane.Repository
|
||||
// iterate through Oqtane theme assemblies
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
if (System.IO.File.Exists(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), Utilities.GetTypeName(assembly.FullName) + ".dll")))
|
||||
{
|
||||
themes = LoadThemesFromAssembly(themes, assembly);
|
||||
}
|
||||
}
|
||||
|
||||
return themes;
|
||||
}
|
||||
@ -143,5 +155,10 @@ namespace Oqtane.Repository
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void DeleteTheme(string ThemeName)
|
||||
{
|
||||
_cache.Remove("themes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
Oqtane.Server/Scripts/Tenant.02.00.01.02.sql
Normal file
27
Oqtane.Server/Scripts/Tenant.02.00.01.02.sql
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
|
||||
Version 2.0.1 Tenant migration script
|
||||
|
||||
*/
|
||||
|
||||
CREATE TABLE [dbo].[Language](
|
||||
[LanguageId] [int] IDENTITY(1,1) NOT NULL,
|
||||
[Name] [nvarchar](100) NOT NULL,
|
||||
[Code] [nvarchar](10) NOT NULL,
|
||||
[IsDefault] [bit] NOT NULL,
|
||||
[SiteId] [int] NOT NULL,
|
||||
[CreatedBy] [nvarchar](256) NOT NULL,
|
||||
[CreatedOn] [datetime] NOT NULL,
|
||||
[ModifiedBy] [nvarchar](256) NOT NULL,
|
||||
[ModifiedOn] [datetime] NOT NULL,
|
||||
CONSTRAINT [PK_Language] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[LanguageId] ASC
|
||||
)
|
||||
)
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[Language] WITH CHECK ADD CONSTRAINT [FK_Language_Site] FOREIGN KEY([SiteId])
|
||||
REFERENCES [dbo].[Site] ([SiteId])
|
||||
ON DELETE CASCADE
|
||||
GO
|
17
Oqtane.Server/Scripts/Tenant.02.00.01.03.sql
Normal file
17
Oqtane.Server/Scripts/Tenant.02.00.01.03.sql
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
|
||||
Version 2.0.1 Tenant migration script
|
||||
|
||||
*/
|
||||
|
||||
DELETE FROM [dbo].[Page]
|
||||
WHERE Path = 'admin/tenants';
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[Site] ADD
|
||||
[AdminContainerType] [nvarchar](200) NULL
|
||||
GO
|
||||
|
||||
UPDATE [dbo].[Site] SET AdminContainerType = ''
|
||||
GO
|
||||
|
@ -26,7 +26,6 @@ namespace Oqtane
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private string _webRoot;
|
||||
private Runtime _runtime;
|
||||
private bool _useSwagger;
|
||||
private IWebHostEnvironment _env;
|
||||
@ -48,7 +47,6 @@ namespace Oqtane
|
||||
//add possibility to switch off swagger on production.
|
||||
_useSwagger = Configuration.GetSection("UseSwagger").Value != "false";
|
||||
|
||||
_webRoot = env.WebRootPath;
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data"));
|
||||
|
||||
_env = env;
|
||||
@ -128,13 +126,11 @@ namespace Oqtane
|
||||
services.AddScoped<ISqlService, SqlService>();
|
||||
services.AddScoped<ISystemService, SystemService>();
|
||||
services.AddScoped<ILocalizationService, LocalizationService>();
|
||||
services.AddScoped<ILanguageService, LanguageService>();
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
services.AddDbContext<MasterDBContext>(options =>
|
||||
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")
|
||||
.Replace("|DataDirectory|", AppContext.GetData("DataDirectory")?.ToString())
|
||||
));
|
||||
services.AddDbContext<MasterDBContext>(options => { });
|
||||
services.AddDbContext<TenantDBContext>(options => { });
|
||||
|
||||
services.AddIdentityCore<IdentityUser>(options => { })
|
||||
@ -183,7 +179,7 @@ namespace Oqtane
|
||||
services.AddSingleton<IDatabaseManager, DatabaseManager>();
|
||||
|
||||
// install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain )
|
||||
InstallationManager.InstallPackages("Modules,Themes", _webRoot);
|
||||
InstallationManager.InstallPackages("Modules,Themes", _env.WebRootPath, _env.ContentRootPath);
|
||||
|
||||
// register transient scoped core services
|
||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||
@ -213,6 +209,7 @@ namespace Oqtane
|
||||
services.AddTransient<ISiteTemplateRepository, SiteTemplateRepository>();
|
||||
services.AddTransient<ISqlRepository, SqlRepository>();
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
services.AddTransient<ILanguageRepository, LanguageRepository>();
|
||||
|
||||
// load the external assemblies into the app domain, install services
|
||||
services.AddOqtane(_runtime, _supportedCultures);
|
||||
|
@ -4,4 +4,4 @@ XCOPY "..\Server\bin\Debug\net5.0\[Owner].[Module].Server.Oqtane.dll" "..\..\[Ro
|
||||
XCOPY "..\Server\bin\Debug\net5.0\[Owner].[Module].Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\net5.0\[Owner].[Module].Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\net5.0\[Owner].[Module].Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
|
||||
XCOPY "..\Server\wwwroot\Modules\[Owner].[Module]\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Modules\[Owner].[Module]\" /Y /S /I
|
||||
XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
||||
|
@ -19,6 +19,11 @@
|
||||
<EmbeddedResource Include="Scripts\[Owner].[Module].Uninstall.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\_content\**\*.*" />
|
||||
<None Include="wwwroot\_content\**\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0" />
|
||||
|
11
Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/_content/Placeholder.txt
vendored
Normal file
11
Oqtane.Server/wwwroot/Modules/Templates/External/Server/wwwroot/_content/Placeholder.txt
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
The _content folder should only contain static resources from shared razor component libraries (RCLs). Static resources can be extracted from shared RCL Nuget packages by executing a Publish task on the module's Server project to a local folder and copying the files from the _content folder which is created. Each shared RCL would have its own appropriately named subfolder within the module's _content folder.
|
||||
|
||||
ie.
|
||||
|
||||
/_content
|
||||
/Radzen.Blazor
|
||||
/css
|
||||
/fonts
|
||||
/syncfusion.blazor
|
||||
/scripts
|
||||
/styles
|
9
Oqtane.Shared/Interfaces/ISettingsControl.cs
Normal file
9
Oqtane.Shared/Interfaces/ISettingsControl.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Interfaces
|
||||
{
|
||||
public interface ISettingsControl
|
||||
{
|
||||
Task UpdateSettings();
|
||||
}
|
||||
}
|
25
Oqtane.Shared/Models/Language.cs
Normal file
25
Oqtane.Shared/Models/Language.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Models
|
||||
{
|
||||
public class Language : IAuditable
|
||||
{
|
||||
public int LanguageId { get; set; }
|
||||
|
||||
public int? SiteId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
public string CreatedBy { get; set; }
|
||||
|
||||
public DateTime CreatedOn { get; set; }
|
||||
|
||||
public string ModifiedBy { get; set; }
|
||||
|
||||
public DateTime ModifiedOn { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Oqtane.Models
|
||||
@ -23,6 +23,50 @@ namespace Oqtane.Models
|
||||
public DateTime? DeletedOn { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public DateTime? SendOn { get; set; }
|
||||
|
||||
public Notification() {}
|
||||
|
||||
public Notification(int siteId, User from, User to, string subject, string body, int? parentId)
|
||||
{
|
||||
SiteId = siteId;
|
||||
if (from != null)
|
||||
{
|
||||
FromUserId = from.UserId;
|
||||
FromDisplayName = from.DisplayName;
|
||||
FromEmail = from.Email;
|
||||
}
|
||||
if (to != null)
|
||||
{
|
||||
ToUserId = to.UserId;
|
||||
ToDisplayName = to.DisplayName;
|
||||
ToEmail = to.Email;
|
||||
}
|
||||
Subject = subject;
|
||||
Body = body;
|
||||
ParentId = parentId;
|
||||
CreatedOn = DateTime.UtcNow;
|
||||
IsDelivered = false;
|
||||
DeliveredOn = null;
|
||||
SendOn = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public Notification(int siteId, string fromDisplayName, string fromEmail, string toDisplayName, string toEmail, string subject, string body)
|
||||
{
|
||||
SiteId = siteId;
|
||||
FromUserId = null;
|
||||
FromDisplayName = fromDisplayName;
|
||||
FromEmail = fromEmail;
|
||||
ToUserId = null;
|
||||
ToDisplayName = toDisplayName;
|
||||
ToEmail = toEmail;
|
||||
Subject = subject;
|
||||
Body = body;
|
||||
ParentId = null;
|
||||
CreatedOn = DateTime.UtcNow;
|
||||
IsDelivered = false;
|
||||
DeliveredOn = null;
|
||||
SendOn = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Oqtane.Models
|
||||
@ -13,6 +13,7 @@ namespace Oqtane.Models
|
||||
public string DefaultThemeType { get; set; }
|
||||
public string DefaultLayoutType { get; set; }
|
||||
public string DefaultContainerType { get; set; }
|
||||
public string AdminContainerType { get; set; }
|
||||
public bool PwaIsEnabled { get; set; }
|
||||
public int? PwaAppIconFileId { get; set; }
|
||||
public int? PwaSplashIconFileId { get; set; }
|
||||
|
@ -44,7 +44,7 @@ namespace Oqtane.Shared {
|
||||
public const string MasterTenant = TenantNames.Master;
|
||||
public const string DefaultSite = "Default Site";
|
||||
|
||||
const string RoleObsoleteMessage = "Use the corresponding memeber from Oqtane.Shared.RoleNames";
|
||||
const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames";
|
||||
|
||||
[Obsolete(RoleObsoleteMessage)]
|
||||
public const string AllUsersRole = RoleNames.Everyone;
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Oqtane.Shared
|
||||
namespace Oqtane.Shared
|
||||
{
|
||||
public class InstallConfig
|
||||
{
|
||||
@ -14,5 +14,6 @@
|
||||
public string DefaultTheme { get; set; }
|
||||
public string DefaultLayout { get; set; }
|
||||
public string DefaultContainer { get; set; }
|
||||
public string DefaultAdminContainer { get; set; }
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user