Merge pull request #18 from oqtane/master

Sync Master
This commit is contained in:
Jim Spillane
2020-06-17 12:14:54 -04:00
committed by GitHub
47 changed files with 399 additions and 281 deletions

View File

@ -1,7 +1,6 @@
@namespace Oqtane.Modules.Admin.Login
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IJSRuntime JsRuntime
@inject IUserService UserService
@inject IServiceProvider ServiceProvider
@ -96,7 +95,7 @@
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
// complete the login on the server so that the cookies are set correctly on SignalR
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields);

View File

@ -4,7 +4,6 @@
@inject IFileService FileService
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@inject IJSRuntime JsRuntime
@if (_packages != null)
{
@ -79,7 +78,7 @@
try
{
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}

View File

@ -3,7 +3,6 @@
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@inject IJSRuntime JsRuntime
@if (_moduleDefinitions == null)
{
@ -86,7 +85,7 @@ else
await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules");
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}
@ -102,7 +101,7 @@ else
try
{
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
}

View File

@ -4,7 +4,6 @@
@inject IFileService FileService
@inject IThemeService ThemeService
@inject IPackageService PackageService
@inject IJSRuntime JsRuntime
@if (_packages != null)
{
@ -79,7 +78,7 @@
try
{
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.InstallThemesAsync();
}

View File

@ -4,7 +4,6 @@
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IPackageService PackageService
@inject IJSRuntime JsRuntime
@if (_themes == null)
{
@ -86,7 +85,7 @@ else
await PackageService.DownloadPackageAsync(themename, version, "Themes");
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.InstallThemesAsync();
}
@ -102,7 +101,7 @@ else
try
{
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
}

View File

@ -4,7 +4,6 @@
@inject IFileService FileService
@inject IPackageService PackageService
@inject IInstallationService InstallationService
@inject IJSRuntime JsRuntime
@if (_package != null)
{
@ -71,7 +70,7 @@
try
{
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await InstallationService.Upgrade();
}
@ -88,7 +87,7 @@
{
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator();
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await InstallationService.Upgrade();
}

View File

@ -73,7 +73,7 @@ else
}
</TabPanel>
<TabPanel Name="Profile">
@if (profiles != null)
@if (profiles != null && settings != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
@ -126,7 +126,16 @@ else
</Row>
<Detail>
<td colspan="2"></td>
<td colspan="3">@(context.Body.Length > 100 ? context.Body.Substring(0, 100) : context.Body)</td>
<td colspan="3">
@{
string input = "___";
if (context.Body.Contains(input)){
context.Body = context.Body.Split(input)[0];
context.Body = context.Body.Replace("\n", "");
context.Body = context.Body.Replace("\r", "");
} }
@(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body)
</td>
</Detail>
</Pager>
}
@ -149,7 +158,16 @@ else
</Row>
<Detail>
<td colspan="2"></td>
<td colspan="3">@(context.Body.Length > 100 ? context.Body.Substring(0, 100) : context.Body)</td>
<td colspan="3">
@{
string input = "___";
if (context.Body.Contains(input)){
context.Body = context.Body.Split(input)[0];
context.Body = context.Body.Replace("\n", "");
context.Body = context.Body.Replace("\r", "");
} }
@(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body)
</td>
</Detail>
</Pager>
}

View File

@ -9,56 +9,94 @@
<table class="table table-borderless">
<tr>
<td>
<label class="control-label">@title: </label>
</td>
<td>
<input class="form-control" @bind="@username" />
<label class="control-label">@title: </label>
</td>
@if (title == "From")
{
<td>
<input class="form-control" @bind="@username" readonly />
</td>
}
@if (title == "To")
{
<td>
<input class="form-control" @bind="@username" />
</td>
}
</tr>
<tr>
<td>
<label class="control-label">Subject: </label>
</td>
<td>
<input class="form-control" @bind="@subject" />
<label class="control-label">Subject: </label>
</td>
@if (title == "From")
{
<td>
<input class="form-control" @bind="@subject" readonly />
</td>
}
@if (title == "To")
{
<td>
<input class="form-control" @bind="@subject" />
</td>
}
</tr>
@if (title == "From")
{
<tr>
<td>
<label class="control-label">Date: </label>
<label class="control-label">Date: </label>
</td>
<td>
<input class="form-control" @bind="@createdon" />
<input class="form-control" @bind="@createdon" readonly />
</td>
</tr>
}
<tr>
<td>
<label class="control-label">Message: </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" />
</td>
</tr>
}
@if (title == "From")
{
<tr>
<td>
<label class="control-label">Message: </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" readonly />
</td>
</tr>
}
@if (title == "To")
{
<tr>
<td>
<label class="control-label">Message: </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" />
</td>
</tr>
}
</table>
@if (reply != string.Empty)
{
<button type="button" class="btn btn-primary" @onclick="Send">Send</button>
}
<button type="button" class="btn btn-primary" @onclick="Send">Send</button> }
else
{
if (title == "From")
{
<button type="button" class="btn btn-primary" @onclick="Reply">Reply</button>
}
<button type="button" class="btn btn-primary" @onclick="Reply">Reply</button>}
}
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<br />
<br />
<p>@reply</p>
}
@if (title == "To")
{
<div class="control-group">
<label class="control-label">Original Message </label>
<textarea class="form-control" @bind="@reply" rows="5" readonly />
</div>
}
}
@code {
private int notificationid;
@ -124,8 +162,12 @@
private void Reply()
{
title = "To";
subject = "RE: " + subject;
if (!subject.Contains("RE:"))
{
subject = "RE: " + subject;
}
reply = body;
body = "\n\n____________________________________________\nSent: " + createdon + "\nSubject: " + subject + "\n\n" + body;
StateHasChanged();
}
@ -165,5 +207,5 @@
AddModuleMessage("Error Adding Notification", MessageType.Error);
}
}
}

