diff --git a/Oqtane.Client/Modules/HtmlText/Index.razor b/Oqtane.Client/Modules/HtmlText/Index.razor index 6679cf83..c3fa8b39 100644 --- a/Oqtane.Client/Modules/HtmlText/Index.razor +++ b/Oqtane.Client/Modules/HtmlText/Index.razor @@ -2,6 +2,7 @@ @namespace Oqtane.Modules.HtmlText @inherits ModuleBase @inject IHtmlTextService HtmlTextService +@inject ISettingService SettingService @inject IStringLocalizer Localizer @if (PageState.EditMode) @@ -36,6 +37,10 @@ { content = htmltext.Content; content = Utilities.FormatContent(content, PageState.Alias, "render"); + if (bool.Parse(SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false"))) + { + content = ReplaceTokens(content); + } } else { diff --git a/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs b/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs index ecd90c8b..59a473c4 100644 --- a/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs +++ b/Oqtane.Client/Modules/HtmlText/ModuleInfo.cs @@ -15,7 +15,7 @@ namespace Oqtane.Modules.HtmlText Version = "1.0.1", ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server", ReleaseVersions = "1.0.0,1.0.1", - SettingsType = string.Empty, + SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client", Resources = new List() { new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" } diff --git a/Oqtane.Client/Modules/HtmlText/Settings.razor b/Oqtane.Client/Modules/HtmlText/Settings.razor new file mode 100644 index 00000000..783e1180 --- /dev/null +++ b/Oqtane.Client/Modules/HtmlText/Settings.razor @@ -0,0 +1,55 @@ +@namespace Oqtane.Modules.HtmlText +@inherits ModuleBase +@inject ISettingService SettingService +@implements Oqtane.Interfaces.ISettingsControl +@inject IStringLocalizer Localizer +@inject IStringLocalizer SharedLocalizer + +
+
+
+ +
+ +
+
+
+
+ +@code { + private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization + + private ElementReference form; + private bool validated = false; + + private string _dynamictokens; + + protected override void OnInitialized() + { + try + { + _dynamictokens = SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false"); + } + catch (Exception ex) + { + AddModuleMessage(ex.Message, MessageType.Error); + } + } + + public async Task UpdateSettings() + { + try + { + var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId); + settings = SettingService.SetSetting(settings, "DynamicTokens", _dynamictokens); + await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId); + } + catch (Exception ex) + { + AddModuleMessage(ex.Message, MessageType.Error); + } + } +} diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index fe40ddf6..b97b229b 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using Microsoft.JSInterop; using System.Linq; using System.Dynamic; +using System.Reflection; namespace Oqtane.Modules { @@ -35,7 +36,7 @@ namespace Oqtane.Modules protected PageState PageState { get; set; } [CascadingParameter] - protected Module ModuleState { get; set; } + protected Models.Module ModuleState { get; set; } [Parameter] public RenderModeBoundary RenderModeBoundary { get; set; } @@ -413,6 +414,79 @@ namespace Oqtane.Modules await interop.ScrollTo(0, 0, "smooth"); } + public string ReplaceTokens(string content) + { + return ReplaceTokens(content, null); + } + + public string ReplaceTokens(string content, object obj) + { + var tokens = new List(); + var pos = content.IndexOf("["); + if (pos != -1) + { + if (content.IndexOf("]", pos) != -1) + { + var token = content.Substring(pos, content.IndexOf("]", pos) - pos + 1); + if (token.Contains(":")) + { + tokens.Add(token.Substring(1, token.Length - 2)); + } + } + pos = content.IndexOf("[", pos + 1); + } + if (tokens.Count != 0) + { + foreach (string token in tokens) + { + var segments = token.Split(":"); + if (segments.Length >= 2 && segments.Length <= 3) + { + var objectName = string.Join(":", segments, 0, segments.Length - 1); + var propertyName = segments[segments.Length - 1]; + var propertyValue = ""; + + switch (objectName) + { + case "ModuleState": + propertyValue = ModuleState.GetType().GetProperty(propertyName)?.GetValue(ModuleState, null).ToString(); + break; + case "PageState": + propertyValue = PageState.GetType().GetProperty(propertyName)?.GetValue(PageState, null).ToString(); + break; + case "PageState:Alias": + propertyValue = PageState.Alias.GetType().GetProperty(propertyName)?.GetValue(PageState.Alias, null).ToString(); + break; + case "PageState:Site": + propertyValue = PageState.Site.GetType().GetProperty(propertyName)?.GetValue(PageState.Site, null).ToString(); + break; + case "PageState:Page": + propertyValue = PageState.Page.GetType().GetProperty(propertyName)?.GetValue(PageState.Page, null).ToString(); + break; + case "PageState:User": + propertyValue = PageState.User?.GetType().GetProperty(propertyName)?.GetValue(PageState.User, null).ToString(); + break; + case "PageState:Route": + propertyValue = PageState.Route.GetType().GetProperty(propertyName)?.GetValue(PageState.Route, null).ToString(); + break; + default: + if (obj != null && obj.GetType().Name == objectName) + { + propertyValue = obj.GetType().GetProperty(propertyName)?.GetValue(obj, null).ToString(); + } + break; + } + if (propertyValue != null) + { + content = content.Replace("[" + token + "]", propertyValue); + } + + } + } + } + return content; + } + // logging methods public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args) { diff --git a/Oqtane.Client/Resources/Modules/HtmlText/Settings.resx b/Oqtane.Client/Resources/Modules/HtmlText/Settings.resx new file mode 100644 index 00000000..68a539c0 --- /dev/null +++ b/Oqtane.Client/Resources/Modules/HtmlText/Settings.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site. + + + Dynamic Tokens? + + \ No newline at end of file diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/AdminSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/AdminSiteTemplate.cs index 6ac4d9b3..7727e9aa 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/AdminSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/AdminSiteTemplate.cs @@ -199,6 +199,9 @@ namespace Oqtane.Infrastructure.SiteTemplates new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }, + Settings = new List { + new Setting { SettingName = "DynamicTokens", SettingValue = "true" } + }, Content = _localizer["Privacy"] } } @@ -226,6 +229,9 @@ namespace Oqtane.Infrastructure.SiteTemplates new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }, + Settings = new List { + new Setting { SettingName = "DynamicTokens", SettingValue = "true" } + }, Content = _localizer["Terms"] } } diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs index e2e46ff8..409ab59c 100644 --- a/Oqtane.Server/Infrastructure/UpgradeManager.cs +++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs @@ -490,6 +490,9 @@ namespace Oqtane.Infrastructure new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }, + Settings = new List { + new Setting { SettingName = "DynamicTokens", SettingValue = "true" } + }, Content = localizer["Privacy"] } } @@ -516,6 +519,9 @@ namespace Oqtane.Infrastructure new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }, + Settings = new List { + new Setting { SettingName = "DynamicTokens", SettingValue = "true" } + }, Content = localizer["Terms"] } } diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs index 36495c8f..10de3576 100644 --- a/Oqtane.Server/Repository/SiteRepository.cs +++ b/Oqtane.Server/Repository/SiteRepository.cs @@ -9,6 +9,7 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Modules; +using Oqtane.Modules.Admin.Modules; using Oqtane.Shared; using Module = Oqtane.Models.Module; @@ -25,6 +26,7 @@ namespace Oqtane.Repository private readonly IPageModuleRepository _pageModuleRepository; private readonly IModuleDefinitionRepository _moduleDefinitionRepository; private readonly IThemeRepository _themeRepository; + private readonly ISettingRepository _settingRepository; private readonly IServiceProvider _serviceProvider; private readonly IConfigurationRoot _config; private readonly IServerStateManager _serverState; @@ -32,8 +34,8 @@ namespace Oqtane.Repository private static readonly object _lock = new object(); public SiteRepository(IDbContextFactory factory, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository, - IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider, - IConfigurationRoot config, IServerStateManager serverState, ILogManager logger) + IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, ISettingRepository settingRepository, + IServiceProvider serviceProvider, IConfigurationRoot config, IServerStateManager serverState, ILogManager logger) { _factory = factory; _roleRepository = roleRepository; @@ -44,6 +46,7 @@ namespace Oqtane.Repository _pageModuleRepository = pageModuleRepository; _moduleDefinitionRepository = moduleDefinitionRepository; _themeRepository = themeRepository; + _settingRepository = settingRepository; _serviceProvider = serviceProvider; _config = config; _serverState = serverState; @@ -391,6 +394,7 @@ namespace Oqtane.Repository { _logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Updated {Page}", page); } + UpdateSettings(EntityNames.Page, page.PageId, pageTemplate.Settings); } } else @@ -401,6 +405,7 @@ namespace Oqtane.Repository { _logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Added {Page}", page); } + UpdateSettings(EntityNames.Page, page.PageId, pageTemplate.Settings); } } catch (Exception ex) @@ -457,6 +462,7 @@ namespace Oqtane.Repository { _logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Module Updated {PageModule}", pageModule); } + UpdateSettings(EntityNames.Module, pageModule.Module.ModuleId, pageTemplateModule.Settings); } else { @@ -475,6 +481,7 @@ namespace Oqtane.Repository { _logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Module Added {PageModule}", pageModule); } + UpdateSettings(EntityNames.Module, pageModule.Module.ModuleId, pageTemplateModule.Settings); } } @@ -522,5 +529,25 @@ namespace Oqtane.Repository } } } + + private void UpdateSettings(string entityName, int entityId, List templateSettings) + { + foreach (var templateSetting in templateSettings) + { + var setting = _settingRepository.GetSetting(entityName, entityId, templateSetting.SettingName); + if (setting == null) + { + templateSetting.EntityName = entityName; + templateSetting.EntityId = entityId; + _settingRepository.AddSetting(templateSetting); + } + else + { + setting.SettingValue = templateSetting.SettingValue; + setting.IsPrivate = templateSetting.IsPrivate; + _settingRepository.UpdateSetting(setting); + } + } + } } } diff --git a/Oqtane.Server/Resources/Infrastructure/SiteTemplates/AdminSiteTemplate.resx b/Oqtane.Server/Resources/Infrastructure/SiteTemplates/AdminSiteTemplate.resx index 07a8937e..c13f4563 100644 --- a/Oqtane.Server/Resources/Infrastructure/SiteTemplates/AdminSiteTemplate.resx +++ b/Oqtane.Server/Resources/Infrastructure/SiteTemplates/AdminSiteTemplate.resx @@ -118,9 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - <p>This privacy policy ("policy") will help you understand how [COMPANY] ("us", "we", "our") uses and protects the data you provide to us when you visit and use this website.</p> + <p>This privacy policy ("policy") will help you understand how [PageState:Site:Name] ("us", "we", "our") uses and protects the data you provide to us when you visit and use this website.</p> -<p>We reserve the right to change this policy at any time. We shall let our users know of these changes through electronic mail. If you want to make sure that you are up to date with the latest changes, we advise you to frequently visit this page.</p> +<p>We reserve the right to change this policy at any time. If you want to make sure that you are up to date with the latest changes, we advise you to frequently visit this page.</p> <h2>What User Data We Collect</h2> @@ -132,7 +132,7 @@ <h2>Safeguarding and Securing the Data</h2> -<p>[COMPANY] is committed to securing your data and keeping it confidential. [COMPANY] has done everything in its power to prevent data theft, unauthorized access, and disclosure by implementing the latest technologies and software, which help us safeguard all the information we collect online.</p> +<p>[PageState:Site:Name] is committed to securing your data and keeping it confidential. [PageState:Site:Name] has done everything in its power to prevent data theft, unauthorized access, and disclosure by implementing the latest technologies and software, which help us safeguard all the information we collect online.</p> <h2>Our Cookie Policy</h2> @@ -146,20 +146,20 @@ <h2>Links to Other Websites</h2> -<p>Our website contains links that lead to other websites. If you click on these links [COMPANY] is not held responsible for your data and privacy protection. Visiting those websites is not governed by this privacy policy agreement. Make sure to read the privacy policy documentation of the website you go to from our website.</p> +<p>Our website contains links that lead to other websites. If you click on these links [PageState:Site:Name] is not held responsible for your data and privacy protection. Visiting those websites is not governed by this privacy policy agreement. Make sure to read the privacy policy documentation of any website you navigate to from our website.</p> <h2>Restricting the Collection of your Personal Data</h2> <p>At some point, you might wish to restrict the use and collection of your personal data. If you previously agreed to share your information with us, feel free to contact us via email and we will be more than happy to change this for you.</p> -<p>[COMPANY] will not lease, sell or distribute your personal information to any third parties, unless we have your permission. Your personal information will only be used when we need to send you promotional materials if you agree to this privacy policy.</p> +<p>[PageState:Site:Name] will not lease, sell or distribute your personal information to any third parties, unless we have your permission. Your personal information will only be used when we need to send you promotional materials if you agree to this privacy policy.</p> - <p>Please read these terms and conditions carefully before using this website operated by [COMPANY] ("us", "we", "our").</p> + <p>Please read these terms and conditions carefully before using this website operated by [PageState:Site:Name] ("us", "we", "our").</p> <h2>Conditions of Use</h2> -<p>By using this website, you certify that you have read and reviewed this Agreement and that you agree to comply with its terms. If you do not want to be bound by the terms of this Agreement, you are advised to stop using the website accordingly. [COMPANY] only grants use and access of this website, its products, and its services to those who have accepted its terms.</p> +<p>By using this website, you certify that you have read and reviewed this Agreement and that you agree to comply with its terms. If you do not want to be bound by the terms of this Agreement, you are advised to stop using the website accordingly. [PageState:Site:Name] only grants use and access of this website, its products, and its services to those who have accepted its terms.</p> <h2>Privacy Policy</h2> @@ -167,9 +167,9 @@ <h2>Intellectual Property</h2> -<p>You agree that all materials, products, and services provided on this website are the property of [COMPANY], its affiliates, directors, officers, employees, agents, suppliers, or licensors including all copyrights, trade secrets, trademarks, patents, and other intellectual property. You also agree that you will not reproduce or redistribute the [COMPANY]’s intellectual property in any way, including electronic, digital, or new trademark registrations.</p> +<p>You agree that all materials, products, and services provided on this website are the property of [PageState:Site:Name], its affiliates, directors, officers, employees, agents, suppliers, or licensors including all copyrights, trade secrets, trademarks, patents, and other intellectual property. You also agree that you will not reproduce or redistribute the [PageState:Site:Name]’s intellectual property in any way, including electronic, digital, or new trademark registrations.</p> -<p>You grant [COMPANY] a royalty-free and non-exclusive license to display, use, copy, transmit, and broadcast the content you upload and publish. For issues regarding intellectual property claims, you should contact us in order to come to an agreement.</p> +<p>You grant [PageState:Site:Name] a royalty-free and non-exclusive license to display, use, copy, transmit, and broadcast the content you upload and publish. For issues regarding intellectual property claims, you should contact us in order to come to an agreement.</p> <h2>User Accounts</h2> @@ -181,19 +181,19 @@ <h2>Applicable Law</h2> -<p>By using this website, you agree that the laws of [LOCATION], without regard to principles of conflict laws, will govern these terms and conditions, or any dispute of any sort that might come between [COMPANY] and you, or its business partners and associates.</p> +<p>By using this website, you agree that the laws of the jurisdiction associated to [PageState:Site:Name], without regard to principles of conflict laws, will govern these terms and conditions, or any dispute of any sort that might come between [PageState:Site:Name] and you, or its business partners and associates.</p> <h2>Disputes</h2> -<p>Any dispute related in any way to your use of this website or to products you purchase from us shall be arbitrated by state or federal court [your location] and you consent to exclusive jurisdiction and venue of such courts.</p> +<p>Any dispute related in any way to your use of this website or to products you purchase from us shall be arbitrated by a court of law and you consent to exclusive jurisdiction and venue of such courts.</p> <h2>Indemnification</h2> -<p>You agree to indemnify [COMPANY] and its affiliates and hold [COMPANY] harmless against legal claims and demands that may arise from your use or misuse of our services. We reserve the right to select our own legal counsel.</p> +<p>You agree to indemnify [PageState:Site:Name] and its affiliates and hold [PageState:Site:Name] harmless against legal claims and demands that may arise from your use or misuse of our services. We reserve the right to select our own legal counsel.</p> <h2>Limitation on Liability</h2> -<p>[COMPANY] is not liable for any damages that may occur to you as a result of your misuse of our website. [COMPANY] reserves the right to edit, modify, and change this Agreement at any time. We shall let our users know of these changes through electronic mail. This Agreement is an understanding between [COMPANY] and the user, and this supersedes and replaces all prior agreements regarding the use of this website.</p> +<p>[PageState:Site:Name] is not liable for any damages that may occur to you as a result of your misuse of our website. [PageState:Site:Name] reserves the right to edit, modify, and change this Agreement at any time. We shall let our users know of these changes through electronic mail. This Agreement is an understanding between [PageState:Site:Name] and the user, and this supersedes and replaces all prior agreements regarding the use of this website.</p> \ No newline at end of file diff --git a/Oqtane.Shared/Models/SiteTemplate.cs b/Oqtane.Shared/Models/SiteTemplate.cs index 5e449a9b..348e1834 100644 --- a/Oqtane.Shared/Models/SiteTemplate.cs +++ b/Oqtane.Shared/Models/SiteTemplate.cs @@ -35,6 +35,7 @@ namespace Oqtane.Models new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }; + Settings = new List(); PageTemplateModules = new List(); // properties used by IModule @@ -60,6 +61,7 @@ namespace Oqtane.Models public bool IsPersonalizable { get; set; } public bool IsDeleted { get; set; } public List PermissionList { get; set; } + public List Settings { get; set; } public List PageTemplateModules { get; set; } // properties used by IModule @@ -99,6 +101,7 @@ namespace Oqtane.Models new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }; + Settings = new List(); Content = ""; } @@ -109,6 +112,7 @@ namespace Oqtane.Models public string ContainerType { get; set; } public bool IsDeleted { get; set; } public List PermissionList { get; set; } + public List Settings { get; set; } public string Content { get; set; } [Obsolete("The ModulePermissions property is deprecated. Use PermissionList instead", false)]