commit
8da55e1926
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,6 +8,8 @@ msbuild.binlog
|
|||
*.binlog
|
||||
*.nupkg
|
||||
|
||||
*.idea
|
||||
|
||||
Oqtane.Server/appsettings.json
|
||||
Oqtane.Server/Data/*.mdf
|
||||
Oqtane.Server/Data/*.ldf
|
||||
|
|
|
@ -1,31 +1,39 @@
|
|||
@inject IInstallationService InstallationService
|
||||
@inject IInstallationService InstallationService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@if (!_installed)
|
||||
@if (!_installation.Success)
|
||||
{
|
||||
<Installer />
|
||||
}
|
||||
else
|
||||
{
|
||||
<CascadingAuthenticationState>
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
</CascadingAuthenticationState>
|
||||
@if (string.IsNullOrEmpty(_installation.Message))
|
||||
{
|
||||
<CascadingAuthenticationState>
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
</CascadingAuthenticationState>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="app-alert">
|
||||
@_installation.Message
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private Installation _installation;
|
||||
private bool _initialized;
|
||||
private bool _installed;
|
||||
|
||||
private PageState PageState { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
var installation = await InstallationService.IsInstalled();
|
||||
_installed = installation.Success;
|
||||
_installation = await InstallationService.IsInstalled();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<tr>
|
||||
<td colspan="2" align="center">
|
||||
<Label For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
|
||||
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="Browse,View,Edit" Permissions="@_permissions" @ref="_permissionGrid" />
|
||||
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<td><a href="@(ContentUrl(context.FileId))" target="_new">@context.Name</a></td>
|
||||
<td>@context.ModifiedOn</td>
|
||||
<td>@context.Extension.ToUpper() @Localizer["File"]</td>
|
||||
<td>@(context.Size / 1000) KB</td>
|
||||
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
@if (_files.Count == 0)
|
||||
|
|
|
@ -12,8 +12,8 @@ else
|
|||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<label>@Localizer["Level:"] </label>
|
||||
<select class="form-control" @onchange="(e => LevelChanged(e))">
|
||||
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
|
||||
<select id="level" class="form-control" @onchange="(e => LevelChanged(e))">
|
||||
<option value="-"><@Localizer["All Levels"]></option>
|
||||
<option value="Trace">@Localizer["Trace"]</option>
|
||||
<option value="Debug">@Localizer["Debug"]</option>
|
||||
|
@ -24,8 +24,8 @@ else
|
|||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<label>@Localizer["Function:"] </label>
|
||||
<select class="form-control" @onchange="(e => FunctionChanged(e))">
|
||||
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
|
||||
<select id="function" class="form-control" @onchange="(e => FunctionChanged(e))">
|
||||
<option value="-"><@Localizer["All Functions"]></option>
|
||||
<option value="Create">@Localizer["Create"]</option>
|
||||
<option value="Read">@Localizer["Read"]</option>
|
||||
|
@ -36,8 +36,8 @@ else
|
|||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<label>@Localizer["Rows:"] </label>
|
||||
<select class="form-control" @onchange="(e => RowsChanged(e))">
|
||||
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
|
||||
<select id="rows" class="form-control" @onchange="(e => RowsChanged(e))">
|
||||
<option value="10">10</option>
|
||||
<option value="50">50</option>
|
||||
<option value="100">100</option>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
@using System.Text.RegularExpressions
|
||||
@using System.IO;
|
||||
|
||||
@if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||
@if (string.IsNullOrEmpty(_moduledefinitionname) && _systeminfo != null && _templates != null)
|
||||
{
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
|
@ -38,13 +38,15 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="template" HelpText="Select a module template. Internal modules are created inside of the framework solution. External modules are created outside of the framework solution." ResourceKey="Template">Template: </Label>
|
||||
<Label For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
|
||||
<option value="-"><@Localizer["Select Template"]></option>
|
||||
<option value="internal">@Localizer["Internal"]</option>
|
||||
<option value="external">@Localizer["External"]</option>
|
||||
@foreach (string template in _templates)
|
||||
{
|
||||
<option value="@template">@template</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -85,28 +87,41 @@ else
|
|||
}
|
||||
|
||||
@code {
|
||||
private string _moduledefinitionname = "";
|
||||
private string _moduledefinitionname = string.Empty;
|
||||
private string _owner = string.Empty;
|
||||
private string _module = string.Empty;
|
||||
private string _description = string.Empty;
|
||||
private string _template = "-";
|
||||
public string _reference = Constants.Version;
|
||||
private string _reference = Constants.Version;
|
||||
private string _location = string.Empty;
|
||||
|
||||
private Dictionary<string, string> _systeminfo;
|
||||
private List<string> _templates;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
|
||||
if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||
try
|
||||
{
|
||||
_owner = ModuleState.Title;
|
||||
_module = ModuleState.Title;
|
||||
_description = ModuleState.Title;
|
||||
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
|
||||
_systeminfo = await SystemService.GetSystemInfoAsync();
|
||||
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(_moduledefinitionname))
|
||||
{
|
||||
_owner = ModuleState.Title;
|
||||
_module = ModuleState.Title;
|
||||
_description = ModuleState.Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Once You Have Compiled The Module And Restarted The Application You Can Activate The Module Below"], MessageType.Info);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(Localizer["Once You Have Compiled The Module And Restarted The Application You Can Activate The Module Below"], MessageType.Info);
|
||||
await logger.LogError(ex, "Error Loading Module Creator");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +138,8 @@ else
|
|||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
|
||||
GetLocation();
|
||||
|
||||
AddModuleMessage(Localizer["The Source Code For Your Module Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
|
||||
}
|
||||
else
|
||||
|
@ -161,38 +178,21 @@ else
|
|||
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
|
||||
}
|
||||
|
||||
private async void TemplateChanged(ChangeEventArgs e)
|
||||
private void TemplateChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
_template = (string)e.Value;
|
||||
GetLocation();
|
||||
}
|
||||
|
||||
private void GetLocation()
|
||||
{
|
||||
_location = string.Empty;
|
||||
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
|
||||
{
|
||||
_location = string.Empty;
|
||||
_template = (string)e.Value;
|
||||
if (_template != "-")
|
||||
{
|
||||
Dictionary<string, string> systeminfo = await SystemService.GetSystemInfoAsync();
|
||||
if (systeminfo != null)
|
||||
{
|
||||
string[] path = systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
|
||||
if (_template == "internal")
|
||||
{
|
||||
_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.DirectorySeparatorChar, path, 0, path.Length - 2) +
|
||||
Path.DirectorySeparatorChar + _owner + "." + _module;
|
||||
}
|
||||
}
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Getting System Info {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error Getting System Info"], MessageType.Error);
|
||||
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
|
||||
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
|
||||
Path.DirectorySeparatorChar + _owner + "." + _module;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ else
|
|||
<td>
|
||||
@if (context.AssemblyName != "Oqtane.Client")
|
||||
{
|
||||
<ActionDialog Header="Delete Module" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Module?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
||||
<ActionDialog Header="Delete Module" Message="@Localizer["Are You Sure You Wish To Delete The {0} Module?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
|
|
|
@ -101,18 +101,11 @@
|
|||
<Label For="Theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
|
||||
<select id="Theme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><@Localizer["Inherit From Site"]></option>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
if (theme.TypeName == _themetype)
|
||||
{
|
||||
<option value="@theme.TypeName" selected>@theme.Name</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
|
|
|
@ -23,18 +23,11 @@
|
|||
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="Parent" class="form-control" @onchange="(e => ParentChanged(e))">
|
||||
<select id="Parent" class="form-control" value="@_parentid" @onchange="(e => ParentChanged(e))">
|
||||
<option value="-1"><@Localizer["Site Root"]></option>
|
||||
@foreach (Page page in _pageList)
|
||||
{
|
||||
if (page.PageId.ToString() == _parentid)
|
||||
{
|
||||
<option value="@(page.PageId)" selected>@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||
}
|
||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
|
@ -112,18 +105,11 @@
|
|||
<Label For="Theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><Inherit From Site></option>
|
||||
<select id="Theme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><@Localizer["Inherit From Site"]></option>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
if (theme.TypeName == _themetype)
|
||||
{
|
||||
<option value="@theme.TypeName" selected>@theme.Name</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
|
||||
<td><ActionDialog Header="Delete Page" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Page?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||
<td><ActionDialog Header="Delete Page" Message="@Localizer["Are You Sure You Wish To Delete The {0} Page?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||
<td>@(new string('-', context.Level * 2))@(context.Name)</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
|
|
|
@ -19,7 +19,7 @@ else
|
|||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" ResourceKey="EditProfile" /></td>
|
||||
<td><ActionDialog Header="Delete Profile" Message="@("Are You Sure You Wish To Delete " + context.Name + "?")" Action="Delete" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
||||
<td><ActionDialog Header="Delete Profile" Message="@Localizer["Are You Sure You Wish To Delete {0}?", context.Name]" Action="Delete" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
||||
<td>@context.Name</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
|
@ -30,9 +30,9 @@ else
|
|||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
await GetProfilesAsync();
|
||||
}
|
||||
|
||||
private async Task DeleteProfile(int profileId)
|
||||
|
@ -41,7 +41,12 @@ else
|
|||
{
|
||||
await ProfileService.DeleteProfileAsync(profileId);
|
||||
await logger.LogInformation("Profile Deleted {ProfileId}", profileId);
|
||||
|
||||
AddModuleMessage(Localizer["Profile Deleted"], MessageType.Success);
|
||||
|
||||
await GetProfilesAsync();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -49,4 +54,9 @@ else
|
|||
AddModuleMessage(Localizer["Error Deleting Profile"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetProfilesAsync()
|
||||
{
|
||||
_profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,12 @@
|
|||
<td>@context.DeletedOn</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
@if (_pages.Any())
|
||||
{
|
||||
<div style="text-align:right;">
|
||||
<ActionDialog Header="Delete All Pages" Message="Are You Sure You Wish To Permanently Delete All Pages?" Action="Delete All Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Modules" ResourceKey="Modules">
|
||||
|
@ -59,6 +65,13 @@
|
|||
<td>@context.DeletedOn</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
@if (_modules.Any())
|
||||
{
|
||||
<div style="text-align:right;">
|
||||
<ActionDialog Header="Delete All Modules" Message="Are You Sure You Wish To Permanently Delete All Modules?" Action="Delete All Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
||||
</div>
|
||||
}
|
||||
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
|
@ -126,6 +139,28 @@
|
|||
}
|
||||
}
|
||||
|
||||
private async Task DeleteAllPages()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (Page page in _pages)
|
||||
{
|
||||
await PageService.DeletePageAsync(page.PageId);
|
||||
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
||||
}
|
||||
|
||||
await logger.LogInformation("Pages Permanently Deleted");
|
||||
await Load();
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestoreModule(Module module)
|
||||
{
|
||||
try
|
||||
|
@ -167,4 +202,31 @@
|
|||
AddModuleMessage(Localizer["Error Permanently Deleting Module"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteAllModules()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (Module module in _modules)
|
||||
{
|
||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||
// check if there are any remaining module instances in the site
|
||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||
|
||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
||||
{
|
||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||
}
|
||||
}
|
||||
|
||||
await logger.LogInformation("Modules Permanently Deleted");
|
||||
await Load();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error Permanently Deleting Modules"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,146 +7,143 @@
|
|||
@inject IThemeService ThemeService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject INotificationService NotificationService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="Enter the site name" ResourceKey="Name">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="logo" HelpText="Upload a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="favicon" HelpText="Select Your default icon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><@Localizer["Select Theme"]></option>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
if (theme.TypeName == _themetype)
|
||||
{
|
||||
<option value="@theme.TypeName" selected>@theme.Name</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
@if (_layouts.Count > 0)
|
||||
{
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultLayout" HelpText="Select the sites default layout" ResourceKey="DefaultLayout">Default Layout: </Label>
|
||||
<Label For="name" HelpText="Enter the site name" ResourceKey="Name">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
|
||||
<option value="-"><@Localizer["Select Layout"]></option>
|
||||
@foreach (var layout in _layouts)
|
||||
<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>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
|
||||
</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>
|
||||
</td>
|
||||
<td>
|
||||
<select id="allowRegister" class="form-control" @bind="@_allowregistration">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="isDeleted" class="form-control" @bind="@_isdeleted">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><@Localizer["Select Theme"]></option>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
<option value="@(layout.TypeName)">@(layout.Name)</option>
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultContainer" class="form-control" @bind="@_containertype">
|
||||
<option value="-"><@Localizer["Select Container"]></option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultAdminContainer" class="form-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>
|
||||
</td>
|
||||
<td>
|
||||
<select id="allowRegister" class="form-control" @bind="@_allowregistration">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="isDeleted" class="form-control" @bind="@_isdeleted">
|
||||
<option value="True">@Localizer["Yes"]</option>
|
||||
<option value="False">@Localizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@if (_layouts.Count > 0)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultLayout" HelpText="Select the sites default layout" ResourceKey="DefaultLayout">Default Layout: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
|
||||
<option value="-"><@Localizer["Select Layout"]></option>
|
||||
@foreach (var layout in _layouts)
|
||||
{
|
||||
<option value="@(layout.TypeName)">@(layout.Name)</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultContainer" class="form-control" @bind="@_containertype">
|
||||
<option value="-"><@Localizer["Select Container"]></option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultAdminContainer" class="form-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>
|
||||
</table>
|
||||
</Section>
|
||||
<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"]
|
||||
<strong>@Localizer["Please Note That SMTP Requires The Notification Job To Be Enabled In Scheduled Jobs"]</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -201,6 +198,8 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Test SMTP Configuration"]</button>
|
||||
<br /><br />
|
||||
</Section>
|
||||
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
|
||||
<table class="table table-borderless">
|
||||
|
@ -420,6 +419,13 @@
|
|||
site.LogoFileId = logofileid;
|
||||
}
|
||||
|
||||
|
||||
var faviconFieldId = _faviconfilemanager.GetFileId();
|
||||
if (faviconFieldId != -1)
|
||||
{
|
||||
site.FaviconFileId = faviconFieldId;
|
||||
}
|
||||
|
||||
site.DefaultThemeType = _themetype;
|
||||
site.DefaultLayoutType = (_layouttype == "-" ? string.Empty : _layouttype);
|
||||
site.DefaultContainerType = _containertype;
|
||||
|
@ -475,7 +481,6 @@
|
|||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
AddModuleMessage(Localizer["Site Settings Saved"], MessageType.Success);
|
||||
}
|
||||
}
|
||||
|
@ -495,4 +500,36 @@
|
|||
AddModuleMessage(Localizer["Error Saving Site"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendEmail()
|
||||
{
|
||||
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
|
||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
|
||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
|
||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
|
||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
|
||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site SMTP Settings Saved");
|
||||
|
||||
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User.DisplayName, PageState.User.Email, PageState.User.DisplayName, PageState.User.Email, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
||||
AddModuleMessage(Localizer["SMTP Settings Saved And A Message Has Been Sent To The Email Address Associated To Your User Account... Please Wait A Few Minutes For Delivery. If You Do Not Receive The Email Please Review The Notification Job In Scheduled Jobs For Any Log Details."], MessageType.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Testing SMTP Configuration");
|
||||
AddModuleMessage(Localizer["Error Testing SMTP Configuration"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["You Must Specify The SMTP Host, Port, And Sender"], MessageType.Warning);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,18 +31,11 @@
|
|||
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
|
||||
<select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
|
||||
<option value="-"><@Localizer["Select Theme"]></option>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
if (theme.TypeName == _themetype)
|
||||
{
|
||||
<option value="@theme.TypeName" selected>@theme.Name</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
|
|
|
@ -22,7 +22,7 @@ else
|
|||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.AliasId.ToString())" ResourceKey="EditSite" /></td>
|
||||
<td><ActionDialog Header="Delete Site" Message="@Localizer["Are You Sure You Wish To Delete The {0} Site?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteSite(context))" ResourceKey="DeleteSite" /></td>
|
||||
<td><a href="@(_scheme + context.Name)">@context.Name</a></td>
|
||||
<td><a href="@(_scheme + context.Name +"?reload")">@context.Name</a></td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="version" HelpText="The version of the thene" ResourceKey="Version">Version: </Label>
|
||||
<Label For="version" HelpText="The version of the theme" ResourceKey="Version">Version: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
{
|
||||
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);
|
||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
|
|
|
@ -183,7 +183,7 @@
|
|||
{
|
||||
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);
|
||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
|
|
|
@ -85,7 +85,7 @@ else
|
|||
var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
await UserService.DeleteUserAsync(user.UserId);
|
||||
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
||||
await logger.LogInformation("User Deleted {User}", UserRole.User);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
|
|
@ -12,21 +12,14 @@
|
|||
@if (ShowFolders || FolderId <= 0)
|
||||
{
|
||||
<div>
|
||||
<select class="form-control" @onchange="(e => FolderChanged(e))">
|
||||
<select class="form-control" value="@FolderId" @onchange="(e => FolderChanged(e))">
|
||||
@if (string.IsNullOrEmpty(Folder))
|
||||
{
|
||||
<option value="-1"><@Localizer["Select Folder"]></option>
|
||||
}
|
||||
@foreach (Folder folder in _folders)
|
||||
{
|
||||
if (folder.FolderId == FolderId)
|
||||
{
|
||||
<option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -34,18 +27,11 @@
|
|||
@if (ShowFiles)
|
||||
{
|
||||
<div>
|
||||
<select class="form-control" @onchange="(e => FileChanged(e))">
|
||||
<select class="form-control" value="@FileId" @onchange="(e => FileChanged(e))">
|
||||
<option value="-1"><@Localizer["Select File"]></option>
|
||||
@foreach (File file in _files)
|
||||
{
|
||||
if (file.FileId == FileId)
|
||||
{
|
||||
<option value="@(file.FileId)" selected>@(file.Name)</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@(file.FileId)">@(file.Name)</option>
|
||||
}
|
||||
<option value="@(file.FileId)">@(file.Name)</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@namespace Oqtane.Modules.Controls
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@typeparam TableItem
|
||||
|
||||
|
@ -114,10 +114,10 @@
|
|||
@code {
|
||||
private int _pages = 0;
|
||||
private int _page = 1;
|
||||
private int _maxItems;
|
||||
private int _maxPages;
|
||||
private int _startPage;
|
||||
private int _endPage;
|
||||
private int _maxItems = 10;
|
||||
private int _maxPages = 5;
|
||||
private int _startPage = 0;
|
||||
private int _endPage = 0;
|
||||
|
||||
[Parameter]
|
||||
public string Format { get; set; }
|
||||
|
@ -172,24 +172,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(PageSize))
|
||||
{
|
||||
_maxItems = 10;
|
||||
}
|
||||
else
|
||||
if (!string.IsNullOrEmpty(PageSize))
|
||||
{
|
||||
_maxItems = int.Parse(PageSize);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(DisplayPages))
|
||||
{
|
||||
_maxPages = 5;
|
||||
}
|
||||
else
|
||||
if (!string.IsNullOrEmpty(DisplayPages))
|
||||
{
|
||||
_maxPages = int.Parse(DisplayPages);
|
||||
}
|
||||
|
||||
_page = 1;
|
||||
_startPage = 0;
|
||||
_endPage = 0;
|
||||
|
||||
if (Items != null)
|
||||
{
|
||||
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -14,7 +14,7 @@
|
|||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>true</IsPackable>
|
||||
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||
|
|
0
Oqtane.Client/Resources/.gitkeep
Normal file
0
Oqtane.Client/Resources/.gitkeep
Normal file
|
@ -13,5 +13,6 @@ namespace Oqtane.Services
|
|||
Task InstallModuleDefinitionsAsync();
|
||||
Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId);
|
||||
Task<ModuleDefinition> CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
|
||||
Task<List<string>> GetModuleDefinitionTemplatesAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Oqtane.Models;
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Services
|
||||
|
@ -13,7 +13,7 @@ namespace Oqtane.Services
|
|||
|
||||
Task<User> UpdateUserAsync(User user);
|
||||
|
||||
Task DeleteUserAsync(int userId);
|
||||
Task DeleteUserAsync(int userId, int siteId);
|
||||
|
||||
Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent);
|
||||
|
||||
|
|
|
@ -53,5 +53,11 @@ namespace Oqtane.Services
|
|||
{
|
||||
return await PostJsonAsync($"{Apiurl}", moduleDefinition);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetModuleDefinitionTemplatesAsync()
|
||||
{
|
||||
List<string> templates = await GetJsonAsync<List<string>>($"{Apiurl}/templates");
|
||||
return templates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Oqtane.Shared;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Models;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -36,9 +36,9 @@ namespace Oqtane.Services
|
|||
return await PutJsonAsync<User>($"{Apiurl}/{user.UserId}", user);
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(int userId)
|
||||
public async Task DeleteUserAsync(int userId, int siteId)
|
||||
{
|
||||
await DeleteAsync($"{Apiurl}/{userId}");
|
||||
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
|
||||
}
|
||||
|
||||
public async Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent)
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
{
|
||||
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 class="nav-link active px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="dropdown-item" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
<a class="nav-link px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ else
|
|||
{
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -43,7 +43,7 @@ else
|
|||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
</li>
|
||||
|
@ -55,7 +55,7 @@ else
|
|||
{
|
||||
<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" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
<MenuItemsHorizontal ParentPage="childPage" Pages="Pages" />
|
||||
|
@ -65,7 +65,7 @@ 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" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
<MenuItemsHorizontal ParentPage="childPage" Pages="Pages" />
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{
|
||||
<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" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -18,7 +18,7 @@
|
|||
{
|
||||
<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" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
</li>
|
||||
|
@ -38,7 +38,7 @@ else
|
|||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<FontIcon Value="@childPage.Icon" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="sr-only">(current)</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -47,7 +47,7 @@ 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" />
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -18,7 +18,7 @@ else
|
|||
|
||||
@code {
|
||||
private bool _useadminborder = false;
|
||||
private string _paneadminborder = "container";
|
||||
private string _paneadminborder = "app-pane-admin-border";
|
||||
private string _panetitle = "";
|
||||
|
||||
[CascadingParameter]
|
||||
|
@ -34,12 +34,11 @@ else
|
|||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && Name != PaneNames.Admin)
|
||||
{
|
||||
_useadminborder = true;
|
||||
_paneadminborder = "app-pane-admin-border";
|
||||
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
_paneadminborder = "container";
|
||||
_useadminborder = false;
|
||||
_panetitle = "";
|
||||
}
|
||||
|
||||
|
@ -130,4 +129,4 @@ else
|
|||
builder.SetKey(module.PageModuleId);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
var urlparameters = string.Empty;
|
||||
var editmode = false;
|
||||
var reload = Reload.None;
|
||||
var lastsyncdate = DateTime.UtcNow;
|
||||
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
||||
var runtime = GetRuntime();
|
||||
|
||||
Uri uri = new Uri(_absoluteUri);
|
||||
|
@ -107,9 +107,14 @@
|
|||
SiteState.Alias = alias; // set state for services
|
||||
lastsyncdate = alias.SyncDate;
|
||||
|
||||
// process any sync events for site
|
||||
// process any sync events
|
||||
if (reload != Reload.Site && alias.SyncEvents.Any())
|
||||
{
|
||||
// if running on WebAssembly reload the client application if the server application was restarted
|
||||
if (runtime == Shared.Runtime.WebAssembly && PageState != null && alias.SyncEvents.Exists(item => item.TenantId == -1))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri + (!_absoluteUri.Contains("?") ? "?" : "&") + "reload", true);
|
||||
}
|
||||
if (alias.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == alias.SiteId))
|
||||
{
|
||||
reload = Reload.Site;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -13,7 +13,7 @@
|
|||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
|
||||
<tags>oqtane</tags>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
|
||||
<summary>A modular application framework for Blazor</summary>
|
||||
</metadata>
|
||||
<files>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -13,7 +13,7 @@
|
|||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
|
||||
<tags>oqtane framework</tags>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
|
||||
<summary>A modular application framework for Blazor</summary>
|
||||
</metadata>
|
||||
<files>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -13,7 +13,7 @@
|
|||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
|
||||
<tags>oqtane</tags>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
|
||||
<summary>A modular application framework for Blazor</summary>
|
||||
</metadata>
|
||||
<files>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -13,7 +13,7 @@
|
|||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
|
||||
<tags>oqtane</tags>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
|
||||
<summary>A modular application framework for Blazor</summary>
|
||||
</metadata>
|
||||
<files>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.0.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.1.Install.zip" -Force
|
|
@ -1 +1 @@
|
|||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.0.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.1.Upgrade.zip" -Force
|
|
@ -55,8 +55,7 @@ namespace Oqtane.Controllers
|
|||
[HttpGet("installed")]
|
||||
public Installation IsInstalled()
|
||||
{
|
||||
bool isInstalled = _databaseManager.IsInstalled();
|
||||
return new Installation {Success = isInstalled, Message = string.Empty};
|
||||
return _databaseManager.IsInstalled();
|
||||
}
|
||||
|
||||
[HttpGet("upgrade")]
|
||||
|
@ -130,7 +129,14 @@ namespace Oqtane.Controllers
|
|||
var instance = Activator.CreateInstance(type) as IModule;
|
||||
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (!list.Contains(name)) list.Insert(0, name);
|
||||
if (System.IO.File.Exists(Path.Combine(binFolder, name + ".dll")))
|
||||
{
|
||||
if (!list.Contains(name)) list.Insert(0, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Module " + instance.ModuleDefinition.ModuleDefinitionName + " dependency " + name + ".dll does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
|
||||
|
@ -138,7 +144,14 @@ namespace Oqtane.Controllers
|
|||
var instance = Activator.CreateInstance(type) as ITheme;
|
||||
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (!list.Contains(name)) list.Insert(0, name);
|
||||
if (System.IO.File.Exists(Path.Combine(binFolder, name + ".dll")))
|
||||
{
|
||||
if (!list.Contains(name)) list.Insert(0, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Theme " + instance.Theme.ThemeName + " dependency " + name + ".dll does not exist" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ using Oqtane.Repository;
|
|||
using Oqtane.Security;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Xml.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
|
@ -135,6 +133,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
// use assets.json to clean up file resources
|
||||
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(Path.Combine(assetpath, "assets.json")));
|
||||
assets.Reverse();
|
||||
foreach(string asset in assets)
|
||||
{
|
||||
// legacy support for assets that were stored as absolute paths
|
||||
|
@ -142,6 +141,10 @@ namespace Oqtane.Controllers
|
|||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
System.IO.File.Delete(filepath);
|
||||
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
|
||||
{
|
||||
Directory.Delete(Path.GetDirectoryName(filepath));
|
||||
}
|
||||
}
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
|
||||
|
@ -169,6 +172,20 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
// GET: api/<controller>/templates
|
||||
[HttpGet("templates")]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public List<string> Get()
|
||||
{
|
||||
var templates = new List<String>();
|
||||
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", Path.DirectorySeparatorChar.ToString());
|
||||
foreach (string directory in Directory.GetDirectories(templatePath))
|
||||
{
|
||||
templates.Add(directory.Replace(templatePath, ""));
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
|
||||
// POST api/<controller>?moduleid=x
|
||||
[HttpPost]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
|
@ -180,30 +197,12 @@ namespace Oqtane.Controllers
|
|||
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
|
||||
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template,Path.DirectorySeparatorChar.ToString());
|
||||
|
||||
if (moduleDefinition.Template == "internal")
|
||||
{
|
||||
rootPath = Utilities.PathCombine(rootFolder.FullName,Path.DirectorySeparatorChar.ToString());
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", Oqtane.Client";
|
||||
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name,Path.DirectorySeparatorChar.ToString());
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
|
||||
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.Oqtane";
|
||||
}
|
||||
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name,Path.DirectorySeparatorChar.ToString());
|
||||
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
|
||||
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.Oqtane";
|
||||
|
||||
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Definition Created {ModuleDefinition}", moduleDefinition);
|
||||
|
||||
if (moduleDefinition.Template == "internal")
|
||||
{
|
||||
// add embedded resources to project file
|
||||
List<string> resources = new List<string>();
|
||||
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + ".1.0.0.sql"));
|
||||
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + ".Uninstall.sql"));
|
||||
EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources);
|
||||
}
|
||||
}
|
||||
|
||||
return moduleDefinition;
|
||||
|
@ -264,19 +263,5 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmbedResourceFiles(string projectfile, List<string> resources)
|
||||
{
|
||||
XDocument project = XDocument.Load(projectfile);
|
||||
var itemGroup = project.Descendants("ItemGroup").Descendants("EmbeddedResource").FirstOrDefault().Parent;
|
||||
if (itemGroup != null)
|
||||
{
|
||||
foreach (var resource in resources)
|
||||
{
|
||||
itemGroup.Add(new XElement("EmbeddedResource", new XAttribute("Include", resource)));
|
||||
}
|
||||
}
|
||||
project.Save(projectfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Oqtane.Enums;
|
||||
|
@ -65,7 +65,7 @@ namespace Oqtane.Controllers
|
|||
if (IsAuthorized(notification.FromUserId))
|
||||
{
|
||||
notification = _notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {Notification}", notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ namespace Oqtane.Controllers
|
|||
if (IsAuthorized(notification.FromUserId))
|
||||
{
|
||||
notification = _notifications.UpdateNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {Folder}", notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
// use assets.json to clean up file resources
|
||||
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(Path.Combine(assetpath, "assets.json")));
|
||||
assets.Reverse();
|
||||
foreach (string asset in assets)
|
||||
{
|
||||
// legacy support for assets that were stored as absolute paths
|
||||
|
@ -70,6 +71,10 @@ namespace Oqtane.Controllers
|
|||
if (System.IO.File.Exists(filepath))
|
||||
{
|
||||
System.IO.File.Delete(filepath);
|
||||
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
|
||||
{
|
||||
Directory.Delete(Path.GetDirectoryName(filepath));
|
||||
}
|
||||
}
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assets Removed For {ThemeName}", theme.ThemeName);
|
||||
|
|
|
@ -111,7 +111,6 @@ namespace Oqtane.Controllers
|
|||
return null;
|
||||
}
|
||||
|
||||
//TODO shoud be moved to another layer
|
||||
private async Task<User> CreateUser(User user)
|
||||
{
|
||||
User newUser = null;
|
||||
|
@ -261,18 +260,50 @@ namespace Oqtane.Controllers
|
|||
// DELETE api/<controller>/5?siteid=x
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public async Task Delete(int id)
|
||||
public async Task Delete(int id, string siteid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(_users.GetUser(id).Username);
|
||||
|
||||
if (identityuser != null)
|
||||
User user = _users.GetUser(id);
|
||||
if (user != null)
|
||||
{
|
||||
var result = await _identityUserManager.DeleteAsync(identityuser);
|
||||
|
||||
if (result != null)
|
||||
// remove user roles for site
|
||||
foreach (UserRole userrole in _userRoles.GetUserRoles(user.UserId, Int32.Parse(siteid)).ToList())
|
||||
{
|
||||
_users.DeleteUser(id);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", id);
|
||||
_userRoles.DeleteUserRole(userrole.UserRoleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
|
||||
}
|
||||
|
||||
// remove user folder for site
|
||||
var folder = _folders.GetFolder(Int32.Parse(siteid), Utilities.PathCombine("Users", user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()));
|
||||
if (folder != null)
|
||||
{
|
||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||
{
|
||||
Directory.Delete(_folders.GetFolderPath(folder), true);
|
||||
}
|
||||
_folders.DeleteFolder(folder.FolderId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Folder Deleted {Folder}", folder);
|
||||
}
|
||||
|
||||
// delete user if they are not a member of any other sites
|
||||
if (!_userRoles.GetUserRoles(user.UserId, -1).Any())
|
||||
{
|
||||
// get identity user
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
// delete identity user
|
||||
var result = await _identityUserManager.DeleteAsync(identityuser);
|
||||
if (result != null)
|
||||
{
|
||||
// delete user
|
||||
_users.DeleteUser(user.UserId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", user.UserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", user.UserId, result.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Oqtane.Extensions
|
||||
{
|
||||
public static class DbContextOptionsBuilderExtensions
|
||||
{
|
||||
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, string connectionString)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(connectionString);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,27 +33,30 @@ namespace Oqtane.Infrastructure
|
|||
_cache = cache;
|
||||
}
|
||||
|
||||
public bool IsInstalled()
|
||||
public Installation IsInstalled()
|
||||
{
|
||||
var defaultConnectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
|
||||
var result = !string.IsNullOrEmpty(defaultConnectionString);
|
||||
if (result)
|
||||
var result = new Installation { Success = false, Message = string.Empty };
|
||||
if (!string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))
|
||||
{
|
||||
result.Success = true;
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
|
||||
result = db.Database.CanConnect();
|
||||
if (result)
|
||||
if (db.Database.CanConnect())
|
||||
{
|
||||
try
|
||||
{
|
||||
result = db.Tenant.Any();
|
||||
var provisioned = db.Tenant.Any();
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = false;
|
||||
result.Message = "Master Database Not Installed Correctly";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Message = "Cannot Connect To Master Database";
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -74,7 +77,8 @@ namespace Oqtane.Infrastructure
|
|||
// startup or silent installation
|
||||
install = new InstallConfig { ConnectionString = _config.GetConnectionString(SettingKeys.ConnectionStringKey), TenantName = TenantNames.Master, IsNewTenant = false };
|
||||
|
||||
if (!IsInstalled())
|
||||
var installation = IsInstalled();
|
||||
if (!installation.Success)
|
||||
{
|
||||
install.Aliases = GetInstallationConfig(SettingKeys.DefaultAliasKey, string.Empty);
|
||||
install.HostPassword = GetInstallationConfig(SettingKeys.HostPasswordKey, string.Empty);
|
||||
|
@ -97,6 +101,14 @@ namespace Oqtane.Infrastructure
|
|||
install.ConnectionString = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(installation.Message))
|
||||
{
|
||||
// problem with prior installation
|
||||
install.ConnectionString = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -168,9 +180,10 @@ namespace Oqtane.Infrastructure
|
|||
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
|
||||
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory);
|
||||
|
||||
using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(NormalizeConnectionString(install.ConnectionString)).Options))
|
||||
var connectionString = NormalizeConnectionString(install.ConnectionString);
|
||||
using (var dbc = new DbContext(new DbContextOptionsBuilder().UseOqtaneDatabase(connectionString).Options))
|
||||
{
|
||||
// create empty database if it does not exist
|
||||
// create empty database if it does not exist
|
||||
dbc.Database.EnsureCreated();
|
||||
result.Success = true;
|
||||
}
|
||||
|
@ -235,7 +248,7 @@ namespace Oqtane.Infrastructure
|
|||
|
||||
if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases))
|
||||
{
|
||||
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||
{
|
||||
Tenant tenant;
|
||||
if (install.IsNewTenant)
|
||||
|
@ -274,7 +287,7 @@ namespace Oqtane.Infrastructure
|
|||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
var upgrades = scope.ServiceProvider.GetRequiredService<IUpgradeManager>();
|
||||
|
||||
|
||||
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
|
||||
{
|
||||
foreach (var tenant in db.Tenant.ToList())
|
||||
|
|
|
@ -126,6 +126,10 @@ namespace Oqtane.Infrastructure
|
|||
{
|
||||
File.Delete(manifestpath);
|
||||
}
|
||||
if (!Directory.Exists(Path.GetDirectoryName(manifestpath)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(manifestpath));
|
||||
}
|
||||
File.WriteAllText(manifestpath, JsonSerializer.Serialize(assets));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
using Oqtane.Models;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public interface IDatabaseManager
|
||||
{
|
||||
bool IsInstalled();
|
||||
Installation IsInstalled();
|
||||
Installation Install();
|
||||
Installation Install(InstallConfig install);
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace Oqtane.SiteTemplates
|
|||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
}.EncodePermissions(),
|
||||
Content = "<p>Copyright (c) 2019-2020 .NET Foundation</p>" +
|
||||
Content = "<p>Copyright (c) 2019-2021 .NET Foundation</p>" +
|
||||
"<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>" +
|
||||
"<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>" +
|
||||
"<p>THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Oqtane.Models;
|
||||
using Oqtane.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -17,7 +17,7 @@ namespace Oqtane.Infrastructure
|
|||
|
||||
public List<SyncEvent> GetSyncEvents(int tenantId, DateTime lastSyncDate)
|
||||
{
|
||||
return SyncEvents.Where(item => item.TenantId == tenantId && item.ModifiedOn >= lastSyncDate).ToList();
|
||||
return SyncEvents.Where(item => (item.TenantId == tenantId || item.TenantId == -1) && item.ModifiedOn >= lastSyncDate).ToList();
|
||||
}
|
||||
|
||||
public void AddSyncEvent(int tenantId, string entityName, int entityId)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
|
@ -12,23 +14,23 @@ namespace Oqtane.Infrastructure
|
|||
{
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public UpgradeManager(IAliasRepository aliases, IServiceScopeFactory serviceScopeFactory)
|
||||
public UpgradeManager(IAliasRepository aliases, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment)
|
||||
{
|
||||
_aliases = aliases;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
public void Upgrade(Tenant tenant, string version)
|
||||
{
|
||||
// core framework upgrade logic - note that you can check if current tenant is Master if you only want to execute logic once
|
||||
var pageTemplates = new List<PageTemplate>();
|
||||
|
||||
// core framework upgrade logic - note that you can check if current tenant is Master if you only want to execute the logic once
|
||||
switch (version)
|
||||
{
|
||||
case "0.9.0":
|
||||
// add a page to all existing sites on upgrade
|
||||
|
||||
// this code is commented out on purpose - it provides an example of how to programmatically add a page to all existing sites on upgrade
|
||||
var pageTemplates = new List<PageTemplate>();
|
||||
//pageTemplates.Add(new PageTemplate
|
||||
//{
|
||||
// Name = "Test",
|
||||
|
@ -61,6 +63,17 @@ namespace Oqtane.Infrastructure
|
|||
//});
|
||||
CreateSitePages(tenant, pageTemplates);
|
||||
break;
|
||||
case "2.0.2":
|
||||
if (tenant.Name == TenantNames.Master)
|
||||
{
|
||||
// remove Internal module template files as they are no longer supported
|
||||
var internalTemplatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", "Internal", Path.DirectorySeparatorChar.ToString());
|
||||
if (Directory.Exists(internalTemplatePath))
|
||||
{
|
||||
Directory.Delete(internalTemplatePath, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@page "/"
|
||||
@page "/"
|
||||
@namespace Oqtane.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using Microsoft.Extensions.Configuration
|
||||
|
@ -35,6 +35,13 @@
|
|||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
@if (Model.Message != "")
|
||||
{
|
||||
<div class="app-alert">
|
||||
@Model.Message
|
||||
</div>
|
||||
}
|
||||
|
||||
<script src="js/interop.js"></script>
|
||||
|
||||
@if (Configuration.GetSection("Runtime").Value == "WebAssembly")
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace Oqtane.Pages
|
|||
|
||||
public string HeadResources = "";
|
||||
public string BodyResources = "";
|
||||
public string Message = "";
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
@ -54,20 +55,28 @@ namespace Oqtane.Pages
|
|||
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 hostname = uri.Authority + "/" + uri.LocalPath.Substring(1);
|
||||
var alias = _aliases.GetAlias(hostname);
|
||||
if (alias != null)
|
||||
{
|
||||
var defaultLanguage = languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First();
|
||||
_state.Alias = alias;
|
||||
|
||||
SetLocalizationCookie(defaultLanguage.Code);
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLocalizationCookie(_localizationManager.GetDefaultCulture());
|
||||
Message = $"No Matching Alias For Host Name {hostname}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class DBContextBase : IdentityUserContext<IdentityUser>
|
||||
public class DBContextBase : IdentityUserContext<IdentityUser>
|
||||
{
|
||||
private ITenantResolver _tenantResolver;
|
||||
private IHttpContextAccessor _accessor;
|
||||
|
@ -24,63 +25,16 @@ namespace Oqtane.Repository
|
|||
var tenant = _tenantResolver.GetTenant();
|
||||
if (tenant != null)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(tenant.DBConnectionString
|
||||
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString())
|
||||
);
|
||||
var connectionString = tenant.DBConnectionString
|
||||
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString());
|
||||
optionsBuilder.UseOqtaneDatabase(connectionString);
|
||||
}
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
ChangeTracker.DetectChanges();
|
||||
|
||||
string username = "";
|
||||
if (_accessor.HttpContext != null && _accessor.HttpContext.User.Identity.Name != null)
|
||||
{
|
||||
username = _accessor.HttpContext.User.Identity.Name;
|
||||
}
|
||||
DateTime date = DateTime.UtcNow;
|
||||
|
||||
var created = ChangeTracker.Entries()
|
||||
.Where(x => x.State == EntityState.Added);
|
||||
|
||||
foreach(var item in created)
|
||||
{
|
||||
if (item.Entity is IAuditable)
|
||||
{
|
||||
item.CurrentValues[nameof(IAuditable.CreatedBy)] = username;
|
||||
item.CurrentValues[nameof(IAuditable.CreatedOn)] = date;
|
||||
}
|
||||
}
|
||||
|
||||
var modified = ChangeTracker.Entries()
|
||||
.Where(x => x.State == EntityState.Modified || x.State == EntityState.Added);
|
||||
|
||||
foreach (var item in modified)
|
||||
{
|
||||
if (item.Entity is IAuditable)
|
||||
{
|
||||
item.CurrentValues[nameof(IAuditable.ModifiedBy)] = username;
|
||||
item.CurrentValues[nameof(IAuditable.ModifiedOn)] = date;
|
||||
}
|
||||
|
||||
if (item.Entity is IDeletable && item.State != EntityState.Added)
|
||||
{
|
||||
if ((bool)item.CurrentValues[nameof(IDeletable.IsDeleted)]
|
||||
&& !item.GetDatabaseValues().GetValue<bool>(nameof(IDeletable.IsDeleted)))
|
||||
{
|
||||
item.CurrentValues[nameof(IDeletable.DeletedBy)] = username;
|
||||
item.CurrentValues[nameof(IDeletable.DeletedOn)] = date;
|
||||
}
|
||||
else if (!(bool)item.CurrentValues[nameof(IDeletable.IsDeleted)]
|
||||
&& item.GetDatabaseValues().GetValue<bool>(nameof(IDeletable.IsDeleted)))
|
||||
{
|
||||
item.CurrentValues[nameof(IDeletable.DeletedBy)] = null;
|
||||
item.CurrentValues[nameof(IDeletable.DeletedOn)] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
DbContextUtils.SaveChanges(this, _accessor);
|
||||
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
|
65
Oqtane.Server/Repository/Context/DbContextUtils.cs
Normal file
65
Oqtane.Server/Repository/Context/DbContextUtils.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class DbContextUtils
|
||||
{
|
||||
public static void SaveChanges(DbContext context, IHttpContextAccessor accessor)
|
||||
{
|
||||
var changeTracker = context.ChangeTracker;
|
||||
|
||||
changeTracker.DetectChanges();
|
||||
|
||||
string username = "";
|
||||
if (accessor.HttpContext != null && accessor.HttpContext.User.Identity.Name != null)
|
||||
{
|
||||
username = accessor.HttpContext.User.Identity.Name;
|
||||
}
|
||||
DateTime date = DateTime.UtcNow;
|
||||
|
||||
var created = changeTracker.Entries()
|
||||
.Where(x => x.State == EntityState.Added);
|
||||
|
||||
foreach(var item in created)
|
||||
{
|
||||
if (item.Entity is IAuditable)
|
||||
{
|
||||
item.CurrentValues[nameof(IAuditable.CreatedBy)] = username;
|
||||
item.CurrentValues[nameof(IAuditable.CreatedOn)] = date;
|
||||
}
|
||||
}
|
||||
|
||||
var modified = changeTracker.Entries()
|
||||
.Where(x => x.State == EntityState.Modified || x.State == EntityState.Added);
|
||||
|
||||
foreach (var item in modified)
|
||||
{
|
||||
if (item.Entity is IAuditable)
|
||||
{
|
||||
item.CurrentValues[nameof(IAuditable.ModifiedBy)] = username;
|
||||
item.CurrentValues[nameof(IAuditable.ModifiedOn)] = date;
|
||||
}
|
||||
|
||||
if (item.Entity is IDeletable && item.State != EntityState.Added)
|
||||
{
|
||||
if ((bool)item.CurrentValues[nameof(IDeletable.IsDeleted)]
|
||||
&& !item.GetDatabaseValues().GetValue<bool>(nameof(IDeletable.IsDeleted)))
|
||||
{
|
||||
item.CurrentValues[nameof(IDeletable.DeletedBy)] = username;
|
||||
item.CurrentValues[nameof(IDeletable.DeletedOn)] = date;
|
||||
}
|
||||
else if (!(bool)item.CurrentValues[nameof(IDeletable.IsDeleted)]
|
||||
&& item.GetDatabaseValues().GetValue<bool>(nameof(IDeletable.IsDeleted)))
|
||||
{
|
||||
item.CurrentValues[nameof(IDeletable.DeletedBy)] = null;
|
||||
item.CurrentValues[nameof(IDeletable.DeletedOn)] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
|
||||
|
||||
public class InstallationContext : DbContext
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
|
@ -15,7 +16,7 @@ namespace Oqtane.Repository
|
|||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseSqlServer(_connectionString);
|
||||
=> optionsBuilder.UseOqtaneDatabase(_connectionString);
|
||||
|
||||
public virtual DbSet<Alias> Alias { get; set; }
|
||||
public virtual DbSet<Tenant> Tenant { get; set; }
|
||||
|
|
|
@ -4,13 +4,14 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Oqtane.Extensions;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class MasterDBContext : DbContext
|
||||
{
|
||||
private IHttpContextAccessor _accessor;
|
||||
private IConfiguration _configuration;
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public MasterDBContext(DbContextOptions<MasterDBContext> options, IHttpContextAccessor accessor, IConfiguration configuration) : base(options)
|
||||
{
|
||||
|
@ -20,11 +21,12 @@ namespace Oqtane.Repository
|
|||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
|
||||
if (!String.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
|
||||
{
|
||||
optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection")
|
||||
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString())
|
||||
);
|
||||
var connectionString = _configuration.GetConnectionString("DefaultConnection")
|
||||
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString());
|
||||
|
||||
optionsBuilder.UseOqtaneDatabase(connectionString);
|
||||
}
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
|
@ -37,38 +39,7 @@ namespace Oqtane.Repository
|
|||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
ChangeTracker.DetectChanges();
|
||||
|
||||
string username = "";
|
||||
if (_accessor.HttpContext != null && _accessor.HttpContext.User.Identity.Name != null)
|
||||
{
|
||||
username = _accessor.HttpContext.User.Identity.Name;
|
||||
}
|
||||
DateTime date = DateTime.UtcNow;
|
||||
|
||||
var created = ChangeTracker.Entries()
|
||||
.Where(x => x.State == EntityState.Added);
|
||||
|
||||
foreach (var item in created)
|
||||
{
|
||||
if (item.Entity is IAuditable)
|
||||
{
|
||||
item.CurrentValues[nameof(IAuditable.CreatedBy)] = username;
|
||||
item.CurrentValues[nameof(IAuditable.CreatedOn)] = date;
|
||||
}
|
||||
}
|
||||
|
||||
var modified = ChangeTracker.Entries()
|
||||
.Where(x => x.State == EntityState.Modified || x.State == EntityState.Added);
|
||||
|
||||
foreach (var item in modified)
|
||||
{
|
||||
if (item.Entity is IAuditable)
|
||||
{
|
||||
item.CurrentValues[nameof(IAuditable.ModifiedBy)] = username;
|
||||
item.CurrentValues[nameof(IAuditable.ModifiedOn)] = date;
|
||||
}
|
||||
}
|
||||
DbContextUtils.SaveChanges(this, _accessor);
|
||||
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
|
@ -27,7 +27,7 @@ namespace Oqtane.Repository
|
|||
return _db.UserRole.Where(item => item.UserId == userId)
|
||||
.Include(item => item.Role) // eager load roles
|
||||
.Include(item => item.User) // eager load users
|
||||
.Where(item => item.Role.SiteId == siteId || item.Role.SiteId == null);
|
||||
.Where(item => item.Role.SiteId == siteId || item.Role.SiteId == null || siteId == -1);
|
||||
}
|
||||
|
||||
public UserRole AddUserRole(UserRole userRole)
|
||||
|
|
|
@ -226,7 +226,7 @@ namespace Oqtane
|
|||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISyncManager sync)
|
||||
{
|
||||
ServiceActivator.Configure(app.ApplicationServices);
|
||||
|
||||
|
@ -264,6 +264,9 @@ namespace Oqtane
|
|||
endpoints.MapControllers();
|
||||
endpoints.MapFallbackToPage("/_Host");
|
||||
});
|
||||
|
||||
// create a sync event to identify server application startup
|
||||
sync.AddSyncEvent(-1, "Application", -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<Version>1.0.0</Version>
|
||||
<Product>[Owner].[Module]</Product>
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
@using Oqtane.Modules.Controls
|
||||
@using [Owner].[Module].Services
|
||||
@using [Owner].[Module].Models
|
||||
|
||||
@namespace [Owner].[Module]
|
||||
@inherits ModuleBase
|
||||
@inject I[Module]Service [Module]Service
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="Enter a name">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="name" class="form-control" @bind="@_name" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="button" class="btn btn-success" @onclick="Save">Save</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
@if (PageState.Action == "Edit")
|
||||
{
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||
}
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
public override string Actions => "Add,Edit";
|
||||
|
||||
public override string Title => "Manage [Module]";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
int _id;
|
||||
string _name;
|
||||
string _createdby;
|
||||
DateTime _createdon;
|
||||
string _modifiedby;
|
||||
DateTime _modifiedon;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.Action == "Edit")
|
||||
{
|
||||
_id = Int32.Parse(PageState.QueryString["id"]);
|
||||
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
|
||||
if ([Module] != null)
|
||||
{
|
||||
_name = [Module].Name;
|
||||
_createdby = [Module].CreatedBy;
|
||||
_createdon = [Module].CreatedOn;
|
||||
_modifiedby = [Module].ModifiedBy;
|
||||
_modifiedon = [Module].ModifiedOn;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message);
|
||||
AddModuleMessage("Error Loading [Module]", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.Action == "Add")
|
||||
{
|
||||
[Module] [Module] = new [Module]();
|
||||
[Module].ModuleId = ModuleState.ModuleId;
|
||||
[Module].Name = _name;
|
||||
[Module] = await [Module]Service.Add[Module]Async([Module]);
|
||||
await logger.LogInformation("[Module] Added {[Module]}", [Module]);
|
||||
}
|
||||
else
|
||||
{
|
||||
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
|
||||
[Module].Name = _name;
|
||||
await [Module]Service.Update[Module]Async([Module]);
|
||||
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);
|
||||
}
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message);
|
||||
AddModuleMessage("Error Saving [Module]", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
@using [Owner].[Module].Services
|
||||
@using [Owner].[Module].Models
|
||||
|
||||
@namespace [Owner].[Module]
|
||||
@inherits ModuleBase
|
||||
@inject I[Module]Service [Module]Service
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@if (_[Module]s == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add [Module]" />
|
||||
<br />
|
||||
<br />
|
||||
@if (@_[Module]s.Count != 0)
|
||||
{
|
||||
<Pager Items="@_[Module]s">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>Name</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.[Module]Id.ToString())" /></td>
|
||||
<td><ActionDialog Header="Delete [Module]" Message="@("Are You Sure You Wish To Delete The " + context.Name + " [Module]?")" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" /></td>
|
||||
<td>@context.Name</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>No [Module]s To Display</p>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- The content below is for informational purposes only and can be safely removed -->
|
||||
|
||||
<hr />
|
||||
[Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:<br /><br />
|
||||
[RootPath]Oqtane.Client\Modules\[Module]\<br />
|
||||
- Edit.razor - component for adding or editing content<br />
|
||||
- Index.razor - main component for your module **the content you are reading is in this file**<br />
|
||||
- ModuleInfo.cs - implements IModule interface to provide configuration settings for your module<br />
|
||||
- Settings.razor - component for managing module settings<br />
|
||||
- Services\I[Module]Service.cs - interface for defining service API methods<br />
|
||||
- Services\[Module]Service.cs - implements service API interface methods<br /><br />
|
||||
[RootPath]Oqtane.Server\Modules\[Module]\<br />
|
||||
- Controllers\[Module]Controller.cs - API methods implemented using a REST pattern<br />
|
||||
- Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content<br />
|
||||
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
|
||||
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
|
||||
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
|
||||
- Scripts\[Owner].[Module]s.1.0.0.sql - database schema definition script<br />
|
||||
- Scripts\[Owner].[Module]s.Uninstall.sql - database uninstall script<br /><br />
|
||||
[RootPath]Oqtane.Shared\Modules\[Module]\<br />
|
||||
- Models\[Module].cs - model definition<br /><br />
|
||||
|
||||
<!-- The content above is for informational purposes only and can be safely removed -->
|
||||
|
||||
@code {
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = ModulePath() + "Module.js" }
|
||||
};
|
||||
|
||||
List<[Module]> _[Module]s;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message);
|
||||
AddModuleMessage("Error Loading [Module]", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Delete([Module] [Module])
|
||||
{
|
||||
try
|
||||
{
|
||||
await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
|
||||
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
|
||||
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message);
|
||||
AddModuleMessage("Error Deleting [Module]", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace [Owner].[Module]
|
||||
{
|
||||
public class Interop
|
||||
{
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
|
||||
public Interop(IJSRuntime jsRuntime)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
|
||||
namespace [Owner].[Module]
|
||||
{
|
||||
public class ModuleInfo : IModule
|
||||
{
|
||||
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||
{
|
||||
Name = "[Module]",
|
||||
Description = "[Module]",
|
||||
Version = "1.0.0",
|
||||
ServerManagerType = "[ServerManagerType]",
|
||||
ReleaseVersions = "1.0.0"
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using [Owner].[Module].Models;
|
||||
|
||||
namespace [Owner].[Module].Services
|
||||
{
|
||||
public interface I[Module]Service
|
||||
{
|
||||
Task<List<Models.[Module]>> Get[Module]sAsync(int ModuleId);
|
||||
|
||||
Task<Models.[Module]> Get[Module]Async(int [Module]Id, int ModuleId);
|
||||
|
||||
Task<Models.[Module]> Add[Module]Async(Models.[Module] [Module]);
|
||||
|
||||
Task<Models.[Module]> Update[Module]Async(Models.[Module] [Module]);
|
||||
|
||||
Task Delete[Module]Async(int [Module]Id, int ModuleId);
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using [Owner].[Module].Models;
|
||||
|
||||
namespace [Owner].[Module].Services
|
||||
{
|
||||
public class [Module]Service : ServiceBase, I[Module]Service, IService
|
||||
{
|
||||
private readonly SiteState _siteState;
|
||||
|
||||
public [Module]Service(HttpClient http, SiteState siteState) : base(http)
|
||||
{
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private string Apiurl => CreateApiUrl(_siteState.Alias, "[Module]");
|
||||
|
||||
public async Task<List<Models.[Module]>> Get[Module]sAsync(int ModuleId)
|
||||
{
|
||||
List<Models.[Module]> [Module]s = await GetJsonAsync<List<Models.[Module]>>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", ModuleId));
|
||||
return [Module]s.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
public async Task<Models.[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
|
||||
{
|
||||
return await GetJsonAsync<Models.[Module]>(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
|
||||
}
|
||||
|
||||
public async Task<Models.[Module]> Add[Module]Async(Models.[Module] [Module])
|
||||
{
|
||||
return await PostJsonAsync<Models.[Module]>(CreateAuthorizationPolicyUrl($"{Apiurl}", [Module].ModuleId), [Module]);
|
||||
}
|
||||
|
||||
public async Task<Models.[Module]> Update[Module]Async(Models.[Module] [Module])
|
||||
{
|
||||
return await PutJsonAsync<Models.[Module]>(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module].[Module]Id}", [Module].ModuleId), [Module]);
|
||||
}
|
||||
|
||||
public async Task Delete[Module]Async(int [Module]Id, int ModuleId)
|
||||
{
|
||||
await DeleteAsync(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
@namespace [Owner].[Module]
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="value" HelpText="Enter a value">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="value" type="text" class="form-control" @bind="@_value" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@code {
|
||||
public override string Title => "[Module] Settings";
|
||||
|
||||
string _value;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
_value = SettingService.GetSetting(settings, "SettingName", "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
SettingService.SetSetting(settings, "SettingName", _value);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using [Owner].[Module].Models;
|
||||
using [Owner].[Module].Repository;
|
||||
|
||||
namespace [Owner].[Module].Controllers
|
||||
{
|
||||
[Route(ControllerRoutes.Default)]
|
||||
public class [Module]Controller : Controller
|
||||
{
|
||||
private readonly I[Module]Repository _[Module]Repository;
|
||||
private readonly ILogManager _logger;
|
||||
protected int _entityId = -1;
|
||||
|
||||
public [Module]Controller(I[Module]Repository [Module]Repository, ILogManager logger, IHttpContextAccessor accessor)
|
||||
{
|
||||
_[Module]Repository = [Module]Repository;
|
||||
_logger = logger;
|
||||
|
||||
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
|
||||
{
|
||||
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/<controller>?moduleid=x
|
||||
[HttpGet]
|
||||
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||
public IEnumerable<Models.[Module]> Get(string moduleid)
|
||||
{
|
||||
return _[Module]Repository.Get[Module]s(int.Parse(moduleid));
|
||||
}
|
||||
|
||||
// GET api/<controller>/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||
public Models.[Module] Get(int id)
|
||||
{
|
||||
Models.[Module] [Module] = _[Module]Repository.Get[Module](id);
|
||||
if ([Module] != null && [Module].ModuleId != _entityId)
|
||||
{
|
||||
[Module] = null;
|
||||
}
|
||||
return [Module];
|
||||
}
|
||||
|
||||
// POST api/<controller>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public Models.[Module] Post([FromBody] Models.[Module] [Module])
|
||||
{
|
||||
if (ModelState.IsValid && [Module].ModuleId == _entityId)
|
||||
{
|
||||
[Module] = _[Module]Repository.Add[Module]([Module]);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
|
||||
}
|
||||
return [Module];
|
||||
}
|
||||
|
||||
// PUT api/<controller>/5
|
||||
[HttpPut("{id}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public Models.[Module] Put(int id, [FromBody] Models.[Module] [Module])
|
||||
{
|
||||
if (ModelState.IsValid && [Module].ModuleId == _entityId)
|
||||
{
|
||||
[Module] = _[Module]Repository.Update[Module]([Module]);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
|
||||
}
|
||||
return [Module];
|
||||
}
|
||||
|
||||
// DELETE api/<controller>/5
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public void Delete(int id)
|
||||
{
|
||||
Models.[Module] [Module] = _[Module]Repository.Get[Module](id);
|
||||
if ([Module] != null && [Module].ModuleId == _entityId)
|
||||
{
|
||||
_[Module]Repository.Delete[Module](id);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using [Owner].[Module].Models;
|
||||
using [Owner].[Module].Repository;
|
||||
|
||||
namespace [Owner].[Module].Manager
|
||||
{
|
||||
public class [Module]Manager : IInstallable, IPortable
|
||||
{
|
||||
private I[Module]Repository _[Module]Repository;
|
||||
private ISqlRepository _sql;
|
||||
|
||||
public [Module]Manager(I[Module]Repository [Module]Repository, ISqlRepository sql)
|
||||
{
|
||||
_[Module]Repository = [Module]Repository;
|
||||
_sql = sql;
|
||||
}
|
||||
|
||||
public bool Install(Tenant tenant, string version)
|
||||
{
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module]." + version + ".sql");
|
||||
}
|
||||
|
||||
public bool Uninstall(Tenant tenant)
|
||||
{
|
||||
return _sql.ExecuteScript(tenant, GetType().Assembly, "[Owner].[Module].Uninstall.sql");
|
||||
}
|
||||
|
||||
public string ExportModule(Module module)
|
||||
{
|
||||
string content = "";
|
||||
List<Models.[Module]> [Module]s = _[Module]Repository.Get[Module]s(module.ModuleId).ToList();
|
||||
if ([Module]s != null)
|
||||
{
|
||||
content = JsonSerializer.Serialize([Module]s);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
public void ImportModule(Module module, string content, string version)
|
||||
{
|
||||
List<Models.[Module]> [Module]s = null;
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
[Module]s = JsonSerializer.Deserialize<List<Models.[Module]>>(content);
|
||||
}
|
||||
if ([Module]s != null)
|
||||
{
|
||||
foreach(var [Module] in [Module]s)
|
||||
{
|
||||
_[Module]Repository.Add[Module](new Models.[Module] { ModuleId = module.ModuleId, Name = [Module].Name });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using [Owner].[Module].Models;
|
||||
|
||||
namespace [Owner].[Module].Repository
|
||||
{
|
||||
public interface I[Module]Repository
|
||||
{
|
||||
IEnumerable<Models.[Module]> Get[Module]s(int ModuleId);
|
||||
Models.[Module] Get[Module](int [Module]Id);
|
||||
Models.[Module] Add[Module](Models.[Module] [Module]);
|
||||
Models.[Module] Update[Module](Models.[Module] [Module]);
|
||||
void Delete[Module](int [Module]Id);
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Repository;
|
||||
using [Owner].[Module].Models;
|
||||
|
||||
namespace [Owner].[Module].Repository
|
||||
{
|
||||
public class [Module]Context : DBContextBase, IService
|
||||
{
|
||||
public virtual DbSet<Models.[Module]> [Module] { get; set; }
|
||||
|
||||
public [Module]Context(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor)
|
||||
{
|
||||
// ContextBase handles multi-tenant database connections
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Modules;
|
||||
using [Owner].[Module].Models;
|
||||
|
||||
namespace [Owner].[Module].Repository
|
||||
{
|
||||
public class [Module]Repository : I[Module]Repository, IService
|
||||
{
|
||||
private readonly [Module]Context _db;
|
||||
|
||||
public [Module]Repository([Module]Context context)
|
||||
{
|
||||
_db = context;
|
||||
}
|
||||
|
||||
public IEnumerable<Models.[Module]> Get[Module]s(int ModuleId)
|
||||
{
|
||||
return _db.[Module].Where(item => item.ModuleId == ModuleId);
|
||||
}
|
||||
|
||||
public Models.[Module] Get[Module](int [Module]Id)
|
||||
{
|
||||
return _db.[Module].Find([Module]Id);
|
||||
}
|
||||
|
||||
public Models.[Module] Add[Module](Models.[Module] [Module])
|
||||
{
|
||||
_db.[Module].Add([Module]);
|
||||
_db.SaveChanges();
|
||||
return [Module];
|
||||
}
|
||||
|
||||
public Models.[Module] Update[Module](Models.[Module] [Module])
|
||||
{
|
||||
_db.Entry([Module]).State = EntityState.Modified;
|
||||
_db.SaveChanges();
|
||||
return [Module];
|
||||
}
|
||||
|
||||
public void Delete[Module](int [Module]Id)
|
||||
{
|
||||
Models.[Module] [Module] = _db.[Module].Find([Module]Id);
|
||||
_db.[Module].Remove([Module]);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
Create [Owner][Module] table
|
||||
*/
|
||||
|
||||
CREATE TABLE [dbo].[[Owner][Module]](
|
||||
[[Module]Id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[ModuleId] [int] NOT NULL,
|
||||
[Name] [nvarchar](256) NOT NULL,
|
||||
[CreatedBy] [nvarchar](256) NOT NULL,
|
||||
[CreatedOn] [datetime] NOT NULL,
|
||||
[ModifiedBy] [nvarchar](256) NOT NULL,
|
||||
[ModifiedOn] [datetime] NOT NULL,
|
||||
CONSTRAINT [PK_[Owner][Module]] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[[Module]Id] ASC
|
||||
)
|
||||
)
|
||||
GO
|
||||
|
||||
/*
|
||||
Create foreign key relationships
|
||||
*/
|
||||
ALTER TABLE [dbo].[[Owner][Module]] WITH CHECK ADD CONSTRAINT [FK_[Owner][Module]_Module] FOREIGN KEY([ModuleId])
|
||||
REFERENCES [dbo].Module ([ModuleId])
|
||||
ON DELETE CASCADE
|
||||
GO
|
|
@ -1,6 +0,0 @@
|
|||
/*
|
||||
Remove [Owner][Module] table
|
||||
*/
|
||||
|
||||
DROP TABLE [dbo].[[Owner][Module]]
|
||||
GO
|
|
@ -1 +0,0 @@
|
|||
/* Module Custom Styles */
|
|
@ -1,5 +0,0 @@
|
|||
/* Module Script */
|
||||
var [Owner] = [Owner] || {};
|
||||
|
||||
[Owner].[Module] = {
|
||||
};
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace [Owner].[Module].Models
|
||||
{
|
||||
[Table("[Owner][Module]")]
|
||||
public class [Module] : IAuditable
|
||||
{
|
||||
public int [Module]Id { get; set; }
|
||||
public int ModuleId { get; set; }
|
||||
public string Name { 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 @@
|
|||
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
|
||||
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
|
||||
|
||||
html, body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
|
@ -125,6 +125,13 @@ app {
|
|||
vertical-align: inherit;
|
||||
}
|
||||
|
||||
.app-alert {
|
||||
padding: 20px;
|
||||
background-color: #f44336; /* red */
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* Tooltips */
|
||||
.app-tooltip {
|
||||
cursor: help;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -5,8 +5,8 @@ namespace Oqtane.Shared {
|
|||
|
||||
public class Constants {
|
||||
public const string PackageId = "Oqtane.Framework";
|
||||
public const string Version = "2.0.0";
|
||||
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0";
|
||||
public const string Version = "2.0.2";
|
||||
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2";
|
||||
|
||||
public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client";
|
||||
public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client";
|
||||
|
@ -55,8 +55,8 @@ namespace Oqtane.Shared {
|
|||
[Obsolete(RoleObsoleteMessage)]
|
||||
public const string RegisteredRole = RoleNames.Registered;
|
||||
|
||||
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,svg,ico";
|
||||
public const string UploadableFiles = "jpg,jpeg,jpe,gif,bmp,png,svg,ico,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg,csv";
|
||||
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,ico";
|
||||
public const string UploadableFiles = ImageFiles + ",mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg,csv";
|
||||
public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$";
|
||||
|
||||
public static readonly char[] InvalidFileNameChars =
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -16,6 +16,8 @@ EndProject
|
|||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77EECA8C-B58E-469E-B8C5-D543AFC9A654}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
|
|
|
@ -50,9 +50,11 @@ There is a separate [Documentation repository](https://github.com/oqtane/oqtane.
|
|||
This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist.
|
||||
|
||||
V.2.1.0 ( Q1 2021 )
|
||||
- [ ] Cross Platform Database Support ( ie. SQLite ) - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964)
|
||||
- [ ] EF Core Migrations for Database Installation/Upgrade - see [#964](https://github.com/oqtane/oqtane.framework/discussions/964)
|
||||
|
||||
V.2.0.1 ( Feb 27, 2021 )
|
||||
- [x] Complete Static Localization of Admin UI
|
||||
- [ ] Cross Platform Database Support ( ie. SQLite )
|
||||
- [ ] EF Core Migrations for Database Installation/Upgrade
|
||||
|
||||
V.2.0.0 ( released in conjuntion with .NET 5 on Nov 11, 2020 )
|
||||
- [x] Migration to .NET 5
|
||||
|
|
Loading…
Reference in New Issue
Block a user