View File

@ -90,7 +90,8 @@
if (!string.IsNullOrEmpty(IconName))
{
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>&nbsp;";
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>{(IconOnly?"":"&nbsp")}";
}
_url = EditUrl(Action, _parameters);

View File

@ -4,7 +4,6 @@
@attribute [OqtaneIgnore]
@inject IFolderService FolderService
@inject IFileService FileService
@inject IJSRuntime JsRuntime
@if (_folders != null)
{
@ -258,7 +257,7 @@
private async Task UploadFile()
{
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
var upload = await interop.GetFiles(_fileinputid);
if (upload.Length > 0)
{

View File

@ -1,7 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inject IJSRuntime JsRuntime
<div class="row" style="margin-bottom: 50px;">
<div class="col">
@ -108,6 +107,13 @@
[Parameter]
public string DebugLevel { get; set; } = "info";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
};
protected override void OnInitialized()
{
_content = Content; // raw HTML
@ -117,7 +123,9 @@
{
if (firstRender)
{
var interop = new RichTextEditorInterop(JsRuntime);
await base.OnAfterRenderAsync(firstRender);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.CreateEditor(
_editorElement,
@ -143,13 +151,13 @@
public async Task RefreshRichText()
{
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.LoadEditorContent(_editorElement, _content);
}
public async Task RefreshRawHtml()
{
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
_content = await interop.GetHtml(_editorElement);
StateHasChanged();
}
@ -157,7 +165,7 @@
public async Task<string> GetHtml()
{
// get rich text content
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
string content = await interop.GetHtml(_editorElement);
if (_original != content)
@ -179,7 +187,7 @@
var fileid = _fileManager.GetFileId();
if (fileid != -1)
{
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.InsertImage(_editorElement, ContentUrl(fileid));
_filemanagervisible = false;
_message = string.Empty;
@ -200,19 +208,19 @@
// other rich text editor methods which can be used by developers
public async Task<string> GetText()
{
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
return await interop.GetText(_editorElement);
}
public async Task<string> GetContent()
{
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
return await interop.GetContent(_editorElement);
}
public async Task EnableEditor(bool mode)
{
var interop = new RichTextEditorInterop(JsRuntime);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.EnableEditor(_editorElement, mode);
}
}

View File

@ -13,7 +13,7 @@ namespace Oqtane.Modules.Controls
_jsRuntime = jsRuntime;
}
public Task CreateEditor(
public async Task CreateEditor(
ElementReference quillElement,
ElementReference toolbar,
bool readOnly,
@ -23,15 +23,14 @@ namespace Oqtane.Modules.Controls
{
try
{
_jsRuntime.InvokeAsync<object>(
await _jsRuntime.InvokeAsync<object>(
"Oqtane.RichTextEditor.createQuill",
quillElement, toolbar, readOnly,
placeholder, theme, debugLevel);
return Task.CompletedTask;
quillElement, toolbar, readOnly, placeholder, theme, debugLevel);
return;
}
catch
{
return Task.CompletedTask;
// handle exception
}
}

View File

@ -4,12 +4,12 @@
<div class="d-flex">
<div>
<a data-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name">
<a data-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
<h5>@_heading</h5>
</a>
</div>
<div class="ml-auto">
<a data-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name">
<a data-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
<i class="oi oi-chevron-bottom"></i>&nbsp;
</a>
</div>

View File

@ -11,13 +11,13 @@
<li class="nav-item">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab">
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab">
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}

View File

@ -27,12 +27,8 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
// the following resources should be declared in the RichTextEditor component however the framework currently only supports resource management for modules and themes
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" },
new Resource { ResourceType = ResourceType.Script, Url = "js/quill1.3.6.min.js" },
new Resource { ResourceType = ResourceType.Script, Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Url = "js/quill-interop.js" }
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" }
};
private RichTextEditor RichTextEditorHtml;

View File

@ -7,6 +7,8 @@ using System;
using Oqtane.Enums;
using Oqtane.UI;
using System.Collections.Generic;
using Microsoft.JSInterop;
using System.Linq;
namespace Oqtane.Modules
{
@ -19,6 +21,9 @@ namespace Oqtane.Modules
[Inject]
protected ILogService LoggingService { get; set; }
[Inject]
protected IJSRuntime JSRuntime { get; set; }
[CascadingParameter]
protected PageState PageState { get; set; }
@ -28,7 +33,6 @@ namespace Oqtane.Modules
[CascadingParameter]
protected ModuleInstance ModuleInstance { get; set; }
// optional interface properties
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
@ -40,7 +44,25 @@ namespace Oqtane.Modules
public virtual List<Resource> Resources { get; set; }
// base lifecycle method for handling JSInterop script registration
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{
var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
{
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
}
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
}
}
}
// path method
public string ModulePath()

View File

@ -153,7 +153,7 @@ namespace Oqtane.Services
public string GetSetting(Dictionary<string, string> settings, string settingName, string defaultValue)
{
string value = defaultValue;
if (settings.ContainsKey(settingName))
if (settings != null && settings.ContainsKey(settingName))
{
value = settings[settingName];
}
@ -162,6 +162,10 @@ namespace Oqtane.Services
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue)
{
if (settings == null)
{
settings = new Dictionary<string, string>();
}
if (settings.ContainsKey(settingName))
{
settings[settingName] = settingValue;

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Themes.BlazorTheme
@inherits ThemeBase
@implements IThemeControl
<div class="breadcrumbs">
<Breadcrumbs />
@ -30,6 +31,11 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css", Integrity = "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.3.1.slim.min.js", Integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", Integrity = "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js", Integrity = "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM", CrossOrigin = "anonymous" }
};
}

View File

@ -1,64 +1,11 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Shared;
using Oqtane.Models;
using Oqtane.UI;
namespace Oqtane.Themes
{
public abstract class ContainerBase : ComponentBase, IContainerControl
public abstract class ContainerBase : ThemeBase, IContainerControl
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }
[CascadingParameter]
protected PageState PageState { get; set; }
[CascadingParameter]
protected Module ModuleState { get; set; }
public virtual string Name { get; set; }
public virtual string Thumbnail { get; set; }
public string ThemePath()
{
return "Themes/" + GetType().Namespace + "/";
}
public string NavigateUrl()
{
return NavigateUrl(PageState.Page.Path);
}
public string NavigateUrl(string path)
{
return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
}
public string EditUrl(string action, string parameters)
{
return EditUrl(ModuleState.ModuleId, action, parameters);
}
public string EditUrl(int moduleid, string action)
{
return EditUrl(moduleid, action, "");
}
public string EditUrl(int moduleid, string action, string parameters)
{
return EditUrl(PageState.Page.Path, moduleid, action, parameters);
}
public string EditUrl(string path, int moduleid, string action, string parameters)
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
}
}
}

View File

@ -1,21 +1,7 @@
using Microsoft.AspNetCore.Components;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Themes
namespace Oqtane.Themes
{
public abstract class LayoutBase : ComponentBase, ILayoutControl
public abstract class LayoutBase : ThemeBase, ILayoutControl
{
[CascadingParameter]
protected PageState PageState { get; set; }
public virtual string Name { get; set; }
public virtual string Thumbnail { get; set; }
public virtual string Panes { get; set; }
public string LayoutPath()
{
return "Themes/" + GetType().Namespace + "/";
}
}
}

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ThemeBase
@implements IThemeControl
<main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
@ -23,10 +24,10 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "BootswatchCyborg.css" },
// remote stylesheets can be linked using the format below, however we want the default theme to display properly in local development scenarios where an Internet connection is not available
//new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.3.1.slim.min.js", Integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", Integrity = "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js", Integrity = "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM", CrossOrigin = "anonymous" }
};
}

