Merge pull request #1673 from sbwalker/dev

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.
This commit is contained in:
Shaun Walker
2021-09-20 17:09:52 -04:00
committed by GitHub
25 changed files with 457 additions and 187 deletions

View File

@ -27,7 +27,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="Enter the url of the file you wish to download" ResourceKey="Url">Url: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@url" required />
<input id="url" class="form-control" @bind="@_url" required />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -42,6 +42,12 @@
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the name of the file being downloaded" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" />
</div>
</div>
</div>
<button type="button" class="btn btn-success" @onclick="Download">@SharedLocalizer["Download"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@ -53,9 +59,10 @@
@code {
private ElementReference form;
private bool validated = false;
private string url = string.Empty;
private string _url = string.Empty;
private List<Folder> _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);
}
}

View File

@ -81,6 +81,7 @@
</TabPanel>
}
</TabStrip>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />

View File

@ -162,6 +162,7 @@
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
<br />
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>

View File

@ -12,7 +12,7 @@
@if (PageState.User != null && photo != null)
{
<img src="@ImageUrl(photofileid, "400x400", "crop")" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
<img src="@ImageUrl(photofileid, 400, 400, "crop")" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
}
else
{

View File

@ -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<string, string> GetUrlParameters(string parametersTemplate = "")

View File

@ -156,4 +156,10 @@
<data name="UploadFiles.Heading" xml:space="preserve">
<value>Upload Files</value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name of the file being downloaded</value>
</data>
<data name="Name.Text" xml:space="preserve">
<value>Name:</value>
</data>
</root>

View File

@ -309,4 +309,7 @@
<data name="Extend" xml:space="preserve">
<value>Extend</value>
</data>
<data name="Not Specified" xml:space="preserve">
<value>Not Specified</value>
</data>
</root>

View File

@ -1,65 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
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.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
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.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
@ -117,10 +117,28 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Title.HelpText" xml:space="preserve">
<value>Specify If The Page Footer Should Be Displayed</value>
<data name="Footer.HelpText" xml:space="preserve">
<value>Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page.</value>
</data>
<data name="Title.Text" xml:space="preserve">
<data name="Footer.Text" xml:space="preserve">
<value>Display Footer?</value>
</data>
<data name="Login.HelpText" xml:space="preserve">
<value>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.</value>
</data>
<data name="Login.Text" xml:space="preserve">
<value>Show Login?</value>
</data>
<data name="Page" xml:space="preserve">
<value>Page</value>
</data>
<data name="Register.HelpText" xml:space="preserve">
<value>Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.</value>
</data>
<data name="Register.Text" xml:space="preserve">
<value>Show Register?</value>
</data>
<data name="Site" xml:space="preserve">
<value>Site</value>
</data>
</root>

View File

@ -67,9 +67,9 @@ namespace Oqtane.Services
await DeleteAsync($"{Apiurl}/{fileId}");
}
public async Task<File> UploadFileAsync(string url, int folderId)
public async Task<File> UploadFileAsync(string url, int folderId, string name)
{
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}");
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}");
}
public async Task<string> UploadFilesAsync(int folderId, string[] files, string id)

View File

@ -62,8 +62,9 @@ namespace Oqtane.Services
/// </summary>
/// <param name="url"></param>
/// <param name="folderId"></param>
/// <param name="name"></param>
/// <returns></returns>
Task<File> UploadFileAsync(string url, int folderId);
Task<File> UploadFileAsync(string url, int folderId, string name);
/// <summary>
/// Upload one or more files.

View File

@ -176,5 +176,9 @@ namespace Oqtane.Services
/// <param name="defaultValue"></param>
/// <returns></returns>
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue);
}
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic);
Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2);
}
}

View File

@ -109,21 +109,31 @@ namespace Oqtane.Services
foreach (KeyValuePair<string, string> 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<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue)
{
return SetSetting(settings, settingName, settingValue, false);
}
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic)
{
if (settings == null)
{
settings = new Dictionary<string, string>();
}
settingValue = (isPublic) ? "[Public]" + settingValue : settingValue;
if (settings.ContainsKey(settingName))
{
{
settings[settingName] = settingValue;
}
else
@ -178,5 +194,28 @@ namespace Oqtane.Services
}
return settings;
}
public Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2)
{
if (settings1 == null)
{
settings1 = new Dictionary<string, string>();
}
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;
}
}
}

View File

@ -12,7 +12,16 @@
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
</Authorized>
<NotAuthorized>
<button type="button" class="btn btn-primary" @onclick="LoginUser">@SharedLocalizer["Login"]</button>
@if (ShowLogin)
{
<button type="button" class="btn btn-primary" @onclick="LoginUser">@SharedLocalizer["Login"]</button>
}
</NotAuthorized>
</AuthorizeView>
</span>
</span>
@code
{
[Parameter]
public bool ShowLogin { get; set; }
}

View File

@ -13,9 +13,9 @@
<button type="button" class="btn btn-primary" @onclick="UpdateProfile">@context.User.Identity.Name</button>
</Authorized>
<NotAuthorized>
@if (PageState.Site.AllowRegistration)
@if (ShowRegister && PageState.Site.AllowRegistration)
{
<button type="button" class="btn btn-primary" @onclick="RegisterUser">@Localizer["Register"]</button>
<button type="button" class="btn btn-primary" @onclick="RegisterUser">@Localizer["Register"]</button>
}
</NotAuthorized>
</AuthorizeView>
@ -23,6 +23,9 @@
@code {
[Parameter]
public bool ShowRegister { get; set; }
private void RegisterUser()
{
NavigationManager.NavigateTo(NavigateUrl("register"));

View File

@ -6,7 +6,7 @@
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
<Logo /><Menu Orientation="Horizontal" />
<div class="controls ms-auto">
<div class="controls-group"><UserProfile /> <Login /> <ControlPanel /></div>
<div class="controls-group"><UserProfile ShowRegister="@_register" /> <Login ShowLogin="@_login" /> <ControlPanel /></div>
</div>
</nav>
<div class="content">
@ -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
{

View File

@ -2,47 +2,149 @@
@inherits ModuleBase
@implements Oqtane.Interfaces.ISettingsControl
@inject ISettingService SettingService
@inject IStringLocalizer<ThemeSettings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@attribute [OqtaneIgnore]
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="footer" ResourceKey="Footer" HelpText="Specify If A Footer Should Always Be Displayed In A Fixed Location At The Bottom Of The Browser Window">Display Fixed Footer?</Label>
<div class="col-sm-9">
<select id="footer" class="form-select" @bind="@_footer">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="scope" ResourceKey="Scope" HelpText="Specify if the settings are applicable to this page or the entire site.">Setting Scope:</Label>
<div class="col-sm-9">
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<option value="site">@Localizer["Site"]</option>
}
<option value="page">@Localizer["Page"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="login" ResourceKey="Login" HelpText="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?</Label>
<div class="col-sm-9">
<select id="login" class="form-select" @bind="@_login">
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="register" ResourceKey="Register" HelpText="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?</Label>
<div class="col-sm-9">
<select id="register" class="form-select" @bind="@_register">
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="footer" ResourceKey="Footer" HelpText="Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page">Display Fixed Footer?</Label>
<div class="col-sm-9">
<select id="footer" class="form-select" @bind="@_footer">
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
@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<string, string> 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<string, string> 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);
}
}
}

View File

@ -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);
}
}
}