Fix #4358: RichTextEditor Provider Abstraction.
This commit is contained in:
parent
1eafed755d
commit
e00c261777
|
@ -1,6 +1,7 @@
|
|||
@namespace Oqtane.Modules.Admin.Site
|
||||
@inherits ModuleBase
|
||||
@using System.Text.RegularExpressions
|
||||
@using Microsoft.Extensions.DependencyInjection
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISiteService SiteService
|
||||
@inject ITenantService TenantService
|
||||
|
@ -8,6 +9,7 @@
|
|||
@inject IAliasService AliasService
|
||||
@inject IThemeService ThemeService
|
||||
@inject ISettingService SettingService
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject INotificationService NotificationService
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
@ -123,6 +125,28 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="textEditorProvider" HelpText="Select the text editor provider for the site" ResourceKey="TextEditorProvider">Text Editor Provider: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="textEditorProvider" class="form-select" value="@_textEditorProvider" required @onchange="TextEditorProviderChanged">
|
||||
@if (_textEditorProviders != null)
|
||||
{
|
||||
@foreach (var provider in _textEditorProviders)
|
||||
{
|
||||
<option value="@provider.EditorType">@provider.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_textEditorProviderSettings != null)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-9 offset-sm-3">
|
||||
@_textEditorProviderSettings
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="FileExtensions" Heading="File Extensions" ResourceKey="FileExtensions">
|
||||
|
@ -438,6 +462,10 @@
|
|||
private string _tenant = string.Empty;
|
||||
private string _database = string.Empty;
|
||||
private string _connectionstring = string.Empty;
|
||||
private string _textEditorProvider = "";
|
||||
private IEnumerable<ITextEditorProvider> _textEditorProviders;
|
||||
private RenderFragment _textEditorProviderSettings;
|
||||
private ISettingsControl _textEditorProviderSettingsControl;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
|
@ -479,6 +507,7 @@
|
|||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||
_textEditorProviders = ServiceProvider.GetServices<ITextEditorProvider>();
|
||||
|
||||
// page content
|
||||
_headcontent = site.HeadContent;
|
||||
|
@ -517,6 +546,10 @@
|
|||
// aliases
|
||||
await GetAliases();
|
||||
|
||||
//text editor
|
||||
_textEditorProvider = SettingService.GetSetting(settings, "TextEditorProvider", Constants.DefaultTextEditorProvider);
|
||||
LoadTextEditorProviderSettingsControl();
|
||||
|
||||
// hosting model
|
||||
_rendermode = site.RenderMode;
|
||||
_runtime = site.Runtime;
|
||||
|
@ -673,17 +706,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
site = await SiteService.UpdateSiteAsync(site);
|
||||
site = await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
// SMTP
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
|
||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||
|
@ -692,141 +725,149 @@
|
|||
settings = SettingService.SetSetting(settings, "ImageFiles", (_ImageFiles != Constants.ImageFiles) ? _ImageFiles.Replace(" ", "") : "", false);
|
||||
settings = SettingService.SetSetting(settings, "UploadableFiles", (_UploadableFiles != Constants.UploadableFiles) ? _UploadableFiles.Replace(" ", "") : "", false);
|
||||
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
//text editor
|
||||
settings = SettingService.SetSetting(settings, "TextEditorProvider", _textEditorProvider);
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl(), true); // reload
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
private async Task DeleteSite()
|
||||
{
|
||||
try
|
||||
{
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||
{
|
||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||
if(_textEditorProviderSettingsControl != null)
|
||||
{
|
||||
await _textEditorProviderSettingsControl.UpdateSettings();
|
||||
}
|
||||
|
||||
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
NavigationManager.NavigateTo(NavigateUrl(), true); // reload
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async Task DeleteSite()
|
||||
{
|
||||
try
|
||||
{
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
|
||||
{
|
||||
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
|
||||
|
||||
private async Task SendEmail()
|
||||
{
|
||||
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site SMTP Settings Saved");
|
||||
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
|
||||
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
||||
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
||||
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendEmail()
|
||||
{
|
||||
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site SMTP Settings Saved");
|
||||
|
||||
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
|
||||
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Testing SMTP Configuration");
|
||||
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.Smtp"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Testing SMTP Configuration");
|
||||
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.Smtp"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleSMTPPassword()
|
||||
{
|
||||
if (_smtppasswordtype == "password")
|
||||
{
|
||||
_smtppasswordtype = "text";
|
||||
_togglesmtppassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_smtppasswordtype = "password";
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
private void ToggleSMTPPassword()
|
||||
{
|
||||
if (_smtppasswordtype == "password")
|
||||
{
|
||||
_smtppasswordtype = "text";
|
||||
_togglesmtppassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_smtppasswordtype = "password";
|
||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetAliases()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
}
|
||||
}
|
||||
private async Task GetAliases()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAlias()
|
||||
{
|
||||
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||
_aliasid = 0;
|
||||
_aliasname = "";
|
||||
_defaultalias = "False";
|
||||
StateHasChanged();
|
||||
}
|
||||
private void AddAlias()
|
||||
{
|
||||
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
|
||||
_aliasid = 0;
|
||||
_aliasname = "";
|
||||
_defaultalias = "False";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void EditAlias(Alias alias)
|
||||
{
|
||||
_aliasid = alias.AliasId;
|
||||
_aliasname = alias.Name;
|
||||
_defaultalias = alias.IsDefault.ToString();
|
||||
StateHasChanged();
|
||||
}
|
||||
private void EditAlias(Alias alias)
|
||||
{
|
||||
_aliasid = alias.AliasId;
|
||||
_aliasname = alias.Name;
|
||||
_defaultalias = alias.IsDefault.ToString();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeleteAlias(Alias alias)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
await GetAliases();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
private async Task DeleteAlias(Alias alias)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
await GetAliases();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveAlias()
|
||||
{
|
||||
|
@ -878,11 +919,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
private async Task CancelAlias()
|
||||
{
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
private async Task CancelAlias()
|
||||
{
|
||||
await GetAliases();
|
||||
_aliasid = -1;
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void TextEditorProviderChanged(ChangeEventArgs e)
|
||||
{
|
||||
_textEditorProvider = e.Value.ToString();
|
||||
LoadTextEditorProviderSettingsControl();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void LoadTextEditorProviderSettingsControl()
|
||||
{
|
||||
var provider = _textEditorProviders.FirstOrDefault(i => i.EditorType == _textEditorProvider);
|
||||
var settingsType = provider != null && !string.IsNullOrEmpty(provider.SettingsType) ? Type.GetType(provider.SettingsType) : null;
|
||||
if (settingsType != null)
|
||||
{
|
||||
_textEditorProviderSettings = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, settingsType);
|
||||
builder.AddComponentReferenceCapture(1, (c) =>
|
||||
{
|
||||
_textEditorProviderSettingsControl = (ISettingsControl)c;
|
||||
});
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_textEditorProviderSettings = null;
|
||||
_textEditorProviderSettingsControl = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@implements ITextEditor
|
||||
@inject IStringLocalizer<QuillTextEditor> Localizer
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<QuillJSTextEditor> Localizer
|
||||
|
||||
<div class="quill-text-editor">
|
||||
<TabStrip ActiveTab="@_activetab">
|
||||
@if (AllowRichText)
|
||||
@if (_allowRichText)
|
||||
{
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||
@if (_richfilemanager)
|
||||
|
@ -15,7 +16,7 @@
|
|||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowFileManagement)
|
||||
@if (_allowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
|
@ -28,9 +29,9 @@
|
|||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
@if (!string.IsNullOrEmpty(_toolbarContent))
|
||||
{
|
||||
@ToolbarContent
|
||||
@((MarkupString)_toolbarContent)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -66,7 +67,7 @@
|
|||
</div>
|
||||
</TabPanel>
|
||||
}
|
||||
@if (AllowRawHtml)
|
||||
@if (_allowRawHtml)
|
||||
{
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||
@if (_rawfilemanager)
|
||||
|
@ -76,7 +77,7 @@
|
|||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowFileManagement)
|
||||
@if (_allowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
|
@ -106,6 +107,14 @@
|
|||
private FileManager _fileManager;
|
||||
private string _activetab = "Rich";
|
||||
|
||||
private bool _allowFileManagement = false;
|
||||
private bool _allowRawHtml = false;
|
||||
private bool _allowRichText = false;
|
||||
private string _theme = "snow";
|
||||
private string _debugLevel = "info";
|
||||
private string _toolbarContent = string.Empty;
|
||||
private bool _settingsLoaded;
|
||||
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _richfilemanager = false;
|
||||
|
@ -121,38 +130,22 @@
|
|||
private bool _contentchanged = false;
|
||||
private int _editorIndex;
|
||||
|
||||
[Parameter]
|
||||
public bool AllowFileManagement{ get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool AllowRichText { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool AllowRawHtml { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
|
||||
public override List<Resource> Resources { get; set; } = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js", Location = ResourceLocation.Body },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js", Location = ResourceLocation.Body },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js", Location = ResourceLocation.Body }
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js", Location = ResourceLocation.Body },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||
};
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
interop = new QuillEditorInterop(JSRuntime);
|
||||
|
||||
|
@ -160,11 +153,13 @@
|
|||
{
|
||||
Placeholder = Localizer["Placeholder"];
|
||||
}
|
||||
|
||||
await LoadSettings();
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!AllowRichText)
|
||||
if (!_allowRichText)
|
||||
{
|
||||
_activetab = "Raw";
|
||||
}
|
||||
|
@ -174,17 +169,17 @@
|
|||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
if (AllowRichText)
|
||||
if (_allowRichText)
|
||||
{
|
||||
if (firstRender)
|
||||
if (_settingsLoaded && !_initialized)
|
||||
{
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
_theme,
|
||||
_debugLevel);
|
||||
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
|
||||
|
@ -202,6 +197,8 @@
|
|||
// reload editor if Content passed to component has changed
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||
|
||||
_contentchanged = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -215,8 +212,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_contentchanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,10 +219,14 @@
|
|||
{
|
||||
_richhtml = content;
|
||||
_rawhtml = content;
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
_originalrichhtml = "";
|
||||
_richhtml = content;
|
||||
_contentchanged = content != _originalrawhtml;
|
||||
if (!_contentchanged)
|
||||
{
|
||||
_contentchanged = content != _originalrawhtml;
|
||||
}
|
||||
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
@ -243,7 +242,7 @@
|
|||
{
|
||||
var richhtml = "";
|
||||
|
||||
if (AllowRichText)
|
||||
if (_allowRichText)
|
||||
{
|
||||
richhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
|
@ -290,7 +289,7 @@
|
|||
{
|
||||
var richhtml = "";
|
||||
|
||||
if (AllowRichText)
|
||||
if (_allowRichText)
|
||||
{
|
||||
richhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
|
@ -362,4 +361,24 @@
|
|||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_allowFileManagement = SettingService.GetSetting(settings, "QuillTextEditor_AllowFileManagement", "true") == "true";
|
||||
_allowRawHtml = SettingService.GetSetting(settings, "QuillTextEditor_AllowRawHtml", "true") == "true";
|
||||
_allowRichText = SettingService.GetSetting(settings, "QuillTextEditor_AllowRichText", "true") == "true";
|
||||
_theme = SettingService.GetSetting(settings, "QuillTextEditor_Theme", "snow");
|
||||
_debugLevel = SettingService.GetSetting(settings, "QuillTextEditor_DebugLevel", "info");
|
||||
_toolbarContent = SettingService.GetSetting(settings, "QuillTextEditor_ToolbarContent", string.Empty);
|
||||
|
||||
_settingsLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
@implements Oqtane.Interfaces.ISettingsControl
|
||||
@inject IStringLocalizer<QuillJSTextEditorSettings> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="AllowFileManagement" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="checkbox" id="AllowFileManagement" class="form-check-input" @bind="_allowFileManagement" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="AllowRawHtml" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify If Editors Can Enter Raw HTML">Allow Raw HTML: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="checkbox" id="AllowRawHtml" class="form-check-input" @bind="_allowRawHtml" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="AllowRichText" ResourceKey="AllowRichText" ResourceType="@resourceType" HelpText="Specify If Editors Can Use Rich Text Editor">Allow Rich Text: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="checkbox" id="AllowRichText" class="form-check-input" @bind="_allowRichText" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="Theme" ResourceKey="Theme" ResourceType="@resourceType" HelpText="Specify the Rich Text Editor's Theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" id="Theme" class="form-control" @bind="_theme" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="DebugLevel" ResourceKey="DebugLevel" ResourceType="@resourceType" HelpText="Specify the Debug Level">Debug Level: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="DebugLevel" class="form-select" @bind="_debugLevel">
|
||||
@foreach (var level in _debugLevels)
|
||||
{
|
||||
<option value="@level">@level</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="ToolbarContent" ResourceKey="ToolbarContent" ResourceType="@resourceType" HelpText="Specify the Toolbar Content">Toolbar Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="ToolbarContent" class="form-control" @bind="_toolbarContent" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Modules.Controls.QuillJSTextEditorSettings, Oqtane.Client";
|
||||
private bool _allowFileManagement;
|
||||
private bool _allowRawHtml;
|
||||
private bool _allowRichText;
|
||||
private string _theme;
|
||||
private string _debugLevel;
|
||||
private string _toolbarContent;
|
||||
|
||||
private List<string> _debugLevels = new List<string> { "info", "log", "warn", "error" };
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_allowFileManagement = SettingService.GetSetting(settings, "QuillTextEditor_AllowFileManagement", "true") == "true";
|
||||
_allowRawHtml = SettingService.GetSetting(settings, "QuillTextEditor_AllowRawHtml", "true") == "true";
|
||||
_allowRichText = SettingService.GetSetting(settings, "QuillTextEditor_AllowRichText", "true") == "true";
|
||||
_theme = SettingService.GetSetting(settings, "QuillTextEditor_Theme", "snow");
|
||||
_debugLevel = SettingService.GetSetting(settings, "QuillTextEditor_DebugLevel", "info");
|
||||
_toolbarContent = SettingService.GetSetting(settings, "QuillTextEditor_ToolbarContent", string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowFileManagement", _allowFileManagement.ToString().ToLower());
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRawHtml", _allowRawHtml.ToString().ToLower());
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRichText", _allowRichText.ToString().ToLower());
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_Theme", _theme);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_DebugLevel", _debugLevel);
|
||||
settings = SettingService.SetSetting(settings, "QuillTextEditor_ToolbarContent", _toolbarContent);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<RichTextEditor> Localizer
|
||||
|
||||
<div class="row" style="margin-bottom: 50px;">
|
||||
|
@ -14,7 +13,7 @@
|
|||
</div>
|
||||
|
||||
@code {
|
||||
private ITextEditorProvider _textEditorProvider;
|
||||
private string _textEditorProvider;
|
||||
private RenderFragment _textEditorComponent;
|
||||
private ITextEditor _textEditor;
|
||||
|
||||
|
@ -28,29 +27,14 @@
|
|||
public string Placeholder { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool AllowFileManagement { get; set; } = true;
|
||||
public string Provider { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool AllowRichText { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool AllowRawHtml { get; set; } = true;
|
||||
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; }
|
||||
|
||||
public override List<Resource> Resources { get; set; } = new List<Resource>();
|
||||
[Parameter(CaptureUnmatchedValues = true)]
|
||||
public Dictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_textEditorProvider = await GetTextEditorProvider();
|
||||
_textEditorProvider = await GetTextEditorType(ServiceProvider, PageState.Site.SiteId);
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
|
@ -71,6 +55,22 @@
|
|||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
public override async Task<List<Resource>> GetResources(IServiceProvider serviceProvider, Page page)
|
||||
{
|
||||
var type = await GetTextEditorType(serviceProvider, page.SiteId);
|
||||
if (!string.IsNullOrEmpty(type))
|
||||
{
|
||||
var editorType = Type.GetType(type);
|
||||
if (editorType != null && editorType.GetInterfaces().Contains(typeof(IModuleControl)))
|
||||
{
|
||||
var control = Activator.CreateInstance(editorType) as IModuleControl;
|
||||
return await control?.GetResources(serviceProvider, page);
|
||||
}
|
||||
}
|
||||
|
||||
return await base.GetResources(serviceProvider, page);
|
||||
}
|
||||
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
return await _textEditor.GetContent();
|
||||
|
@ -78,37 +78,32 @@
|
|||
|
||||
private void CreateTextEditor(RenderTreeBuilder builder)
|
||||
{
|
||||
if(_textEditorProvider != null)
|
||||
if(!string.IsNullOrEmpty(_textEditorProvider))
|
||||
{
|
||||
var editorType = Type.GetType(_textEditorProvider.EditorType);
|
||||
var editorType = Type.GetType(_textEditorProvider);
|
||||
if (editorType != null)
|
||||
{
|
||||
builder.OpenComponent(0, editorType);
|
||||
|
||||
//set editor parameters if available.
|
||||
var attributes = new Dictionary<string, object>
|
||||
{
|
||||
{ "AllowFileManagement", AllowFileManagement },
|
||||
{ "AllowRichText", AllowRichText },
|
||||
{ "AllowRawHtml", AllowRawHtml },
|
||||
{ "Placeholder", Placeholder },
|
||||
{ "ReadOnly", ReadOnly }
|
||||
};
|
||||
|
||||
if(!string.IsNullOrEmpty(Theme))
|
||||
if (AdditionalAttributes != null)
|
||||
{
|
||||
attributes.Add("Theme", Theme);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(DebugLevel))
|
||||
{
|
||||
attributes.Add("DebugLevel", DebugLevel);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Placeholder))
|
||||
{
|
||||
attributes.Add("Placeholder", Placeholder);
|
||||
}
|
||||
if(ToolbarContent != null)
|
||||
{
|
||||
attributes.Add("ToolbarContent", ToolbarContent);
|
||||
foreach(var key in AdditionalAttributes.Keys)
|
||||
{
|
||||
if(!attributes.ContainsKey(key))
|
||||
{
|
||||
attributes.Add(key, AdditionalAttributes[key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
attributes[key] = AdditionalAttributes[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var index = 1;
|
||||
|
@ -129,26 +124,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<ITextEditorProvider> GetTextEditorProvider()
|
||||
private async Task<string> GetTextEditorType(IServiceProvider serviceProvider, int siteId)
|
||||
{
|
||||
const string DefaultEditorName = "Quill";
|
||||
const string EditorSettingName = "TextEditorProvider";
|
||||
|
||||
var editorName = await GetTextEditorName(DefaultEditorName);
|
||||
var editorProviders = ServiceProvider.GetServices<ITextEditorProvider>();
|
||||
var editorProvider = editorProviders.FirstOrDefault(i => i.Name == editorName);
|
||||
if(editorProvider == null)
|
||||
if(!string.IsNullOrEmpty(Provider))
|
||||
{
|
||||
editorProvider = editorProviders.FirstOrDefault(i => i.Name == DefaultEditorName);
|
||||
var provider = serviceProvider.GetServices<ITextEditorProvider>().FirstOrDefault(i => i.Name.Equals(Provider, StringComparison.OrdinalIgnoreCase));
|
||||
if(provider != null)
|
||||
{
|
||||
return provider.EditorType;
|
||||
}
|
||||
}
|
||||
|
||||
return editorProvider;
|
||||
}
|
||||
|
||||
private async Task<string> GetTextEditorName(string defaultName)
|
||||
{
|
||||
const string EditorSettingName = "TextEditor";
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
return SettingService.GetSetting(settings, EditorSettingName, defaultName);
|
||||
var settingService = serviceProvider.GetService<ISettingService>();
|
||||
var settings = await settingService.GetSiteSettingsAsync(siteId);
|
||||
return settingService.GetSetting(settings, EditorSettingName, Constants.DefaultTextEditorProvider);
|
||||
}
|
||||
}
|
||||
|
|
31
Oqtane.Client/Modules/Controls/TextAreaTextEditor.razor
Normal file
31
Oqtane.Client/Modules/Controls/TextAreaTextEditor.razor
Normal file
|
@ -0,0 +1,31 @@
|
|||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@implements ITextEditor
|
||||
|
||||
<div class="text-area-editor">
|
||||
<textarea @bind="_content" @ref="_editor" placeholder="@Placeholder" readonly="@ReadOnly" />
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private ElementReference _editor;
|
||||
private string _content;
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; }
|
||||
|
||||
public void Initialize(string content)
|
||||
{
|
||||
_content = content;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetContent()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
return _content;
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||
@if (_content != null)
|
||||
{
|
||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<RichTextEditor Content="@_content" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
@ -47,44 +47,36 @@
|
|||
</TabStrip>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
public override string Title => "Edit Html/Text";
|
||||
public override string Title => "Edit Html/Text";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
|
||||
};
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private List<Models.HtmlText> _htmltexts;
|
||||
private string _view = "";
|
||||
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private bool _allowfilemanagement;
|
||||
private bool _allowrawhtml;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private List<Models.HtmlText> _htmltexts;
|
||||
private string _view = "";
|
||||
public override List<string> ResourcesRegistrationTypes => new List<string> { typeof(RichTextEditor).FullName };
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||
_allowrawhtml = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true"));
|
||||
await LoadContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadContent()
|
||||
{
|
||||
private async Task LoadContent()
|
||||
{
|
||||
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
|
||||
if (htmltext != null)
|
||||
{
|
||||
|
|
|
@ -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 = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
|
||||
SettingsType = string.Empty,
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
@implements Oqtane.Interfaces.ISettingsControl
|
||||
@inject IStringLocalizer<Settings> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="files" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="files" class="form-select" @bind="@_allowfilemanagement">
|
||||
<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="files" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify If Editors Can Enter Raw HTML">Allow Raw HTML: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="files" class="form-select" @bind="@_allowrawhtml">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||
private string _allowfilemanagement;
|
||||
private string _allowrawhtml;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
try
|
||||
{
|
||||
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
||||
_allowrawhtml = SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
||||
settings = SettingService.SetSetting(settings, "AllowRawHtml", _allowrawhtml);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,6 +50,8 @@ namespace Oqtane.Modules
|
|||
|
||||
public virtual List<Resource> Resources { get; set; }
|
||||
|
||||
public virtual List<string> ResourcesRegistrationTypes { get; }
|
||||
|
||||
public virtual string RenderMode { get { return RenderModes.Interactive; } } // interactive by default
|
||||
|
||||
public virtual bool? Prerender { get { return null; } } // allows the Site Prerender property to be overridden
|
||||
|
@ -71,6 +73,21 @@ namespace Oqtane.Modules
|
|||
|
||||
// base lifecycle method for handling JSInterop script registration
|
||||
|
||||
public virtual async Task<List<Resource>> GetResources(IServiceProvider serviceProvider, Page page)
|
||||
{
|
||||
var resources = Resources ?? new List<Resource>();
|
||||
|
||||
if(ResourcesRegistrationTypes != null)
|
||||
{
|
||||
foreach(var type in ResourcesRegistrationTypes)
|
||||
{
|
||||
resources.AddRange(await LoadResourcesFromType(serviceProvider, page, type));
|
||||
}
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
|
@ -482,6 +499,25 @@ namespace Oqtane.Modules
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<List<Resource>> LoadResourcesFromType(IServiceProvider serviceProvider, Page page, string typeName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = Type.GetType(typeName, false, true);
|
||||
if (type != null && type.GetInterfaces().Contains(typeof(IModuleControl)))
|
||||
{
|
||||
var control = Activator.CreateInstance(type) as IModuleControl;
|
||||
return await control?.GetResources(serviceProvider, page);
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
//await Log(null, LogLevel.Error, string.Empty, ex, "Load Resources From Type {Type} Failed. {Message}", typeName, ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)]
|
||||
public string ContentUrl(int fileid)
|
||||
{
|
||||
|
|
13
Oqtane.Client/Providers/QuillJSTextEditorProvider.cs
Normal file
13
Oqtane.Client/Providers/QuillJSTextEditorProvider.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Oqtane.Interfaces;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class QuillJSTextEditorProvider : ITextEditorProvider
|
||||
{
|
||||
public string Name => "QuillJS";
|
||||
|
||||
public string EditorType => "Oqtane.Modules.Controls.QuillJSTextEditor, Oqtane.Client";
|
||||
|
||||
public string SettingsType => "Oqtane.Modules.Controls.QuillJSTextEditorSettings, Oqtane.Client";
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using Oqtane.Interfaces;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class QuillTextEditorProvider : ITextEditorProvider
|
||||
{
|
||||
public string Name => "Quill";
|
||||
|
||||
public string EditorType => "Oqtane.Modules.Controls.QuillTextEditor, Oqtane.Client";
|
||||
}
|
||||
}
|
13
Oqtane.Client/Providers/TextAreaTextEditorProvider.cs
Normal file
13
Oqtane.Client/Providers/TextAreaTextEditorProvider.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Oqtane.Interfaces;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class TextAreaTextEditorProvider : ITextEditorProvider
|
||||
{
|
||||
public string Name => "TextArea";
|
||||
|
||||
public string EditorType => "Oqtane.Modules.Controls.TextAreaTextEditor, Oqtane.Client";
|
||||
|
||||
public string SettingsType => string.Empty;
|
||||
}
|
||||
}
|
|
@ -429,4 +429,10 @@
|
|||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Interactivity:</value>
|
||||
</data>
|
||||
<data name="TextEditorProvider.HelpText" xml:space="preserve">
|
||||
<value>Select the text editor provider for the site</value>
|
||||
</data>
|
||||
<data name="TextEditorProvider.Text" xml:space="preserve">
|
||||
<value>Text Editor Provider:</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,156 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
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.
|
||||
|
||||
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>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AllowFileManagement.HelpText" xml:space="preserve">
|
||||
<value>Specify If Editors Can Upload and Select Files</value>
|
||||
</data>
|
||||
<data name="AllowFileManagement.Text" xml:space="preserve">
|
||||
<value>Allow File Management:</value>
|
||||
</data>
|
||||
<data name="AllowRawHtml.HelpText" xml:space="preserve">
|
||||
<value>Specify If Editors Can Enter Raw HTML</value>
|
||||
</data>
|
||||
<data name="AllowRawHtml.Text" xml:space="preserve">
|
||||
<value>Allow Raw HTML:</value>
|
||||
</data>
|
||||
<data name="AllowRichText.HelpText" xml:space="preserve">
|
||||
<value>Specify If Editors Can Use Rich Text Editor</value>
|
||||
</data>
|
||||
<data name="AllowRichText.Text" xml:space="preserve">
|
||||
<value>Allow Rich Text: </value>
|
||||
</data>
|
||||
<data name="DebugLevel.HelpText" xml:space="preserve">
|
||||
<value>Specify the Debug Level</value>
|
||||
</data>
|
||||
<data name="DebugLevel.Text" xml:space="preserve">
|
||||
<value>Debug Level:</value>
|
||||
</data>
|
||||
<data name="Theme.HelpText" xml:space="preserve">
|
||||
<value>Specify the Rich Text Editor's Theme</value>
|
||||
</data>
|
||||
<data name="Theme.Text" xml:space="preserve">
|
||||
<value>Theme:</value>
|
||||
</data>
|
||||
<data name="ToolbarContent.HelpText" xml:space="preserve">
|
||||
<value>Specify the Toolbar Content</value>
|
||||
</data>
|
||||
<data name="ToolbarContent.Text" xml:space="preserve">
|
||||
<value>Toolbar Content:</value>
|
||||
</data>
|
||||
</root>
|
|
@ -5,6 +5,7 @@
|
|||
@using Microsoft.AspNetCore.Http.Extensions
|
||||
@using Microsoft.AspNetCore.Antiforgery
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@using Microsoft.Extensions.DependencyInjection
|
||||
@using Microsoft.Net.Http.Headers
|
||||
@using Microsoft.Extensions.Primitives
|
||||
@using Oqtane.Client
|
||||
|
@ -30,6 +31,7 @@
|
|||
@inject IUrlMappingRepository UrlMappingRepository
|
||||
@inject IVisitorRepository VisitorRepository
|
||||
@inject IJwtManager JwtManager
|
||||
@inject IServiceProvider ServiceProvider
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
|
@ -674,7 +676,7 @@
|
|||
var obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
if (obj != null)
|
||||
{
|
||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||
resources = AddResources(resources, await obj.GetResources(ServiceProvider, page), ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||
{
|
||||
// settings components are embedded within a framework settings module
|
||||
|
@ -682,7 +684,7 @@
|
|||
if (moduletype != null)
|
||||
{
|
||||
obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||
resources = AddResources(resources, await obj.GetResources(ServiceProvider, page), ResourceLevel.Module, alias, "Modules", moduletype.Namespace, site.RenderMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddTransient<IUserManager, UserManager>();
|
||||
|
||||
// providers
|
||||
services.AddTransient<ITextEditorProvider, QuillTextEditorProvider>();
|
||||
services.AddTransient<ITextEditorProvider, QuillJSTextEditorProvider>();
|
||||
services.AddTransient<ITextEditorProvider, TextAreaTextEditorProvider>();
|
||||
|
||||
// obsolete - replaced by ITenantManager
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
|
|
|
@ -248,4 +248,10 @@ app {
|
|||
}
|
||||
.app-search input + button .oi{
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Text Editor */
|
||||
.text-area-editor > textarea {
|
||||
width: 100%;
|
||||
min-height: 250px;
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Modules
|
||||
{
|
||||
|
@ -31,6 +33,17 @@ namespace Oqtane.Modules
|
|||
/// </summary>
|
||||
List<Resource> Resources { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The component types which need to register it's own resources.
|
||||
/// </summary>
|
||||
List<string> ResourcesRegistrationTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies all resources in a module including resources from resources registration types.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<Resource>> GetResources(IServiceProvider serviceProvider, Page page);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the required render mode for the module control ie. Static,Interactive
|
||||
/// </summary>
|
||||
|
|
|
@ -14,5 +14,10 @@ namespace Oqtane.Interfaces
|
|||
/// The text editor type full name.
|
||||
/// </summary>
|
||||
string EditorType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The text editor settings type full name.
|
||||
/// </summary>
|
||||
string SettingsType { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,9 @@ namespace Oqtane.Shared
|
|||
public const string DefaultSearchProviderName = "Database";
|
||||
public const string SearchPageIdPropertyName = "PageId";
|
||||
public const string SearchModuleIdPropertyName = "ModuleId";
|
||||
|
||||
|
||||
public const string DefaultTextEditorProvider = "Oqtane.Modules.Controls.QuillJSTextEditor, Oqtane.Client";
|
||||
|
||||
// Obsolete constants
|
||||
|
||||
const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames";
|
||||
|
|
Loading…
Reference in New Issue
Block a user