View File

@ -4,11 +4,12 @@ using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.UI;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Oqtane.Themes
{
public abstract class ThemeBase : ComponentBase, IThemeControl
public abstract class ThemeBase : ComponentBase
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }
@ -22,6 +23,25 @@ namespace Oqtane.Themes
public virtual string Panes { get; set; }
public virtual List<Resource> Resources { get; set; }
// base lifecycle method for handling JSInterop script registration
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{
var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
{
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
}
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
}
}
}
// path method
public string ThemePath()
@ -60,5 +80,10 @@ namespace Oqtane.Themes
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
}
public string ContentUrl(int fileid)
{
return Utilities.ContentUrl(PageState.Alias, fileid);
}
}
}

View File

@ -1,47 +1,7 @@
using Microsoft.AspNetCore.Components;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Themes
namespace Oqtane.Themes
{
public abstract class ThemeControlBase : ComponentBase
public abstract class ThemeControlBase : ThemeBase
{
[CascadingParameter]
protected PageState PageState { get; set; }
public string NavigateUrl()
{
return NavigateUrl(PageState.Page.Path);
}
public string NavigateUrl(string path)
{
return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
}
public string EditUrl(int moduleid, string action)
{
return EditUrl(moduleid, action, "");
}
public string EditUrl(int moduleid, string action, string parameters)
{
return EditUrl(PageState.Page.Path, moduleid, action, parameters);
}
public string EditUrl(string path, int moduleid, string action, string parameters)
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
}
public string ContentUrl(int fileid)
{
return Utilities.ContentUrl(PageState.Alias, fileid);
}
}
}

