diff --git a/.gitignore b/.gitignore index d47b8dcf..98408e86 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ msbuild.binlog *.binlog *.nupkg +*.idea + Oqtane.Server/appsettings.json Oqtane.Server/Data/*.mdf Oqtane.Server/Data/*.ldf diff --git a/Oqtane.Client/App.razor b/Oqtane.Client/App.razor index 3d13adff..ca4b25bd 100644 --- a/Oqtane.Client/App.razor +++ b/Oqtane.Client/App.razor @@ -1,31 +1,39 @@ -@inject IInstallationService InstallationService +@inject IInstallationService InstallationService @if (_initialized) { - @if (!_installed) + @if (!_installation.Success) { } else { - - - - - + @if (string.IsNullOrEmpty(_installation.Message)) + { + + + + + + } + else + { +
+ @_installation.Message +
+ } } } @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; } diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 892f0559..b2ac3b64 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -36,7 +36,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Files/Index.razor b/Oqtane.Client/Modules/Admin/Files/Index.razor index 26cd56f9..58990804 100644 --- a/Oqtane.Client/Modules/Admin/Files/Index.razor +++ b/Oqtane.Client/Modules/Admin/Files/Index.razor @@ -42,7 +42,7 @@ @context.Name @context.ModifiedOn @context.Extension.ToUpper() @Localizer["File"] - @(context.Size / 1000) KB + @string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB @if (_files.Count == 0) diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor index 3ace61fd..cbd355db 100644 --- a/Oqtane.Client/Modules/Admin/Logs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor @@ -12,8 +12,8 @@ else + + + + } + + + + + + + + +
- - @@ -24,8 +24,8 @@ else - - @@ -36,8 +36,8 @@ else - - diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor index 66794ad5..6baa0376 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Index.razor @@ -9,7 +9,7 @@ @using System.Text.RegularExpressions @using System.IO; -@if (string.IsNullOrEmpty(_moduledefinitionname)) +@if (string.IsNullOrEmpty(_moduledefinitionname) && _systeminfo != null && _templates != null) { @@ -38,13 +38,15 @@ @@ -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 _systeminfo; + private List _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 Restart 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 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(); } } diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor index 538bf700..3ebfed0b 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor @@ -26,7 +26,7 @@ else diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index 171ab885..b0234619 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -101,18 +101,11 @@ diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 5efdd992..e1229e43 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -23,18 +23,11 @@ @@ -112,18 +105,11 @@ diff --git a/Oqtane.Client/Modules/Admin/Pages/Index.razor b/Oqtane.Client/Modules/Admin/Pages/Index.razor index 4ff925fc..fef67427 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Index.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Index.razor @@ -16,7 +16,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Profiles/Index.razor b/Oqtane.Client/Modules/Admin/Profiles/Index.razor index 0bdf8554..4705b290 100644 --- a/Oqtane.Client/Modules/Admin/Profiles/Index.razor +++ b/Oqtane.Client/Modules/Admin/Profiles/Index.razor @@ -19,7 +19,7 @@ else - + @@ -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); + } } diff --git a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor index 2d81476a..47e2a47a 100644 --- a/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor +++ b/Oqtane.Client/Modules/Admin/RecycleBin/Index.razor @@ -31,6 +31,12 @@ + @if (_pages.Any()) + { +
+ +
+ } } @@ -59,6 +65,13 @@ + @if (_modules.Any()) + { +
+ +
+ } + } @@ -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); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index acd0105b..4fcb5dd3 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -7,146 +7,143 @@ @inject IThemeService ThemeService @inject ISettingService SettingService @inject IStringLocalizer Localizer +@inject INotificationService NotificationService @if (_initialized) { -
- +
@if (context.AssemblyName != "Oqtane.Client") { - + } @context.Name - @foreach (var theme in _themes) { - if (theme.TypeName == _themetype) - { - - } - else - { - - } + } - @foreach (Page page in _pageList) { - if (page.PageId.ToString() == _parentid) - { - - } - else - { - - } + } - + @foreach (var theme in _themes) { - if (theme.TypeName == _themetype) - { - - } - else - { - - } + } @(new string('-', context.Level * 2))@(context.Name) @context.Name @context.DeletedOn @context.DeletedOn
- - - - - - - - - - - - - - - - - - - - - - - - - @if (_layouts.Count > 0) - { +
- - - -
- - - -
- - - -
- - - -
- - - -
- - - -
+ + + + + + + + + + + + + + + + + +
- + - +
+ + + +
+ + + +
+ + + +
+ + + +
+
+ + + + + + + + + + + + - } - - - - - - - - - - - - - - - - -
+ + + +
+ + + +
+ + +
- - - -
- - - -
- - - -
- - - -
- + @if (_layouts.Count > 0) + { +
+ + + +
+ + + +
+ + + +
+
@@ -201,6 +198,8 @@
- @Localizer["Please Note That SMTP Requires The Notification Job To Be Enabled In the Scheduled Jobs"] + @Localizer["Please Note That SMTP Requires The Notification Job To Be Enabled In Scheduled Jobs"]
+ +

@@ -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); + } + + } } diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor index b1afc85c..6d28aee6 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor @@ -31,18 +31,11 @@ diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor index 3a571cd3..9d028e1d 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor @@ -22,7 +22,7 @@ else - + } diff --git a/Oqtane.Client/Modules/Admin/Themes/View.razor b/Oqtane.Client/Modules/Admin/Themes/View.razor index 2510759d..d6d4b4de 100644 --- a/Oqtane.Client/Modules/Admin/Themes/View.razor +++ b/Oqtane.Client/Modules/Admin/Themes/View.razor @@ -24,7 +24,7 @@ - - - - - - - - - - } - else - { -

No [Module]s To Display

- } -} - - - -
-[Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:

-[RootPath]Oqtane.Client\Modules\[Module]\
-- Edit.razor - component for adding or editing content
-- Index.razor - main component for your module **the content you are reading is in this file**
-- ModuleInfo.cs - implements IModule interface to provide configuration settings for your module
-- Settings.razor - component for managing module settings
-- Services\I[Module]Service.cs - interface for defining service API methods
-- Services\[Module]Service.cs - implements service API interface methods

-[RootPath]Oqtane.Server\Modules\[Module]\
-- Controllers\[Module]Controller.cs - API methods implemented using a REST pattern
-- Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content
-- Repository\I[Module]Repository.cs - interface for defining repository methods
-- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core
-- Repository\[Module]Context.cs - provides a DB Context for data access
-- Scripts\[Owner].[Module]s.1.0.0.sql - database schema definition script
-- Scripts\[Owner].[Module]s.Uninstall.sql - database uninstall script

-[RootPath]Oqtane.Shared\Modules\[Module]\
-- Models\[Module].cs - model definition

- - - -@code { - public override List Resources => new List() - { - 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); - } - } -} \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Interop.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Interop.cs deleted file mode 100644 index 9d6f0a4e..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Interop.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/ModuleInfo.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/ModuleInfo.cs deleted file mode 100644 index 6586d51d..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/ModuleInfo.cs +++ /dev/null @@ -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" - }; - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/I[Module]Service.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/I[Module]Service.cs deleted file mode 100644 index 601eba6a..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/I[Module]Service.cs +++ /dev/null @@ -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> Get[Module]sAsync(int ModuleId); - - Task Get[Module]Async(int [Module]Id, int ModuleId); - - Task Add[Module]Async(Models.[Module] [Module]); - - Task Update[Module]Async(Models.[Module] [Module]); - - Task Delete[Module]Async(int [Module]Id, int ModuleId); - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/[Module]Service.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/[Module]Service.cs deleted file mode 100644 index 05a85ac7..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Services/[Module]Service.cs +++ /dev/null @@ -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> Get[Module]sAsync(int ModuleId) - { - List [Module]s = await GetJsonAsync>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", ModuleId)); - return [Module]s.OrderBy(item => item.Name).ToList(); - } - - public async Task Get[Module]Async(int [Module]Id, int ModuleId) - { - return await GetJsonAsync(CreateAuthorizationPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId)); - } - - public async Task Add[Module]Async(Models.[Module] [Module]) - { - return await PostJsonAsync(CreateAuthorizationPolicyUrl($"{Apiurl}", [Module].ModuleId), [Module]); - } - - public async Task Update[Module]Async(Models.[Module] [Module]) - { - return await PutJsonAsync(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)); - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Settings.razor b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Settings.razor deleted file mode 100644 index 7989d7c9..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Settings.razor +++ /dev/null @@ -1,47 +0,0 @@ -@namespace [Owner].[Module] -@inherits ModuleBase -@inject ISettingService SettingService - -
- @foreach (var theme in _themes) { - if (theme.TypeName == _themetype) - { - - } - else - { - - } + } @context.Name@context.Name
- + diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor index dcc96ff4..48959e80 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor @@ -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 diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor index ff9f3937..263a0345 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor @@ -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 diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index 8ac788ee..e648083f 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -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(); } diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index f4650202..90bc48c5 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -12,21 +12,14 @@ @if (ShowFolders || FolderId <= 0) {
- @if (string.IsNullOrEmpty(Folder)) { } @foreach (Folder folder in _folders) { - if (folder.FolderId == FolderId) - { - - } - else - { - - } + }
@@ -34,18 +27,11 @@ @if (ShowFiles) {
- @foreach (File file in _files) { - if (file.FileId == FileId) - { - - } - else - { - - } + }
diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor index 64f32728..c89b1301 100644 --- a/Oqtane.Client/Modules/Controls/Pager.razor +++ b/Oqtane.Client/Modules/Controls/Pager.razor @@ -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); diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 72024f75..f6ce11c8 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -5,7 +5,7 @@ Exe 3.0 Debug;Release - 2.0.0 + 2.0.1 Oqtane Shaun Walker .NET Foundation @@ -14,7 +14,7 @@ https://www.oqtane.org https://github.com/oqtane Git - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 Oqtane true true diff --git a/Oqtane.Client/Resources/.gitkeep b/Oqtane.Client/Resources/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs index e4b8ce64..55f8ad91 100644 --- a/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs +++ b/Oqtane.Client/Services/Interfaces/IModuleDefinitionService.cs @@ -13,5 +13,6 @@ namespace Oqtane.Services Task InstallModuleDefinitionsAsync(); Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId); Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition); + Task> GetModuleDefinitionTemplatesAsync(); } } diff --git a/Oqtane.Client/Services/Interfaces/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs index eca072c5..b47b3481 100644 --- a/Oqtane.Client/Services/Interfaces/IUserService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserService.cs @@ -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 UpdateUserAsync(User user); - Task DeleteUserAsync(int userId); + Task DeleteUserAsync(int userId, int siteId); Task LoginUserAsync(User user, bool setCookie, bool isPersistent); diff --git a/Oqtane.Client/Services/ModuleDefinitionService.cs b/Oqtane.Client/Services/ModuleDefinitionService.cs index 87ae7e09..9f8984c0 100644 --- a/Oqtane.Client/Services/ModuleDefinitionService.cs +++ b/Oqtane.Client/Services/ModuleDefinitionService.cs @@ -53,5 +53,11 @@ namespace Oqtane.Services { return await PostJsonAsync($"{Apiurl}", moduleDefinition); } + + public async Task> GetModuleDefinitionTemplatesAsync() + { + List templates = await GetJsonAsync>($"{Apiurl}/templates"); + return templates; + } } } diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index 34aaaebb..3538ecb7 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -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($"{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 LoginUserAsync(User user, bool setCookie, bool isPersistent) diff --git a/Oqtane.Client/Themes/Controls/MenuItemsHorizontal.razor b/Oqtane.Client/Themes/Controls/MenuItemsHorizontal.razor index 840fbf4e..5e75738b 100644 --- a/Oqtane.Client/Themes/Controls/MenuItemsHorizontal.razor +++ b/Oqtane.Client/Themes/Controls/MenuItemsHorizontal.razor @@ -8,15 +8,15 @@ { if (childPage.PageId == PageState.Page.PageId) { - - - @childPage.Name (current) + + } else { - - + + } @@ -34,7 +34,7 @@ else { @@ -43,7 +43,7 @@ else { @@ -55,7 +55,7 @@ else { @@ -18,7 +18,7 @@ { @@ -38,7 +38,7 @@ else { @@ -47,7 +47,7 @@ else { diff --git a/Oqtane.Client/UI/Pane.razor b/Oqtane.Client/UI/Pane.razor index 0c04ff94..139135b9 100644 --- a/Oqtane.Client/UI/Pane.razor +++ b/Oqtane.Client/UI/Pane.razor @@ -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 = "
" + Name + " Pane
"; } else { - _paneadminborder = "container"; + _useadminborder = false; _panetitle = ""; } @@ -130,4 +129,4 @@ else builder.SetKey(module.PageModuleId); builder.CloseComponent(); } -} \ No newline at end of file +} diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 6b42f15b..70bda7df 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -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; diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index 6b01cba1..c669a8a2 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -2,7 +2,7 @@ Oqtane.Client - 2.0.0 + 2.0.1 Shaun Walker .NET Foundation Oqtane Framework @@ -13,7 +13,7 @@ https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg oqtane - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 A modular application framework for Blazor diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 10313bc4..6e68fa19 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 2.0.0 + 2.0.1 Shaun Walker .NET Foundation Oqtane Framework @@ -13,7 +13,7 @@ https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg oqtane framework - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 A modular application framework for Blazor diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index 36715c37..6adfcb6b 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -2,7 +2,7 @@ Oqtane.Server - 2.0.0 + 2.0.1 Shaun Walker .NET Foundation Oqtane Framework @@ -13,7 +13,7 @@ https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg oqtane - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 A modular application framework for Blazor diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index 538e0c8d..59a93473 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -2,7 +2,7 @@ Oqtane.Shared - 2.0.0 + 2.0.1 Shaun Walker .NET Foundation Oqtane Framework @@ -13,7 +13,7 @@ https://github.com/oqtane/oqtane.framework https://www.oqtane.org/Portals/0/icon.jpg oqtane - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 A modular application framework for Blazor diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1 index 116d8567..d4b21d97 100644 --- a/Oqtane.Package/install.ps1 +++ b/Oqtane.Package/install.ps1 @@ -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 \ No newline at end of file +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.1.Install.zip" -Force \ No newline at end of file diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1 index e41001aa..57e1de1a 100644 --- a/Oqtane.Package/upgrade.ps1 +++ b/Oqtane.Package/upgrade.ps1 @@ -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 \ No newline at end of file +Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.1.Upgrade.zip" -Force \ No newline at end of file diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index cac15728..2d8491d6 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -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" ); + } } } } diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index dc371214..8769c120 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -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 assets = JsonSerializer.Deserialize>(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//templates + [HttpGet("templates")] + [Authorize(Roles = RoleNames.Host)] + public List Get() + { + var templates = new List(); + 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/?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 resources = new List(); - 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 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); - } } } diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index 5c73eb78..8fbb4872 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -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; } diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs index 615080e9..851aeb68 100644 --- a/Oqtane.Server/Controllers/ThemeController.cs +++ b/Oqtane.Server/Controllers/ThemeController.cs @@ -63,6 +63,7 @@ namespace Oqtane.Controllers { // use assets.json to clean up file resources List assets = JsonSerializer.Deserialize>(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); diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 9819994b..4d3f1abc 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -111,7 +111,6 @@ namespace Oqtane.Controllers return null; } - //TODO shoud be moved to another layer private async Task CreateUser(User user) { User newUser = null; @@ -261,18 +260,50 @@ namespace Oqtane.Controllers // DELETE api//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()); + } + } } } } diff --git a/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs b/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs new file mode 100644 index 00000000..e34d1563 --- /dev/null +++ b/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs @@ -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; + } + } +} diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 35f93353..9ee98e25 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -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(); - 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(); - + using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))) { foreach (var tenant in db.Tenant.ToList()) diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index f14e8ae1..3b627c70 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -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)); } } diff --git a/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs index ffb0ff9c..6257db16 100644 --- a/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/Interfaces/IDatabaseManager.cs @@ -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); } diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index 3dd8ab3f..e21629d8 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -65,7 +65,7 @@ namespace Oqtane.SiteTemplates new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }.EncodePermissions(), - Content = "

Copyright (c) 2019-2020 .NET Foundation

" + + Content = "

Copyright (c) 2019-2021 .NET Foundation

" + "

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:

" + "

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

" + "

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.

" diff --git a/Oqtane.Server/Infrastructure/SyncManager.cs b/Oqtane.Server/Infrastructure/SyncManager.cs index f41c01b0..d70cc456 100644 --- a/Oqtane.Server/Infrastructure/SyncManager.cs +++ b/Oqtane.Server/Infrastructure/SyncManager.cs @@ -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 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) diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs index 0344de6e..b2385b2b 100644 --- a/Oqtane.Server/Infrastructure/UpgradeManager.cs +++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs @@ -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(); - + // 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(); //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; } } diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 474b9335..4a6802f5 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -3,7 +3,7 @@ net5.0 Debug;Release - 2.0.0 + 2.0.1 Oqtane Shaun Walker .NET Foundation @@ -12,7 +12,7 @@ https://www.oqtane.org https://github.com/oqtane Git - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 Oqtane true diff --git a/Oqtane.Server/Pages/_Host.cshtml b/Oqtane.Server/Pages/_Host.cshtml index cf2d69f0..1678969c 100644 --- a/Oqtane.Server/Pages/_Host.cshtml +++ b/Oqtane.Server/Pages/_Host.cshtml @@ -1,4 +1,4 @@ -@page "/" +@page "/" @namespace Oqtane.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.Extensions.Configuration @@ -35,6 +35,13 @@ 🗙 + @if (Model.Message != "") + { +
+ @Model.Message +
+ } + @if (Configuration.GetSection("Runtime").Value == "WebAssembly") diff --git a/Oqtane.Server/Pages/_Host.cshtml.cs b/Oqtane.Server/Pages/_Host.cshtml.cs index 4dc9cb7e..156b5fac 100644 --- a/Oqtane.Server/Pages/_Host.cshtml.cs +++ b/Oqtane.Server/Pages/_Host.cshtml.cs @@ -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}"; } } } diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index 522bf0d9..9d488f45 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -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 + public class DBContextBase : IdentityUserContext { 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(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(nameof(IDeletable.IsDeleted))) - { - item.CurrentValues[nameof(IDeletable.DeletedBy)] = null; - item.CurrentValues[nameof(IDeletable.DeletedOn)] = null; - } - } - } + DbContextUtils.SaveChanges(this, _accessor); return base.SaveChanges(); } diff --git a/Oqtane.Server/Repository/Context/DbContextUtils.cs b/Oqtane.Server/Repository/Context/DbContextUtils.cs new file mode 100644 index 00000000..1b4014cb --- /dev/null +++ b/Oqtane.Server/Repository/Context/DbContextUtils.cs @@ -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(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(nameof(IDeletable.IsDeleted))) + { + item.CurrentValues[nameof(IDeletable.DeletedBy)] = null; + item.CurrentValues[nameof(IDeletable.DeletedOn)] = null; + } + } + } + } + } +} diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs index 29bfc281..6eaf5a65 100644 --- a/Oqtane.Server/Repository/Context/InstallationContext.cs +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -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 { get; set; } public virtual DbSet Tenant { get; set; } diff --git a/Oqtane.Server/Repository/Context/MasterDBContext.cs b/Oqtane.Server/Repository/Context/MasterDBContext.cs index 976874cf..dd94dedd 100644 --- a/Oqtane.Server/Repository/Context/MasterDBContext.cs +++ b/Oqtane.Server/Repository/Context/MasterDBContext.cs @@ -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 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(); } diff --git a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs index 5ce7467f..dba914a7 100644 --- a/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IFolderRepository.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Oqtane.Models; namespace Oqtane.Repository diff --git a/Oqtane.Server/Repository/UserRoleRepository.cs b/Oqtane.Server/Repository/UserRoleRepository.cs index 79f8a629..b47bd6f7 100644 --- a/Oqtane.Server/Repository/UserRoleRepository.cs +++ b/Oqtane.Server/Repository/UserRoleRepository.cs @@ -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) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index ba2a7d89..1e744c01 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -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); } } } diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Edit.razor b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/Edit.razor similarity index 100% rename from Oqtane.Server/wwwroot/Modules/Templates/External/Client/Edit.razor rename to Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/Edit.razor diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Index.razor b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/Index.razor similarity index 100% rename from Oqtane.Server/wwwroot/Modules/Templates/External/Client/Index.razor rename to Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/Index.razor diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/ModuleInfo.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/ModuleInfo.cs similarity index 100% rename from Oqtane.Server/wwwroot/Modules/Templates/External/Client/ModuleInfo.cs rename to Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/ModuleInfo.cs diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Settings.razor b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/Settings.razor similarity index 100% rename from Oqtane.Server/wwwroot/Modules/Templates/External/Client/Settings.razor rename to Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].[Module]/Settings.razor diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module].Server.csproj b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module].Server.csproj index cfce1a60..ec0e8e4a 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module].Server.csproj +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Server/[Owner].[Module].Server.csproj @@ -2,7 +2,6 @@ net5.0 - 7.3 true 1.0.0 [Owner].[Module] diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Edit.razor b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Edit.razor deleted file mode 100644 index 339879f8..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Edit.razor +++ /dev/null @@ -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 - - - - - - -
- - - -
- -Cancel -
-
-@if (PageState.Action == "Edit") -{ - -} - -@code { - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; - - public override string Actions => "Add,Edit"; - - public override string Title => "Manage [Module]"; - - public override List Resources => new List() - { - 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); - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Index.razor b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Index.razor deleted file mode 100644 index 37cfef2a..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Client/Modules/[Owner].[Module]/Index.razor +++ /dev/null @@ -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) -{ -

Loading...

-} -else -{ - -
-
- @if (@_[Module]s.Count != 0) - { - -
-
  Name@context.Name
- - - - -
- - - -
- -@code { - public override string Title => "[Module] Settings"; - - string _value; - - protected override async Task OnInitializedAsync() - { - try - { - Dictionary 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 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); - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Controllers/[Module]Controller.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Controllers/[Module]Controller.cs deleted file mode 100644 index 3e2fe7b8..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Controllers/[Module]Controller.cs +++ /dev/null @@ -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/?moduleid=x - [HttpGet] - [Authorize(Policy = PolicyNames.ViewModule)] - public IEnumerable Get(string moduleid) - { - return _[Module]Repository.Get[Module]s(int.Parse(moduleid)); - } - - // GET api//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/ - [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//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//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); - } - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Manager/[Module]Manager.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Manager/[Module]Manager.cs deleted file mode 100644 index 486c8ddf..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Manager/[Module]Manager.cs +++ /dev/null @@ -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 [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 [Module]s = null; - if (!string.IsNullOrEmpty(content)) - { - [Module]s = JsonSerializer.Deserialize>(content); - } - if ([Module]s != null) - { - foreach(var [Module] in [Module]s) - { - _[Module]Repository.Add[Module](new Models.[Module] { ModuleId = module.ModuleId, Name = [Module].Name }); - } - } - } - } -} \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/I[Module]Repository.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/I[Module]Repository.cs deleted file mode 100644 index 52b7913a..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/I[Module]Repository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using [Owner].[Module].Models; - -namespace [Owner].[Module].Repository -{ - public interface I[Module]Repository - { - IEnumerable 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); - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Context.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Context.cs deleted file mode 100644 index a10d7219..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Context.cs +++ /dev/null @@ -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 [Module] { get; set; } - - public [Module]Context(ITenantResolver tenantResolver, IHttpContextAccessor accessor) : base(tenantResolver, accessor) - { - // ContextBase handles multi-tenant database connections - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Repository.cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Repository.cs deleted file mode 100644 index 9c8c3628..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Repository/[Module]Repository.cs +++ /dev/null @@ -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 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(); - } - } -} diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module].1.0.0.sql b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module].1.0.0.sql deleted file mode 100644 index 7a1b99ea..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module].1.0.0.sql +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module].Uninstall.sql b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module].Uninstall.sql deleted file mode 100644 index 47baecc9..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/Modules/[Owner].[Module]/Scripts/[Owner].[Module].Uninstall.sql +++ /dev/null @@ -1,6 +0,0 @@ -/* -Remove [Owner][Module] table -*/ - -DROP TABLE [dbo].[[Owner][Module]] -GO diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/wwwroot/Modules/[Owner].[Module]/Module.css b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/wwwroot/Modules/[Owner].[Module]/Module.css deleted file mode 100644 index 0856a263..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/wwwroot/Modules/[Owner].[Module]/Module.css +++ /dev/null @@ -1 +0,0 @@ -/* Module Custom Styles */ \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/wwwroot/Modules/[Owner].[Module]/Module.js b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/wwwroot/Modules/[Owner].[Module]/Module.js deleted file mode 100644 index 8f072470..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Server/wwwroot/Modules/[Owner].[Module]/Module.js +++ /dev/null @@ -1,5 +0,0 @@ -/* Module Script */ -var [Owner] = [Owner] || {}; - -[Owner].[Module] = { -}; \ No newline at end of file diff --git a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Shared/Modules/[Owner].[Module]/Models/[Module].cs b/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Shared/Modules/[Owner].[Module]/Models/[Module].cs deleted file mode 100644 index 94f73ffa..00000000 --- a/Oqtane.Server/wwwroot/Modules/Templates/Internal/Oqtane.Shared/Modules/[Owner].[Module]/Models/[Module].cs +++ /dev/null @@ -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; } - } -} diff --git a/Oqtane.Server/wwwroot/css/app.css b/Oqtane.Server/wwwroot/css/app.css index ce87887d..eebf63a6 100644 --- a/Oqtane.Server/wwwroot/css/app.css +++ b/Oqtane.Server/wwwroot/css/app.css @@ -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; diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 976f255b..65df5df5 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ net5.0 Debug;Release - 2.0.0 + 2.0.1 Oqtane Shaun Walker .NET Foundation @@ -12,7 +12,7 @@ https://www.oqtane.org https://github.com/oqtane Git - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 Oqtane true diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 51e71450..60106dd2 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -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 = diff --git a/Oqtane.Test/Oqtane.Test.csproj b/Oqtane.Test/Oqtane.Test.csproj index 78c230db..da295fb4 100644 --- a/Oqtane.Test/Oqtane.Test.csproj +++ b/Oqtane.Test/Oqtane.Test.csproj @@ -3,7 +3,7 @@ net5.0 Debug;Release - 2.0.0 + 2.0.1 Oqtane Shaun Walker .NET Foundation @@ -12,7 +12,7 @@ https://www.oqtane.org https://github.com/oqtane Git - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 Oqtane false diff --git a/Oqtane.Upgrade/Oqtane.Upgrade.csproj b/Oqtane.Upgrade/Oqtane.Upgrade.csproj index cd3d0a4c..f67d23d2 100644 --- a/Oqtane.Upgrade/Oqtane.Upgrade.csproj +++ b/Oqtane.Upgrade/Oqtane.Upgrade.csproj @@ -3,7 +3,7 @@ net5.0 Exe - 2.0.0 + 2.0.1 Oqtane Shaun Walker .NET Foundation @@ -12,7 +12,7 @@ https://www.oqtane.org https://github.com/oqtane Git - https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1 Oqtane false diff --git a/Oqtane.sln b/Oqtane.sln index 2755bdb2..70e85123 100644 --- a/Oqtane.sln +++ b/Oqtane.sln @@ -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 diff --git a/README.md b/README.md index 5d0bb04c..20559301 100644 --- a/README.md +++ b/README.md @@ -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