From f739db1e42b6caf0ff88966fcc8e21adf28aa478 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 20 Sep 2021 17:15:52 -0400 Subject: [PATCH] Enhance Settings API for public Site Settings. Added Settings to Site model by default. Added new parameters to Login and UserProfile components. Enhanced Oqtane Theme settings to use new component parameters. Enhanced image download and resizing logic. --- Oqtane.Client/Modules/Admin/Files/Add.razor | 29 +-- .../Modules/Admin/Modules/Settings.razor | 1 + Oqtane.Client/Modules/Admin/Pages/Edit.razor | 1 + .../Modules/Admin/UserProfile/Index.razor | 2 +- Oqtane.Client/Modules/ModuleBase.cs | 4 +- .../Resources/Modules/Admin/Files/Add.resx | 6 + Oqtane.Client/Resources/SharedResources.resx | 3 + .../OqtaneTheme/Themes/ThemeSettings.resx | 140 ++++++++------- Oqtane.Client/Services/FileService.cs | 4 +- .../Services/Interfaces/IFileService.cs | 3 +- .../Services/Interfaces/ISettingService.cs | 8 +- Oqtane.Client/Services/SettingService.cs | 47 ++++- .../Themes/Controls/Theme/Login.razor | 13 +- .../Themes/Controls/Theme/UserProfile.razor | 7 +- .../Themes/OqtaneTheme/Themes/Default.razor | 9 +- .../OqtaneTheme/Themes/ThemeSettings.razor | 168 ++++++++++++++---- Oqtane.Client/Themes/ThemeBase.cs | 4 +- Oqtane.Server/Controllers/FileController.cs | 96 +++++----- .../Controllers/SettingController.cs | 44 +++-- Oqtane.Server/Controllers/SiteController.cs | 10 +- .../Tenant/02030002_AddSettingIsPublic.cs | 31 ++++ Oqtane.Shared/Models/Page.cs | 3 + Oqtane.Shared/Models/Setting.cs | 5 + Oqtane.Shared/Models/Site.cs | 4 + Oqtane.Shared/Shared/Utilities.cs | 4 +- 25 files changed, 458 insertions(+), 188 deletions(-) create mode 100644 Oqtane.Server/Migrations/Tenant/02030002_AddSettingIsPublic.cs diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index a4f17c73..a36e6cc2 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -27,7 +27,7 @@
- +
@@ -42,6 +42,12 @@
+
+ +
+ +
+
@SharedLocalizer["Cancel"] @@ -53,9 +59,10 @@ @code { private ElementReference form; private bool validated = false; - private string url = string.Empty; + private string _url = string.Empty; private List _folders; private int _folderId = -1; + private string _name = ""; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; @@ -75,22 +82,24 @@ var interop = new Interop(JSRuntime); if (await interop.FormValid(form)) { - if (url == string.Empty || _folderId == -1) + if (_url == string.Empty || _folderId == -1) { AddModuleMessage(Localizer["Message.Required.UrlFolder"], MessageType.Warning); return; } - var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + if (string.IsNullOrEmpty(_name)) + { + _name = _url.Substring(_url.LastIndexOf("/", StringComparison.Ordinal) + 1); + } - if (!Constants.UploadableFiles.Split(',') - .Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) + if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", ""))) { AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning); return; } - if (!filename.IsPathOrFileValid()) + if (!_name.IsPathOrFileValid()) { AddModuleMessage(Localizer["Message.Required.UrlName"], MessageType.Warning); return; @@ -98,13 +107,13 @@ try { - await FileService.UploadFileAsync(url, _folderId); - await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); + await FileService.UploadFileAsync(_url, _folderId, _name); + await logger.LogInformation("File Downloaded Successfully From Url {Url}", _url); AddModuleMessage(Localizer["Success.Download.File"], MessageType.Success); } catch (Exception ex) { - await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); + await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", _url, ex.Message); AddModuleMessage(Localizer["Error.Download.InvalidUrl"], MessageType.Error); } } diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index c2c3f6ef..e86ba978 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -81,6 +81,7 @@ } +
@SharedLocalizer["Cancel"]
diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index f7cbb612..00e02fab 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -162,6 +162,7 @@ @ThemeSettingsComponent +
} diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index d5771d47..5644482d 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -12,7 +12,7 @@ @if (PageState.User != null && photo != null) { - @displayname + @displayname } else { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 8db02254..c4c5bef9 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -134,9 +134,9 @@ namespace Oqtane.Modules return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); } - public string ImageUrl(int fileid, string size, string mode) + public string ImageUrl(int fileid, int width, int height, string mode) { - return Utilities.ImageUrl(PageState.Alias, fileid, size, mode); + return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode); } public virtual Dictionary GetUrlParameters(string parametersTemplate = "") diff --git a/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx index fae32989..67845d7b 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx @@ -156,4 +156,10 @@ Upload Files + + Enter the name of the file being downloaded + + + Name: + \ No newline at end of file diff --git a/Oqtane.Client/Resources/SharedResources.resx b/Oqtane.Client/Resources/SharedResources.resx index 86a88e73..b89dd91d 100644 --- a/Oqtane.Client/Resources/SharedResources.resx +++ b/Oqtane.Client/Resources/SharedResources.resx @@ -309,4 +309,7 @@ Extend + + Not Specified + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Themes/OqtaneTheme/Themes/ThemeSettings.resx b/Oqtane.Client/Resources/Themes/OqtaneTheme/Themes/ThemeSettings.resx index 34b6b05b..7d8c7c4d 100644 --- a/Oqtane.Client/Resources/Themes/OqtaneTheme/Themes/ThemeSettings.resx +++ b/Oqtane.Client/Resources/Themes/OqtaneTheme/Themes/ThemeSettings.resx @@ -1,65 +1,65 @@  - + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + @@ -117,10 +117,28 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Specify If The Page Footer Should Be Displayed + + Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page. - + Display Footer? + + Specify if a Login option should be displayed, Note that this option does not prevent the login page from being accessible via a direct url. + + + Show Login? + + + Page + + + Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings. + + + Show Register? + + + Site + \ No newline at end of file diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 9848e792..17d93b02 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -67,9 +67,9 @@ namespace Oqtane.Services await DeleteAsync($"{Apiurl}/{fileId}"); } - public async Task UploadFileAsync(string url, int folderId) + public async Task UploadFileAsync(string url, int folderId, string name) { - return await GetJsonAsync($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}"); + return await GetJsonAsync($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}"); } public async Task UploadFilesAsync(int folderId, string[] files, string id) diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index 1a756e4a..f46d8fca 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -62,8 +62,9 @@ namespace Oqtane.Services /// /// /// + /// /// - Task UploadFileAsync(string url, int folderId); + Task UploadFileAsync(string url, int folderId, string name); /// /// Upload one or more files. diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs index 97c6266d..e2cb50d4 100644 --- a/Oqtane.Client/Services/Interfaces/ISettingService.cs +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -1,4 +1,4 @@ -using Oqtane.Models; +using Oqtane.Models; using System.Collections.Generic; using System.Threading.Tasks; @@ -51,5 +51,9 @@ namespace Oqtane.Services string GetSetting(Dictionary settings, string settingName, string defaultValue); Dictionary SetSetting(Dictionary settings, string settingName, string settingValue); - } + + Dictionary SetSetting(Dictionary settings, string settingName, string settingValue, bool isPublic); + + Dictionary MergeSettings(Dictionary settings1, Dictionary settings2); +} } diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index 7ac1eff3..4d04838b 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -109,21 +109,31 @@ namespace Oqtane.Services foreach (KeyValuePair kvp in settings) { - Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase)); + string value = kvp.Value; + bool ispublic = false; + if (value.StartsWith("[Public]")) + { + value = value.Substring(8); // remove [Public] + ispublic = true; + } + + Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase)); if (setting == null) { setting = new Setting(); setting.EntityName = entityName; setting.EntityId = entityId; setting.SettingName = kvp.Key; - setting.SettingValue = kvp.Value; + setting.SettingValue = value; + setting.IsPublic = ispublic; setting = await AddSettingAsync(setting); } else { if (setting.SettingValue != kvp.Value) { - setting.SettingValue = kvp.Value; + setting.SettingValue = value; + setting.IsPublic = ispublic; setting = await UpdateSettingAsync(setting); } } @@ -163,13 +173,19 @@ namespace Oqtane.Services } public Dictionary SetSetting(Dictionary settings, string settingName, string settingValue) + { + return SetSetting(settings, settingName, settingValue, false); + } + + public Dictionary SetSetting(Dictionary settings, string settingName, string settingValue, bool isPublic) { if (settings == null) { settings = new Dictionary(); } + settingValue = (isPublic) ? "[Public]" + settingValue : settingValue; if (settings.ContainsKey(settingName)) - { + { settings[settingName] = settingValue; } else @@ -178,5 +194,28 @@ namespace Oqtane.Services } return settings; } + + public Dictionary MergeSettings(Dictionary settings1, Dictionary settings2) + { + if (settings1 == null) + { + settings1 = new Dictionary(); + } + if (settings2 != null) + { + foreach (var setting in settings2) + { + if (settings1.ContainsKey(setting.Key)) + { + settings1[setting.Key] = setting.Value; + } + else + { + settings1.Add(setting.Key, setting.Value); + } + } + } + return settings1; + } } } diff --git a/Oqtane.Client/Themes/Controls/Theme/Login.razor b/Oqtane.Client/Themes/Controls/Theme/Login.razor index 98f880fc..906c2b89 100644 --- a/Oqtane.Client/Themes/Controls/Theme/Login.razor +++ b/Oqtane.Client/Themes/Controls/Theme/Login.razor @@ -12,7 +12,16 @@ - + @if (ShowLogin) + { + + } - \ No newline at end of file + + +@code +{ + [Parameter] + public bool ShowLogin { get; set; } +} \ No newline at end of file diff --git a/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor b/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor index fca3cfd0..36a5e0c1 100644 --- a/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor +++ b/Oqtane.Client/Themes/Controls/Theme/UserProfile.razor @@ -13,9 +13,9 @@ - @if (PageState.Site.AllowRegistration) + @if (ShowRegister && PageState.Site.AllowRegistration) { - + } @@ -23,6 +23,9 @@ @code { + [Parameter] + public bool ShowRegister { get; set; } + private void RegisterUser() { NavigationManager.NavigateTo(NavigateUrl("register")); diff --git a/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor b/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor index 9779050c..85ddaa9e 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Themes/Default.razor @@ -6,7 +6,7 @@
@@ -117,13 +117,18 @@ new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", Integrity = "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM", CrossOrigin = "anonymous" } }; + private bool _login = true; + private bool _register = true; private bool _footer = false; protected override void OnParametersSet() { try { - _footer = bool.Parse(SettingService.GetSetting(PageState.Page.Settings, GetType().Namespace + ":Footer", "false")); + var settings = SettingService.MergeSettings(PageState.Site.Settings, PageState.Page.Settings); + _login = bool.Parse(SettingService.GetSetting(settings, GetType().Namespace + ":Login", "true")); + _register = bool.Parse(SettingService.GetSetting(settings, GetType().Namespace + ":Register", "true")); + _footer = bool.Parse(SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "false")); } catch { diff --git a/Oqtane.Client/Themes/OqtaneTheme/Themes/ThemeSettings.razor b/Oqtane.Client/Themes/OqtaneTheme/Themes/ThemeSettings.razor index 407a5c99..08b41fcd 100644 --- a/Oqtane.Client/Themes/OqtaneTheme/Themes/ThemeSettings.razor +++ b/Oqtane.Client/Themes/OqtaneTheme/Themes/ThemeSettings.razor @@ -2,47 +2,149 @@ @inherits ModuleBase @implements Oqtane.Interfaces.ISettingsControl @inject ISettingService SettingService +@inject IStringLocalizer Localizer +@inject IStringLocalizer SharedLocalizer @attribute [OqtaneIgnore] -
-
- -
- -
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+@code { + private string _scope = "page"; + private string _login = "-"; + private string _register = "-"; + private string _footer = "-"; - @code { - private string _footer = "false"; - - protected override void OnInitialized() + protected override async Task OnInitializedAsync() + { + try { - try - { - _footer = SettingService.GetSetting(PageState.Page.Settings, GetType().Namespace + ":Footer", "false"); - } - catch (Exception ex) - { - ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); - } + await LoadSettings(); } - - public async Task UpdateSettings() + catch (Exception ex) { - try - { - var settings = PageState.Page.Settings; - settings = SettingService.SetSetting(settings, GetType().Namespace + ":Footer", _footer); - await SettingService.UpdatePageSettingsAsync(settings, PageState.Page.PageId); - } - catch (Exception ex) - { - ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error); - } + await logger.LogError(ex, "Error Loading Settings {Error}", ex.Message); + AddModuleMessage("Error Loading Settings", MessageType.Error); } } + + private async Task LoadSettings() + { + await Task.Yield(); + Dictionary settings; + if (_scope == "site") + { + settings = PageState.Site.Settings; + _login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "true"); + _register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "true"); + _footer = SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "false"); + } + else + { + settings = SettingService.MergeSettings(PageState.Site.Settings, PageState.Page.Settings); + _login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "-"); + _register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "-"); + _footer = SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "-"); + } + } + + private async Task ScopeChanged(ChangeEventArgs eventArgs) + { + try + { + _scope = (string)eventArgs.Value; + await LoadSettings(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Settings {Error}", ex.Message); + AddModuleMessage("Error Loading Settings", MessageType.Error); + } + } + + public async Task UpdateSettings() + { + try + { + Dictionary settings; + if (_scope == "site") + { + settings = PageState.Site.Settings; + } + else + { + settings = PageState.Page.Settings; + } + + if (_login != "-") + { + settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login, true); + } + if (_register != "-") + { + settings = SettingService.SetSetting(settings, GetType().Namespace + ":Register", _register, true); + } + if (_footer != "-") + { + settings = SettingService.SetSetting(settings, GetType().Namespace + ":Footer", _footer, true); + } + + if (_scope == "site") + { + await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId); + } + else + { + await SettingService.UpdatePageSettingsAsync(settings, PageState.Page.PageId); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Saving Settings {Error}", ex.Message); + AddModuleMessage("Error Saving Settings", MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 2ab10f7b..6dbece0b 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -104,9 +104,9 @@ namespace Oqtane.Themes return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); } - public string ImageUrl(int fileid, string size, string mode) + public string ImageUrl(int fileid, int width, int height, string mode) { - return Utilities.ImageUrl(PageState.Alias, fileid, size, mode); + return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode); } } } diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index efccfa95..57ffb98f 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -188,9 +188,9 @@ namespace Oqtane.Controllers } } - // GET api//upload?url=x&folderid=y + // GET api//upload?url=x&folderid=y&name=z [HttpGet("upload")] - public Models.File UploadFile(string url, string folderid) + public Models.File UploadFile(string url, string folderid, string name) { Models.File file = null; @@ -206,16 +206,19 @@ namespace Oqtane.Controllers string folderPath = _folders.GetFolderPath(folder); CreateDirectory(folderPath); - string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + if (string.IsNullOrEmpty(name)) + { + name = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + } // check for allowable file extensions - if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) + if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(name).ToLower().Replace(".", ""))) { _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url); HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; return file; } - if (!filename.IsPathOrFileValid()) + if (!name.IsPathOrFileValid()) { _logger.Log(LogLevel.Error, this, LogFunction.Create, $"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}"); HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; @@ -225,7 +228,7 @@ namespace Oqtane.Controllers try { var client = new WebClient(); - string targetPath = Path.Combine(folderPath, filename); + string targetPath = Path.Combine(folderPath, name); // remove file if it already exists if (System.IO.File.Exists(targetPath)) { @@ -233,15 +236,15 @@ namespace Oqtane.Controllers } client.DownloadFile(url, targetPath); - file = CreateFile(filename, folder.FolderId, targetPath); + file = CreateFile(name, folder.FolderId, targetPath); if (file != null) { file = _files.AddFile(file); } } - catch + catch (Exception ex) { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url} {Error}", url, ex.Message); } } else @@ -494,8 +497,8 @@ namespace Oqtane.Controllers return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null; } - [HttpGet("image/{id}/{size}/{mode?}")] - public IActionResult GetImage(int id, string size, string mode) + [HttpGet("image/{id}/{width}/{height}/{mode?}")] + public IActionResult GetImage(int id, int width, int height, string mode) { var file = _files.GetFile(id); if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) @@ -505,27 +508,31 @@ namespace Oqtane.Controllers var filepath = _files.GetFilePath(file); if (System.IO.File.Exists(filepath)) { - size = size.ToLower(); mode = (string.IsNullOrEmpty(mode)) ? "crop" : mode; - if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.Permissions) || - size.Contains("x") && !string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(size)) - && Enum.TryParse(mode, true, out ResizeMode resizemode)) + + string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + mode.ToLower() + ".png"); + if (!System.IO.File.Exists(imagepath)) { - var imagepath = CreateImage(filepath, size, resizemode.ToString()); - if (!string.IsNullOrEmpty(imagepath)) + if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.Permissions) || + !string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString())) + && Enum.TryParse(mode, true, out ResizeMode resizemode)) { - return PhysicalFile(imagepath, file.GetMimeType()); + imagepath = CreateImage(filepath, width, height, resizemode.ToString(), imagepath); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "Error Creating Image For File {File} {Size}", file, size); - HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Invalid Image Size For Folder Or Invalid Mode Specification {Folder} {Width} {Height} {Mode}", file.Folder, width, height, mode); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } + if (!string.IsNullOrEmpty(imagepath)) + { + return PhysicalFile(imagepath, file.GetMimeType()); + } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Invalid Image Size For Folder Or Invalid Mode Specification {Folder} {Size} {Mode}", file.Folder, size, mode); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + _logger.Log(LogLevel.Error, this, LogFunction.Create, "Error Displaying Image For File {File} {Width} {Height}", file, width, height); + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; } } else @@ -550,38 +557,31 @@ namespace Oqtane.Controllers return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null; } - private string CreateImage(string filepath, string size, string mode) + private string CreateImage(string filepath, int width, int height, string mode, string imagepath) { - string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + size + "." + mode.ToLower() + ".png"); - - if (!System.IO.File.Exists(imagepath)) + try { - try + FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read); + using (Image image = Image.Load(stream)) { - FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read); - using (Image image = Image.Load(stream)) - { - var parts = size.Split('x'); - int width = (!string.IsNullOrEmpty(parts[0])) ? int.Parse(parts[0]) : 0; - int height = (!string.IsNullOrEmpty(parts[1])) ? int.Parse(parts[1]) : 0; - Enum.TryParse(mode, true, out ResizeMode resizemode); + Enum.TryParse(mode, true, out ResizeMode resizemode); - image.Mutate(x => - x.Resize(new ResizeOptions - { - Size = new Size(width, height), - Mode = resizemode - }) - .BackgroundColor(new Rgba32(255, 255, 255, 0))); + image.Mutate(x => + x.Resize(new ResizeOptions + { + Size = new Size(width, height), + Mode = resizemode + }) + .BackgroundColor(new Rgba32(255, 255, 255, 0))); - image.Save(imagepath, new PngEncoder()); - } - stream.Close(); - } - catch // error creating image - { - imagepath = ""; + image.Save(imagepath, new PngEncoder()); } + stream.Close(); + } + catch (Exception ex) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Error}", filepath, width, height, mode, ex.Message); + imagepath = ""; } return imagepath; diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 32d0573b..b305686e 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -39,6 +39,10 @@ namespace Oqtane.Controllers if (IsAuthorized(entityname, entityid, PermissionNames.View)) { settings = _settings.GetSettings(entityname, entityid).ToList(); + if (entityname == EntityNames.Site && !User.IsInRole(RoleNames.Admin)) + { + settings = settings.Where(item => item.IsPublic).ToList(); + } } else { @@ -55,6 +59,10 @@ namespace Oqtane.Controllers Setting setting = _settings.GetSetting(id); if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.View)) { + if (setting.EntityName == EntityNames.Site && !User.IsInRole(RoleNames.Admin) && !setting.IsPublic) + { + setting = null; + } return setting; } else @@ -72,10 +80,7 @@ namespace Oqtane.Controllers if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit)) { setting = _settings.AddSetting(setting); - if (setting.EntityName == EntityNames.Module) - { - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId); - } + AddSyncEvent(setting.EntityName); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting); } else @@ -94,10 +99,7 @@ namespace Oqtane.Controllers if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit)) { setting = _settings.UpdateSetting(setting); - if (setting.EntityName == EntityNames.Module) - { - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId); - } + AddSyncEvent(setting.EntityName); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting); } else @@ -117,10 +119,7 @@ namespace Oqtane.Controllers if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit)) { _settings.DeleteSetting(id); - if (setting.EntityName == EntityNames.Module) - { - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId); - } + AddSyncEvent(setting.EntityName); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting); } else @@ -144,7 +143,14 @@ namespace Oqtane.Controllers authorized = User.IsInRole(RoleNames.Host); break; case EntityNames.Site: - authorized = User.IsInRole(RoleNames.Admin); + if (permissionName == PermissionNames.Edit) + { + authorized = User.IsInRole(RoleNames.Admin); + } + else + { + authorized = true; + } break; case EntityNames.Page: case EntityNames.Module: @@ -161,5 +167,17 @@ namespace Oqtane.Controllers } return authorized; } + + private void AddSyncEvent(string EntityName) + { + switch (EntityName) + { + case EntityNames.Module: + case EntityNames.Page: + case EntityNames.Site: + _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId); + break; + } + } } } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index 78715eaa..b67cc126 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -15,13 +15,15 @@ namespace Oqtane.Controllers public class SiteController : Controller { private readonly ISiteRepository _sites; + private readonly ISettingRepository _settings; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly Alias _alias; - public SiteController(ISiteRepository sites, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) + public SiteController(ISiteRepository sites, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) { _sites = sites; + _settings = settings; _syncManager = syncManager; _logger = logger; _alias = tenantManager.GetAlias(); @@ -42,6 +44,12 @@ namespace Oqtane.Controllers var site = _sites.GetSite(id); if (site.SiteId == _alias.SiteId) { + var settings = _settings.GetSettings(EntityNames.Site, site.SiteId); + if (!User.IsInRole(RoleNames.Admin)) + { + settings = settings.Where(item => item.IsPublic); + } + site.Settings = settings.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); return site; } else diff --git a/Oqtane.Server/Migrations/Tenant/02030002_AddSettingIsPublic.cs b/Oqtane.Server/Migrations/Tenant/02030002_AddSettingIsPublic.cs new file mode 100644 index 00000000..74d1e18a --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/02030002_AddSettingIsPublic.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; +using Oqtane.Shared; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.02.03.00.02")] + public class AddSettingIsPublic : MultiDatabaseMigration + { + public AddSettingIsPublic(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase); + settingEntityBuilder.AddBooleanColumn("IsPublic", true); + settingEntityBuilder.UpdateColumn("IsPublic", "0"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase); + settingEntityBuilder.DropColumn("IsPublic"); + } + } +} diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index 0ceb2dbb..e11661f1 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -104,10 +104,13 @@ namespace Oqtane.Models ///
[NotMapped] public List Resources { get; set; } + [NotMapped] public string Permissions { get; set; } + [NotMapped] public Dictionary Settings { get; set; } + [NotMapped] public int Level { get; set; } diff --git a/Oqtane.Shared/Models/Setting.cs b/Oqtane.Shared/Models/Setting.cs index 5619415d..37d72dbe 100644 --- a/Oqtane.Shared/Models/Setting.cs +++ b/Oqtane.Shared/Models/Setting.cs @@ -32,6 +32,11 @@ namespace Oqtane.Models /// public string SettingValue { get; set; } + /// + /// Indicates if this setting is publicly available - only applicable to Site Settings as other entities have more granular permissions + /// + public bool IsPublic { get; set; } + #region IAuditable Properties /// diff --git a/Oqtane.Shared/Models/Site.cs b/Oqtane.Shared/Models/Site.cs index 86492571..82a7fc62 100644 --- a/Oqtane.Shared/Models/Site.cs +++ b/Oqtane.Shared/Models/Site.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace Oqtane.Models @@ -81,6 +82,9 @@ namespace Oqtane.Models [NotMapped] public string SiteTemplateType { get; set; } + [NotMapped] + public Dictionary Settings { get; set; } + [NotMapped] [Obsolete("This property is deprecated.", false)] public string DefaultLayoutType { get; set; } diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index aad5849a..aee8b568 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -112,10 +112,10 @@ namespace Oqtane.Shared return $"{aliasUrl}{Constants.ContentUrl}{fileId}{method}"; } - public static string ImageUrl(Alias alias, int fileId, string size, string mode) + public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode) { var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; - return $"{aliasUrl}{Constants.ImageUrl}{fileId}/{size}/{mode}"; + return $"{aliasUrl}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}"; } public static string TenantUrl(Alias alias, string url)