View File

@ -3,6 +3,7 @@
@inject IInstallationService InstallationService
@inject ISiteService SiteService
@inject IUserService UserService
@inject IJSRuntime JSRuntime
<div class="container">
<div class="row">
@ -138,6 +139,15 @@
private string _integratedSecurityDisplay = "display: none;";
private string _loadingDisplay = "display: none;";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var interop = new Interop(JSRuntime);
await interop.IncludeLink("app-stylesheet", "stylesheet", "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css", "text/css", "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T", "anonymous", "");
}
}
private void SetIntegratedSecurity(ChangeEventArgs e)
{
_integratedSecurityDisplay = Convert.ToBoolean((string)e.Value)

View File

@ -17,9 +17,9 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
"Oqtane.Interop.setCookie",
name, value, days);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.setCookie",
name, value, days);
return Task.CompletedTask;
}
catch
@ -46,7 +46,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.updateTitle",
title);
return Task.CompletedTask;
@ -61,7 +61,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeMeta",
id, attribute, name, content, key);
return Task.CompletedTask;
@ -76,7 +76,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeLink",
id, rel, href, type, integrity, crossorigin, key);
return Task.CompletedTask;
@ -91,7 +91,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeLinks",
(object) links);
return Task.CompletedTask;
@ -106,7 +106,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeScript",
id, src, integrity, crossorigin, content, location, key);
return Task.CompletedTask;
@ -117,18 +117,17 @@ namespace Oqtane.UI
}
}
public Task IncludeScripts(object[] scripts)
public async Task IncludeScripts(object[] scripts)
{
try
{
_jsRuntime.InvokeAsync<object>(
await _jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeScripts",
(object)scripts);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
// ignore exception
}
}
@ -136,7 +135,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.removeElementsById",
prefix, first, last);
return Task.CompletedTask;
@ -165,9 +164,9 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
"Oqtane.Interop.submitForm",
path, fields);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.submitForm",
path, fields);
return Task.CompletedTask;
}
catch
@ -194,9 +193,9 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
"Oqtane.Interop.uploadFiles",
posturl, folder, id);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.uploadFiles",
posturl, folder, id);
return Task.CompletedTask;
}
catch
@ -209,7 +208,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.refreshBrowser",
force, wait);
return Task.CompletedTask;
@ -224,7 +223,7 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<object>(
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.redirectBrowser",
url, wait);
return Task.CompletedTask;
@ -234,5 +233,6 @@ namespace Oqtane.UI
return Task.CompletedTask;
}
}
}
}

