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)