View File

@ -277,7 +277,6 @@
{
if (user == null)
{
await LogService.Log(null, null, null, GetType().AssemblyQualifiedName, Utilities.GetTypeNameLastSegment(GetType().AssemblyQualifiedName, 1), LogFunction.Security, LogLevel.Error, null, "Page Does Not Exist Or User Is Not Authorized To View Page {Path}", path);
// redirect to login page
NavigationManager.NavigateTo(Utilities.NavigateUrl(alias.Path, "login", "returnurl=" + path));
}
@ -361,20 +360,26 @@
string panes = "";
Type themetype = Type.GetType(page.ThemeType);
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
if (themetype != null)
{
panes = themeobject.Panes;
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
{
panes = themeobject.Panes;
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
}
}
if (!string.IsNullOrEmpty(page.LayoutType))
{
Type layouttype = Type.GetType(page.LayoutType);
var layoutobject = Activator.CreateInstance(layouttype) as ILayoutControl;
if (layoutobject != null)
if (layouttype != null)
{
panes = layoutobject.Panes;
var layoutobject = Activator.CreateInstance(layouttype) as ILayoutControl;
if (layoutobject != null)
{
panes = layoutobject.Panes;
}
}
}

View File

@ -30,27 +30,15 @@
await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name);
}
// include page resources
// manage stylesheets for this page
string batch = DateTime.Now.ToString("yyyyMMddHHmmssfff");
var links = new List<object>();
var scripts = new List<object>();
foreach (Resource resource in PageState.Page.Resources)
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
{
switch (resource.ResourceType)
{
case ResourceType.Stylesheet:
links.Add(new { id = "app-stylesheet-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" });
break;
case ResourceType.Script:
scripts.Add(new { id = "app-script-" + batch + "-" + (scripts.Count + 1).ToString("00"), src = resource.Url, integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", content = "", location = "body", key = "" });
break;
}
links.Add(new { id = "app-stylesheet-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" });
}
await interop.IncludeLinks(links.ToArray());
await interop.IncludeScripts(scripts.ToArray());
// remove any page resource references which are no longer required for this page
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00");
await interop.RemoveElementsById("app-script", "", "app-script-" + batch + "-00");
// add favicon
if (PageState.Site.FaviconFileId != null)

View File

@ -75,7 +75,7 @@ namespace Oqtane.Controllers
private User Filter(User user)
{
if (user != null && !User.IsInRole(Constants.AdminRole) && User.Identity.Name != user.Username)
if (user != null && !User.IsInRole(Constants.AdminRole) && User.Identity.Name?.ToLower() != user.Username.ToLower())
{
user.DisplayName = "";
user.Email = "";

View File

@ -194,10 +194,12 @@ namespace Oqtane.Infrastructure
if (install.TenantName == Constants.MasterTenant)
{
MigrateScriptNamingConvention("Master", install.ConnectionString);
var upgradeConfig = DeployChanges
.To
.SqlDatabase(NormalizeConnectionString(install.ConnectionString))
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.") && s.EndsWith(".sql",StringComparison.OrdinalIgnoreCase));
.To
.SqlDatabase(NormalizeConnectionString(install.ConnectionString))
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master.") && s.EndsWith(".sql",StringComparison.OrdinalIgnoreCase));
var upgrade = upgradeConfig.Build();
if (upgrade.IsUpgradeRequired())
@ -285,6 +287,8 @@ namespace Oqtane.Infrastructure
{
foreach (var tenant in db.Tenant.ToList())
{
MigrateScriptNamingConvention("Tenant", tenant.DBConnectionString);
var upgradeConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(tenant.DBConnectionString))
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant.") && s.EndsWith(".sql", StringComparison.OrdinalIgnoreCase));
@ -568,6 +572,18 @@ namespace Oqtane.Infrastructure
if (string.IsNullOrEmpty(value)) value = defaultValue;
return value;
}
private void MigrateScriptNamingConvention(string scriptType, string connectionString)
{
// migrate to new naming convention for scripts
var migrateConfig = DeployChanges.To.SqlDatabase(NormalizeConnectionString(connectionString))
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s == scriptType + ".00.00.00.00.sql");
var migrate = migrateConfig.Build();
if (migrate.IsUpgradeRequired())
{
migrate.PerformUpgrade();
}
}
}
}

View File

@ -23,16 +23,14 @@
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Scripts\Master.1.0.1.sql" />
<None Remove="Scripts\Tenant.1.0.1.sql" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Scripts\Master.1.0.1.sql" />
<EmbeddedResource Include="Scripts\Master.0.9.0.sql" />
<EmbeddedResource Include="Scripts\Tenant.1.0.1.sql" />
<EmbeddedResource Include="Scripts\Tenant.0.9.0.sql" />
<EmbeddedResource Include="Scripts\Tenant.0.9.1.sql" />
<EmbeddedResource Include="Scripts\Tenant.0.9.2.sql" />
<EmbeddedResource Include="Scripts\Master.00.00.00.00.sql" />
<EmbeddedResource Include="Scripts\Master.00.09.00.00.sql" />
<EmbeddedResource Include="Scripts\Master.01.00.01.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.00.00.00.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.00.09.00.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.00.09.01.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.00.09.02.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.01.00.01.00.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
</ItemGroup>

View File

@ -14,8 +14,8 @@
<link id="app-favicon" rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<!-- stub the PWA manifest but defer the assignment of href -->
<link id="app-manifest" rel="manifest" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="css/app.css" />
<script src="js/loadjs.min.js"></script>
</head>
<body>
@(Html.AntiForgeryToken())
@ -23,10 +23,18 @@
<component type="typeof(Oqtane.App)" render-mode="Server" />
</app>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="~/" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="js/interop.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
@if (Configuration.GetSection("Runtime").Value == "WebAssembly")
{

View File

@ -24,9 +24,9 @@ namespace Oqtane.Repository
return _db.Role.Where(item => item.SiteId == siteId || item.SiteId == null);
}
public Role AddRole(Role role)
{
role.Description = role.Description.Substring(0, (role.Description.Length > 256) ? 256 : role.Description.Length);
_db.Role.Add(role);
_db.SaveChanges();
return role;
@ -34,6 +34,7 @@ namespace Oqtane.Repository
public Role UpdateRole(Role role)
{
role.Description = role.Description.Substring(0, (role.Description.Length > 256) ? 256 : role.Description.Length);
_db.Entry(role).State = EntityState.Modified;
_db.SaveChanges();
return role;

View File

@ -0,0 +1,10 @@
/*
migrate to new naming convention for scripts
*/
UPDATE [dbo].[SchemaVersions] SET ScriptName = 'Oqtane.Scripts.Master.00.09.00.00.sql' WHERE ScriptName = 'Oqtane.Scripts.Master.0.9.0.sql'
GO
UPDATE [dbo].[SchemaVersions] SET ScriptName = 'Oqtane.Scripts.Master.01.00.01.00.sql' WHERE ScriptName = 'Oqtane.Scripts.Master.1.0.1.sql'
GO

View File

@ -0,0 +1,14 @@
/*
migrate to new naming convention for scripts
*/
UPDATE [dbo].[SchemaVersions] SET ScriptName = 'Oqtane.Scripts.Tenant.00.09.00.00.sql' WHERE ScriptName = 'Oqtane.Scripts.Tenant.0.9.0.sql'
GO
UPDATE [dbo].[SchemaVersions] SET ScriptName = 'Oqtane.Scripts.Tenant.00.09.01.00.sql' WHERE ScriptName = 'Oqtane.Scripts.Tenant.0.9.1.sql'
GO
UPDATE [dbo].[SchemaVersions] SET ScriptName = 'Oqtane.Scripts.Tenant.00.09.02.00.sql' WHERE ScriptName = 'Oqtane.Scripts.Tenant.0.9.2.sql'
GO
UPDATE [dbo].[SchemaVersions] SET ScriptName = 'Oqtane.Scripts.Tenant.01.00.01.00.sql' WHERE ScriptName = 'Oqtane.Scripts.Tenant.1.0.1.sql'
GO

View File

@ -29,7 +29,8 @@ namespace Oqtane
public IConfigurationRoot Configuration { get; }
private string _webRoot;
private Runtime _runtime;
private bool _useSwagger;
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
@ -37,9 +38,12 @@ namespace Oqtane
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Configuration = builder.Build();
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
//add possibility to switch off swagger on production.
_useSwagger = Configuration.GetSection("UseSwagger").Value != "false";
_webRoot = env.WebRootPath;
_webRoot = env.WebRootPath;
AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data"));
}
@ -47,7 +51,6 @@ namespace Oqtane
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddServerSideBlazor();
// setup HttpClient for server side in a client side compatible fashion ( with auth cookie )
@ -59,7 +62,7 @@ namespace Oqtane
var navigationManager = s.GetRequiredService<NavigationManager>();
var httpContextAccessor = s.GetRequiredService<IHttpContextAccessor>();
var authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Identity.Application"];
var client = new HttpClient(new HttpClientHandler { UseCookies = false });
var client = new HttpClient(new HttpClientHandler {UseCookies = false});
if (authToken != null)
{
client.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Identity.Application=" + authToken);
@ -121,7 +124,7 @@ namespace Oqtane
.AddEntityFrameworkStores<TenantDBContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
// Password settings
@ -199,14 +202,12 @@ namespace Oqtane
services.AddMvc()
.AddNewtonsoftJson()
.AddOqtaneApplicationParts() // register any Controllers from custom modules
.ConfigureOqtaneMvc(); // any additional configuration from IStart classes.
.ConfigureOqtaneMvc(); // any additional configuration from IStart classes.
services.AddSwaggerGen(c =>
if (_useSwagger)
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" });
});
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Oqtane", Version = "v1"}); });
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -230,11 +231,11 @@ namespace Oqtane
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(c =>
if (_useSwagger)
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane V1");
});
app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane V1"); });
}
app.UseEndpoints(endpoints =>
{
@ -242,7 +243,6 @@ namespace Oqtane
endpoints.MapControllers();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}

View File

@ -163,4 +163,23 @@ app {
margin-left: -5em;
width: 10em;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@ -145,7 +145,7 @@ Oqtane.Interop = {
script.innerHTML = content;
}
script.async = false;
this.loadScript(script, location)
this.addScript(script, location)
.then(() => {
console.log(src + ' loaded');
})
@ -185,7 +185,7 @@ Oqtane.Interop = {
}
}
},
loadScript: function (script, location) {
addScript: function (script, location) {
if (location === 'head') {
document.head.appendChild(script);
}
@ -198,9 +198,48 @@ Oqtane.Interop = {
script.onerror = rej();
});
},
includeScripts: function (scripts) {
for (let i = 0; i < scripts.length; i++) {
this.includeScript(scripts[i].id, scripts[i].src, scripts[i].integrity, scripts[i].crossorigin, scripts[i].content, scripts[i].location, scripts[i].key);
includeScripts: async function (scripts) {
const bundles = [];
for (let s = 0; s < scripts.length; s++) {
if (scripts[s].bundle === '') {
scripts[s].bundle = scripts[s].href;
}
if (!bundles.includes(scripts[s].bundle)) {
bundles.push(scripts[s].bundle);
}
}
const urls = [];
for (let b = 0; b < bundles.length; b++) {
for (let s = 0; s < scripts.length; s++) {
if (scripts[s].bundle === bundles[b]) {
urls.push(scripts[s].href);
}
}
const promise = new Promise((resolve, reject) => {
if (loadjs.isDefined(bundles[b])) {
resolve(true);
}
else {
loadjs(urls, bundles[b], {
async: true,
returnPromise: true,
before: function (path, element) {
for (let s = 0; s < scripts.length; s++) {
if (path === scripts[s].href && scripts[s].integrity !== '') {
element.integrity = scripts[s].integrity;
}
if (path === scripts[s].href && scripts[s].crossorigin !== '') {
element.crossOrigin = scripts[s].crossorigin;
}
}
}
})
.then(function () { resolve(true) })
.catch(function (pathsNotFound) { reject(false) });
}
});
await promise;
urls = [];
}
},
getAbsoluteUrl: function (url) {

View File

@ -0,0 +1 @@
loadjs=function(){var h=function(){},c={},u={},f={};function o(e,n){if(e){var r=f[e];if(u[e]=n,r)for(;r.length;)r[0](e,n),r.splice(0,1)}}function l(e,n){e.call&&(e={success:e}),n.length?(e.error||h)(n):(e.success||h)(e)}function d(r,t,s,i){var c,o,e=document,n=s.async,u=(s.numRetries||0)+1,f=s.before||h,l=r.replace(/[\?|#].*$/,""),a=r.replace(/^(css|img)!/,"");i=i||0,/(^css!|\.css$)/.test(l)?((o=e.createElement("link")).rel="stylesheet",o.href=a,(c="hideFocus"in o)&&o.relList&&(c=0,o.rel="preload",o.as="style")):/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(l)?(o=e.createElement("img")).src=a:((o=e.createElement("script")).src=r,o.async=void 0===n||n),!(o.onload=o.onerror=o.onbeforeload=function(e){var n=e.type[0];if(c)try{o.sheet.cssText.length||(n="e")}catch(e){18!=e.code&&(n="e")}if("e"==n){if((i+=1)<u)return d(r,t,s,i)}else if("preload"==o.rel&&"style"==o.as)return o.rel="stylesheet";t(r,n,e.defaultPrevented)})!==f(r,o)&&e.head.appendChild(o)}function r(e,n,r){var t,s;if(n&&n.trim&&(t=n),s=(t?r:n)||{},t){if(t in c)throw"LoadJS";c[t]=!0}function i(n,r){!function(e,t,n){var r,s,i=(e=e.push?e:[e]).length,c=i,o=[];for(r=function(e,n,r){if("e"==n&&o.push(e),"b"==n){if(!r)return;o.push(e)}--i||t(o)},s=0;s<c;s++)d(e[s],r,n)}(e,function(e){l(s,e),n&&l({success:n,error:r},e),o(t,e)},s)}if(s.returnPromise)return new Promise(i);i()}return r.ready=function(e,n){return function(e,r){e=e.push?e:[e];var n,t,s,i=[],c=e.length,o=c;for(n=function(e,n){n.length&&i.push(e),--o||r(i)};c--;)t=e[c],(s=u[t])?n(t,s):(f[t]=f[t]||[]).push(n)}(e,function(e){l(n,e)}),r},r.done=function(e){o(e,[])},r.reset=function(){c={},u={},f={}},r.isDefined=function(e){return e in c},r}();

View File

@ -1,7 +1,7 @@
var Oqtane = Oqtane || {};
Oqtane.RichTextEditor = {
createQuill: function (
createQuill: async function (
quillElement, toolBar, readOnly,
placeholder, theme, debugLevel) {

View File

@ -8,5 +8,6 @@ namespace Oqtane.Models
public string Url { get; set; }
public string Integrity { get; set; }
public string CrossOrigin { get; set; }
public string Bundle { get; set; }
}
}

View File

@ -9,7 +9,7 @@ Please note that this project is owned by the .NET Foundation and is governed by
**To get started with Oqtane:**
1.&nbsp;Install **[.NET Core 3.2 SDK (v3.1.300)](https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.300-windows-x64-installer)**.
1.&nbsp;Install **[.NET Core 3.1 SDK (v3.1.300)](https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.300-windows-x64-installer)**.
2.&nbsp;Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload enabled. Oqtane works with all editions of Visual Studio from Community to Enterprise. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**.