Merge branch 'dev' into 3805-stop-registration-redirect-without-verification

This commit is contained in:
Mostafa 2024-02-22 00:22:21 +01:00 committed by GitHub
commit 1c586d8811
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 772 additions and 351 deletions

View File

@ -17,7 +17,6 @@ else
<Pager Items="@_jobs" SearchProperties="Name">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th>
@ -28,7 +27,6 @@ else
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
<td><ActionDialog Header="Delete Job" Message="Are You Sure You Wish To Delete This Job?" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteJob(context))" ResourceKey="DeleteJob" /></td>
<td><ActionLink Action="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
<td>@context.Name</td>
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
@ -49,17 +47,17 @@ else
}
@code {
private List<Job> _jobs;
private List<Job> _jobs;
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
protected override async Task OnParametersSetAsync()
{
_jobs = await JobService.GetJobsAsync();
if (_jobs.Count == 0)
{
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
}
protected override async Task OnInitializedAsync()
{
_jobs = await JobService.GetJobsAsync();
if (_jobs.Count == 0)
{
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
}
}
private string DisplayStatus(bool isEnabled, bool isExecuting)
@ -112,22 +110,6 @@ else
return result;
}
private async Task DeleteJob(Job job)
{
try
{
await JobService.DeleteJobAsync(job.JobId);
await logger.LogInformation("Job Deleted {Job}", job);
_jobs = await JobService.GetJobsAsync();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Job {Job} {Error}", job, ex.Message);
AddModuleMessage(Localizer["Error.Job.Delete"], MessageType.Error);
}
}
private async Task StartJob(int jobId)
{
try

View File

@ -47,8 +47,13 @@
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
}
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
@if (PageState.Site.AllowRegistration)
{
<br /><br />
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
}
}
</div>
</form>
}
@ -84,8 +89,6 @@
private bool _alwaysremember = false;
private string _code = string.Empty;
private string _returnUrl = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
public override List<Resource> Resources => new List<Resource>()
@ -103,11 +106,6 @@
_togglepassword = SharedLocalizer["ShowPassword"];
if (PageState.QueryString.ContainsKey("returnurl"))
{
_returnUrl = PageState.QueryString["returnurl"];
}
if (PageState.QueryString.ContainsKey("name"))
{
_username = PageState.QueryString["name"];
@ -208,12 +206,12 @@
// hybrid apps utilize an interactive login
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
NavigationManager.NavigateTo(NavigateUrl(PageState.ReturnUrl, true));
}
else
{
// post back to the Login page so that the cookies are set correctly
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = WebUtility.UrlEncode(PageState.ReturnUrl) };
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
await interop.SubmitForm(url, fields);
}
@ -255,7 +253,7 @@
private void Cancel()
{
NavigationManager.NavigateTo(WebUtility.UrlDecode(_returnUrl));
NavigationManager.NavigateTo(PageState.ReturnUrl);
}
private async Task Forgot()
@ -323,7 +321,7 @@
private void ExternalLogin()
{
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + WebUtility.UrlEncode(PageState.ReturnUrl)), true);
}
}

View File

@ -201,7 +201,7 @@
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading=@Localizer["Theme.Heading"] ResourceKey="ThemeSettings">
@ThemeSettingsComponent
@_themeSettingsComponent
</TabPanel>
}
</TabStrip>
@ -240,7 +240,7 @@
private PermissionGrid _permissionGrid;
private Type _themeSettingsType;
private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; }
private RenderFragment _themeSettingsComponent { get; set; }
private bool _refresh = false;
protected Page _parent = null;
protected Dictionary<string, string> _icons;
@ -337,13 +337,14 @@
private void ThemeSettings()
{
_themeSettingsType = null;
_themeSettingsComponent = null;
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
{
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
if (_themeSettingsType != null)
{
ThemeSettingsComponent = builder =>
_themeSettingsComponent = builder =>
{
builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });

View File

@ -234,7 +234,7 @@
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
@_themeSettingsComponent
</TabPanel>
<br />
}
@ -278,7 +278,7 @@
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
@_themeSettingsComponent
</TabPanel>
<br />
}
@ -317,7 +317,7 @@
private string _containertype = "-";
private Type _themeSettingsType;
private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; }
private RenderFragment _themeSettingsComponent { get; set; }
private string _headcontent;
private string _bodycontent;
private List<Permission> _permissions = null;
@ -467,7 +467,7 @@
_containertype = _containers.First().TypeName;
ThemeSettings();
StateHasChanged();
// if theme chosen is different than default site theme, display warning message to user
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
{
@ -478,13 +478,14 @@
private void ThemeSettings()
{
_themeSettingsType = null;
_themeSettingsComponent = null;
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
{
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
if (_themeSettingsType != null)
{
ThemeSettingsComponent = builder =>
_themeSettingsComponent = builder =>
{
builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
@ -630,11 +631,11 @@
await logger.LogInformation("Page Saved {Page}", _page);
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
{
NavigationManager.NavigateTo(PageState.ReturnUrl);
NavigationManager.NavigateTo(PageState.ReturnUrl, true);
}
else
{
NavigationManager.NavigateTo(NavigateUrl());
NavigationManager.NavigateTo(NavigateUrl(), true);
}
}
else

View File

@ -62,6 +62,11 @@
<br />
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
@if (_allowsitelogin)
{
<br /><br />
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
}
</form>
</NotAuthorized>
</AuthorizeView>
@ -84,12 +89,14 @@ else
private string _email = string.Empty;
private string _displayname = string.Empty;
private bool _userCreated = false;
private bool _allowsitelogin = true;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
protected override async Task OnInitializedAsync()
{
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
}
protected override void OnParametersSet()
@ -158,7 +165,7 @@ else
private void Cancel()
{
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
NavigationManager.NavigateTo(PageState.ReturnUrl);
}
private void TogglePassword()

View File

@ -579,9 +579,6 @@
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
{
bool refresh = false;
bool reload = false;
site.Name = _name;
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
@ -595,7 +592,6 @@
if (logofileid != _logofileid)
{
_logofileid = logofileid;
refresh = true; // needs to be refreshed on client
}
}
int? faviconFieldId = _faviconfilemanager.GetFileId();
@ -603,17 +599,14 @@
if (site.FaviconFileId != faviconFieldId)
{
site.FaviconFileId = faviconFieldId;
reload = true; // needs to be reloaded on server
}
if (site.DefaultThemeType != _themetype)
{
site.DefaultThemeType = _themetype;
refresh = true; // needs to be refreshed on client
}
if (site.DefaultContainerType != _containertype)
{
site.DefaultContainerType = _containertype;
refresh = true; // needs to be refreshed on client
}
site.AdminContainerType = _admincontainertype;
@ -621,33 +614,28 @@
if (site.HeadContent != _headcontent)
{
site.HeadContent = _headcontent;
reload = true;
}
if (site.BodyContent != _bodycontent)
{
site.BodyContent = _bodycontent;
reload = true;
}
// PWA
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
{
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
reload = true; // needs to be reloaded on server
}
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
if (pwaappiconfileid == -1) pwaappiconfileid = null;
if (site.PwaAppIconFileId != pwaappiconfileid)
{
site.PwaAppIconFileId = pwaappiconfileid;
reload = true; // needs to be reloaded on server
}
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
if (site.PwaSplashIconFileId != pwasplashiconfileid)
{
site.PwaSplashIconFileId = pwasplashiconfileid;
reload = true; // needs to be reloaded on server
}
// hosting model
@ -659,7 +647,6 @@
site.Runtime = _runtime;
site.Prerender = bool.Parse(_prerender);
site.Hybrid = bool.Parse(_hybrid);
reload = true; // needs to be reloaded on serve
}
}
@ -686,15 +673,7 @@
await logger.LogInformation("Site Settings Saved {Site}", site);
if (refresh || reload)
{
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
}
else
{
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
await ScrollToPageTop();
}
NavigationManager.NavigateTo(NavigateUrl(), true); // reload
}
}
else

View File

@ -483,9 +483,9 @@
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
await logger.LogInformation("User Profile Saved");
if (PageState.QueryString.ContainsKey("returnurl"))
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
{
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
NavigationManager.NavigateTo(PageState.ReturnUrl);
}
else // legacy behavior
{
@ -551,7 +551,7 @@
private void Cancel()
{
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
NavigationManager.NavigateTo(PageState.ReturnUrl);
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)

View File

@ -6,20 +6,24 @@
{
<div class="@_classname alert-dismissible fade show mb-3" role="alert">
@((MarkupString)_message)
@if (Type == MessageType.Error && PageState != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
@if (PageState != null)
{
@((MarkupString)"&nbsp;&nbsp;")<NavLink href="@NavigateUrl("admin/log")">View Details</NavLink>
@if (Type == MessageType.Error && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
}
<form method="post" @onsubmit="DismissModal" @formname="@_formname" data-enhance>
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<button type="submit" class="btn-close" aria-label="Close"></button>
</form>
}
<form method="post" @onsubmit="DismissModal" @formname="@($"ModuleMessageForm{ModuleState.PageModuleId}")" data-enhance>
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<button type="submit" class="btn-close" aria-label="Close"></button>
</form>
</div>
}
@code {
private string _message = string.Empty;
private string _classname = string.Empty;
private string _formname = "ModuleMessageForm";
[Parameter]
public string Message { get; set; }
@ -27,6 +31,14 @@
[Parameter]
public MessageType Type { get; set; }
protected override void OnInitialized()
{
if (ModuleState != null)
{
_formname += ModuleState.PageModuleId.ToString();
}
}
protected override void OnParametersSet()
{
_message = Message;

View File

@ -10,7 +10,6 @@ using System.Collections.Generic;
using Microsoft.JSInterop;
using System.Linq;
using System.Dynamic;
using System.Net.Http.Headers;
namespace Oqtane.Modules
{
@ -90,7 +89,7 @@ namespace Oqtane.Modules
resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList();
}
}
if (resources != null &&resources.Any())
if (resources != null && resources.Any())
{
var interop = new Interop(JSRuntime);
var scripts = new List<object>();

View File

@ -144,18 +144,9 @@
<data name="Month" xml:space="preserve">
<value>Month(s)</value>
</data>
<data name="Error.Job.Delete" xml:space="preserve">
<value>Error Deleting Job</value>
</data>
<data name="ViewJobs.Text" xml:space="preserve">
<value>View Logs</value>
</data>
<data name="DeleteJob.Header" xml:space="preserve">
<value>Delete Job</value>
</data>
<data name="DeleteJob.Message" xml:space="preserve">
<value>Are You Sure You Wish To Delete This Job?</value>
</data>
<data name="Frequency" xml:space="preserve">
<value>Frequency</value>
</data>
@ -165,9 +156,6 @@
<data name="Stop" xml:space="preserve">
<value>Stop</value>
</data>
<data name="DeleteJob.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="EditJob.Text" xml:space="preserve">
<value>Edit</value>
</data>

View File

@ -228,4 +228,7 @@
<data name="ExternalLoginStatus.ReviewClaims" xml:space="preserve">
<value>The Review Claims Option Was Enabled In External Login Settings. Please Visit The Event Log To View The Claims Returned By The Provider.</value>
</data>
<data name="Register" xml:space="preserve">
<value>Register as new user?</value>
</data>
</root>

View File

@ -177,4 +177,7 @@
<data name="Username.Text" xml:space="preserve">
<value>Username:</value>
</data>
<data name="Login" xml:space="preserve">
<value>Already have account? Login now.</value>
</data>
</root>

View File

@ -1,5 +1,4 @@
using Oqtane.Models;
using Oqtane.UI;
using System.Collections.Generic;
using System.Threading.Tasks;

View File

@ -10,7 +10,7 @@ namespace Oqtane.Services
public interface IPageService
{
/// <summary>
/// Retuns a list of pages
/// Returns a list of pages
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>

View File

@ -3,11 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System;
using System.Reflection;
using Oqtane.Documentation;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Services
{

View File

@ -52,7 +52,7 @@ namespace Oqtane.Themes.Controls
else
{
// use existing value
loginurl = "?returnurl=" + PageState.QueryString["returnurl"];
loginurl += "?returnurl=" + PageState.QueryString["returnurl"];
}
// set logout url

View File

@ -31,7 +31,16 @@
protected override void OnParametersSet()
{
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
if (!PageState.QueryString.ContainsKey("returnurl"))
{
// remember current url
_returnurl += WebUtility.UrlEncode(PageState.Route.PathAndQuery);
}
else
{
// use existing value
_returnurl += PageState.QueryString["returnurl"];
}
}
}

View File

@ -16,9 +16,9 @@ namespace Oqtane.Themes.OqtaneTheme
ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client",
Resources = new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.0/cyborg/bootstrap.min.css", Integrity = "sha512-jwIqEv8o/kTBMJVtbNCBrDqhBojl0YSUam+EFpLjVOC86Ci6t4ZciTnIkelFNOik+dEQVymKGcQLiaJZNAfWRg==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/cyborg/bootstrap.min.css", Integrity = "sha512-RfNxVfFNFgqk9MXO4TCKXYXn9hgc+keHCg3xFFGbnp2q7Cifda+YYzMTDHwsQtNx4DuqIMgfvZead7XOtB9CDQ==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", Integrity = "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", CrossOrigin = "anonymous" }
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-X/YkDZyjTf4wyc2Vy16YGCPHwAY8rZJY+POgokZjQB2mhIRFJCckEGc6YyX9eNsPfn0PzThEuNs+uaomE5CO6A==", CrossOrigin = "anonymous" }
}
};
}

View File

@ -3,6 +3,8 @@
@using Oqtane.Shared
@inject SiteState SiteState
@implements IDisposable
@* the following StreamRendering attribute is required - if it is removed the framework will not render the content in static rendering *@
@attribute [StreamRendering]
@if (!string.IsNullOrEmpty(_title))
{
@ -17,6 +19,12 @@
private string _title = "";
private string _content = "";
[Parameter]
public string RenderMode { get; set; }
[Parameter]
public string Runtime { get; set; }
protected override void OnInitialized()
{
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged;
@ -47,7 +55,7 @@
private string RemoveScripts(string headcontent)
{
if (!string.IsNullOrEmpty(headcontent))
if (!string.IsNullOrEmpty(headcontent) && RenderMode == RenderModes.Interactive)
{
var index = headcontent.IndexOf("<script");
while (index >= 0)

View File

@ -17,7 +17,7 @@ namespace Oqtane.UI
case Runtimes.Auto:
return new InteractiveAutoRenderMode(prerender: prerender);
}
return new InteractiveServerRenderMode(prerender: prerender); // default to interactiver server
return new InteractiveServerRenderMode(prerender: prerender);
}
}
}

View File

@ -1,18 +1,18 @@
@namespace Oqtane.UI
@inject SiteState SiteState
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static)
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
{
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
<StreamRenderingDisabled ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
}
else
{
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
<StreamRenderingEnabled ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
}
@code {
// this component is on the static side of the render mode boundary
// it passes state as serializable parameters across the boundary so that the state can be used by downstream interactive components
// it passes state as serializable parameters across the boundary
[CascadingParameter]
protected PageState PageState { get; set; }
@ -20,7 +20,8 @@ else
[CascadingParameter]
private Module ModuleState { get; set; }
[Obsolete("AddModuleMessage is deprecated. Use ModuleBase.AddModuleMessage instead.", false)]
[Obsolete("AddModuleMessage is deprecated. Use AddModuleMessage in ModuleBase instead.", false)]
public void AddModuleMessage(string message, MessageType type)
{
}
@ -30,12 +31,12 @@ else
{
}
[Obsolete("ShowProgressIndicator is deprecated. Use ModuleBase.ShowProgressIndicator instead.", false)]
[Obsolete("ShowProgressIndicator is deprecated. Use ShowProgressIndicator in ModuleBase instead.", false)]
public void ShowProgressIndicator()
{
}
[Obsolete("HideProgressIndicator is deprecated. Use ModuleBase.HideProgressIndicator instead.", false)]
[Obsolete("HideProgressIndicator is deprecated. Use HideProgressIndicator in ModuleBase instead.", false)]
public void HideProgressIndicator()
{
}

View File

@ -67,7 +67,7 @@ else
// pane matches current pane
if (Name.ToLower() == pane.ToLower())
{
if (module.ModuleId == PageState.ModuleId && PageState.Action != Constants.DefaultAction)
if (PageState.ModuleId == module.ModuleId && PageState.Action != Constants.DefaultAction)
{
var moduleType = Type.GetType(module.ModuleType);
if (moduleType != null)

View File

@ -4,12 +4,12 @@
@inject ILogService LoggingService
@inherits ErrorBoundary
@if (CurrentException is null)
{
@if (ModuleType != null)
{
<CascadingValue Value="@PageState">
<CascadingValue Value="@ModuleState">
<CascadingValue Value="@PageState">
<CascadingValue Value="@ModuleState">
@if (CurrentException is null)
{
@if (ModuleType != null)
{
@if (!string.IsNullOrEmpty(_messageContent) && _messagePosition == "top")
{
<ModuleMessage Message="@_messageContent" Type="@_messageType" />
@ -23,19 +23,22 @@
{
<ModuleMessage Message="@_messageContent" Type="@_messageType" />
}
</CascadingValue>
</CascadingValue>
}
}
else
{
@if (!string.IsNullOrEmpty(_error))
{
<ModuleMessage Message="@_error" Type="@MessageType.Error" />
}
}
}
}
else
{
@if (!string.IsNullOrEmpty(_error))
{
<ModuleMessage Message="@_error" Type="@MessageType.Error" />
}
}
</CascadingValue>
</CascadingValue>
@code {
// this component is on the interactive side of the render mode boundary
// it receives state as serializable parameters so that the state can be made available to downstream components
private Type ModuleType { get; set; }
RenderFragment DynamicComponent { get; set; }

View File

@ -3,7 +3,8 @@
@inject IInstallationService InstallationService
@inject IJSRuntime JSRuntime
@inject SiteState SiteState
@attribute [StreamRendering]
@* the following StreamRendering attribute is required - if it is removed the framework will not render the content in static rendering *@
@attribute [StreamRendering]
@if (_initialized)
{

View File

@ -50,7 +50,7 @@
DynamicComponent = builder =>
{
if (PageState != null)
if (PageState != null && !PageState.Refresh)
{
builder.OpenComponent(0, typeof(ThemeBuilder));
builder.CloseComponent();
@ -101,8 +101,8 @@
_error = "";
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
int moduleid = int.Parse(route.ModuleId);
var action = route.Action;
var querystring = Utilities.ParseQueryString(route.Query);
var returnurl = "";
@ -567,9 +567,19 @@
// ensure resource does not exist already
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
{
resource.Level = level;
resource.Namespace = name;
pageresources.Add(resource);
pageresources.Add(new Resource
{
ResourceType = resource.ResourceType,
Url = resource.Url,
Integrity = resource.Integrity,
CrossOrigin = resource.CrossOrigin,
Bundle = resource.Bundle,
Location = resource.Location,
ES6Module = resource.ES6Module,
Content = resource.Content,
Level = level,
Namespace = name
});
}
}
}

View File

@ -0,0 +1,21 @@
@attribute [StreamRendering(false)]
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static)
{
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
}
else
{
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
}
@code {
[Parameter]
public SiteState SiteState { get; set; }
[Parameter]
public PageState PageState { get; set; }
[Parameter]
public Module ModuleState { get; set; }
}

View File

@ -0,0 +1,21 @@
@attribute [StreamRendering(true)]
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Static)
{
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
}
else
{
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
}
@code {
[Parameter]
public SiteState SiteState { get; set; }
[Parameter]
public PageState PageState { get; set; }
[Parameter]
public Module ModuleState { get; set; }
}

View File

@ -53,30 +53,6 @@
{
headcontent = AddHeadContent(headcontent, PageState.Page.HeadContent);
}
if (PageState.RenderMode == RenderModes.Static)
{
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
int count = 0;
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet && item.Level == ResourceLevel.Module))
{
// if (resource.Url.StartsWith("~"))
// {
// resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(name) + "/").Replace("//", "/");
// }
if (!resource.Url.Contains("://") && PageState.Alias.BaseUrl != "" && !resource.Url.StartsWith(PageState.Alias.BaseUrl))
{
resource.Url = PageState.Alias.BaseUrl + resource.Url;
}
if (!headcontent.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
{
count++;
string id = "id=\"app-stylesheet-" + resource.Level.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" ";
headcontent += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + (!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") + (!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") + " type=\"text/css\"/>" + Environment.NewLine;
}
}
}
SiteState.Properties.HeadContent = headcontent;
DynamicComponent = builder =>
@ -96,7 +72,7 @@
while (index >= 0)
{
var element = content.Substring(index, content.IndexOf(">", index) - index + 1);
if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("<script") && !element.ToLower().StartsWith("</script"))
if (!string.IsNullOrEmpty(element) && (PageState.RenderMode == RenderModes.Static || (!element.ToLower().StartsWith("<script") && !element.ToLower().StartsWith("</script"))))
{
if (!headcontent.Contains(element))
{
@ -168,6 +144,7 @@
string integrity = "";
string crossorigin = "";
string type = "";
var dataAttributes = new Dictionary<string, string>();
foreach (var attribute in attributes)
{
if (attribute.Contains("="))
@ -190,6 +167,12 @@
case "type":
type = value[1];
break;
default:
if(!string.IsNullOrWhiteSpace(value[0]) && value[0].StartsWith("data-"))
{
dataAttributes.Add(value[0], value[1]);
}
break;
}
}
}
@ -197,7 +180,7 @@
if (!string.IsNullOrEmpty(src))
{
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower() });
scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower(), dataAttributes = dataAttributes });
}
else
{
@ -217,4 +200,67 @@
await interop.IncludeScripts(scripts.ToArray());
}
}
private string ManageStyleSheets(List<Resource> resources, Alias alias)
{
var stylesheets = "";
if (resources != null)
{
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
int count = 0;
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(resource.Namespace) + "/").Replace("//", "/");
}
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
{
resource.Url = alias.BaseUrl + resource.Url;
}
if (!stylesheets.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
{
count++;
string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" ";
stylesheets += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + (!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") + (!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") + " type=\"text/css\"/>" + Environment.NewLine;
}
}
}
return stylesheets;
}
private string ManageScripts(List<Resource> resources, Alias alias)
{
var scripts = "";
if (resources != null)
{
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Script && item.Location == ResourceLocation.Head))
{
var script = CreateScript(resource, alias);
if (!scripts.Contains(script, StringComparison.OrdinalIgnoreCase))
{
scripts += script + Environment.NewLine;
}
}
}
return scripts;
}
private string CreateScript(Resource resource, Alias alias)
{
if (!string.IsNullOrEmpty(resource.Url))
{
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
return "<script src=\"" + url + "\"" +
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
"></script>";
}
else
{
// inline script
return "<script>" + resource.Content + "</script>";
}
}
}

View File

@ -35,7 +35,7 @@
<ItemGroup>
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
</ItemGroup>
<ItemGroup>

View File

@ -1,6 +1,16 @@
<DynamicComponent Type="@ComponentType"></DynamicComponent>
@using Oqtane.Shared;
<DynamicComponent Type="@ComponentType" Parameters="@Parameters"></DynamicComponent>
@code {
Type ComponentType = Type.GetType("Oqtane.UI.Head, Oqtane.Client");
Type ComponentType = Type.GetType("Oqtane.UI.Head, Oqtane.Client");
private IDictionary<string, object> Parameters { get; set; }
protected override void OnInitialized()
{
Parameters = new Dictionary<string, object>();
Parameters.Add(new KeyValuePair<string, object>("RenderMode", RenderModes.Interactive));
Parameters.Add(new KeyValuePair<string, object>("Runtime", Runtimes.Hybrid));
}
}

View File

@ -21,17 +21,15 @@
@inject IConfigManager ConfigManager
@inject ITenantManager TenantManager
@inject ISiteService SiteService
@inject IPageRepository PageRepository
@inject IThemeRepository ThemeRepository
@inject ILanguageRepository LanguageRepository
@inject IServerStateManager ServerStateManager
@inject ILocalizationManager LocalizationManager
@inject IAliasRepository AliasRepository
@inject IUrlMappingRepository UrlMappingRepository
@inject IVisitorRepository VisitorRepository
@inject IJwtManager JwtManager
@if (_pageState != null)
@if (_initialized)
{
<!DOCTYPE html>
<html lang="@_language">
@ -51,11 +49,11 @@
<link id="app-stylesheet-module" />
@if (_renderMode == RenderModes.Static)
{
<Head />
<Head RenderMode="@_renderMode" Runtime="@_runtime" />
}
else
{
<Head @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(_runtime, _prerender)" />
<Head RenderMode="@_renderMode" Runtime="@_runtime" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(_runtime, _prerender)" />
}
@((MarkupString)_headResources)
</head>
@ -93,6 +91,7 @@
}
@code {
private bool _initialized = false;
private string _renderMode = RenderModes.Interactive;
private string _runtime = Runtimes.Server;
private bool _prerender = true;
@ -139,12 +138,19 @@
_prerender = site.Prerender;
Route route = new Route(url, alias.Path);
var page = PageRepository.GetPage(route.PagePath, site.SiteId);
if (page == null && route.PagePath == "" && site.HomePageId != null)
var page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
if (page == null && route.PagePath == "") // naked path refers to site home page
{
page = PageRepository.GetPage(site.HomePageId.Value);
if (site.HomePageId != null)
{
page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId);
}
if (page == null)
{
// fallback to use the first page in the collection
page = site.Pages.FirstOrDefault();
}
}
if (page == null || page.IsDeleted)
{
HandlePageNotFound(site, page, route);
@ -161,34 +167,15 @@
CreateJwtToken(alias);
}
// stylesheets
var themes = ThemeRepository.GetThemes().ToList();
var resources = new List<Resource>();
if (string.IsNullOrEmpty(page.ThemeType))
{
page.ThemeType = site.DefaultThemeType;
}
var theme = themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
if (theme?.Resources != null)
{
resources.AddRange(theme.Resources);
}
var type = Type.GetType(page.ThemeType);
if (type != null)
{
var obj = Activator.CreateInstance(type) as IThemeControl;
if (obj?.Resources != null)
{
resources.AddRange(obj.Resources);
}
}
ManageStyleSheets(resources, alias, theme.ThemeName);
// include stylesheets to prevent FOUC
var resources = GetPageResources(alias, site, page, int.Parse(route.ModuleId), route.Action);
ManageStyleSheets(resources);
// scripts
if (_renderMode == RenderModes.Static)
{
ManageScripts(resources, alias);
}
// scripts
if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server)
{
_reconnectScript = CreateReconnectScript();
@ -199,22 +186,16 @@
}
_headResources += ParseScripts(site.HeadContent);
_bodyResources += ParseScripts(site.BodyContent);
var scripts = ServerStateManager.GetServerState(alias.SiteKey).Scripts;
foreach (var script in scripts)
{
AddScript(script, alias);
}
// set culture if not specified
string culture = Context.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
if (culture == null)
{
// get default language for site
var languages = LanguageRepository.GetLanguages(alias.SiteId);
if (languages.Any())
if (site.Languages.Any())
{
// use default language if specified otherwise use first language in collection
culture = (languages.Where(l => l.IsDefault).SingleOrDefault() ?? languages.First()).Code;
culture = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code;
}
else
{
@ -266,6 +247,7 @@
_message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
}
}
_initialized = true;
}
private void HandleDefaultAliasRedirect(Alias alias, string url)
@ -562,7 +544,111 @@
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)));
}
private void ManageStyleSheets(List<Resource> resources, Alias alias, string name)
private List<Resource> GetPageResources(Alias alias, Site site, Page page, int moduleid, string action)
{
var resources = new List<Resource>();
var themeType = (string.IsNullOrEmpty(page.ThemeType)) ? site.DefaultThemeType : page.ThemeType;
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == themeType));
if (theme != null)
{
resources = AddResources(resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName));
}
var type = Type.GetType(themeType);
if (type != null)
{
var obj = Activator.CreateInstance(type) as IThemeControl;
if (obj != null)
{
resources = AddResources(resources, obj.Resources, ResourceLevel.Page, alias, "Themes", type.Namespace);
}
}
foreach (Module module in site.Modules)
{
if (module.PageId == page.PageId || module.ModuleId == moduleid)
{
var typename = "";
if (module.ModuleDefinition != null)
{
typename = module.ModuleDefinition.ControlTypeTemplate;
resources = AddResources(resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
}
if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
{
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
}
else
{
typename = typename.Replace(Constants.ActionToken, action);
}
Type moduletype = Type.GetType(typename, false, true); // case insensitive
if (moduletype != null && moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
{
module.ModuleType = Utilities.GetFullTypeName(moduletype.AssemblyQualifiedName); // get actual type name
}
if (moduletype != null && module.ModuleType != "")
{
var obj = Activator.CreateInstance(moduletype) as IModuleControl;
if (obj != null)
{
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", type.Namespace);
}
}
}
}
// site level resources for modules in site
var modules = site.Modules.GroupBy(item => item.ModuleDefinition.ModuleDefinitionName).Select(group => group.First()).ToList();
foreach (var module in modules)
{
if (module.ModuleDefinition.Resources != null)
{
resources = AddResources(resources, module.ModuleDefinition.Resources.Where(item => item.Level == ResourceLevel.Site).ToList(), ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
}
}
return resources;
}
private List<Resource> AddResources(List<Resource> pageresources, List<Resource> resources, ResourceLevel level, Alias alias, string type, string name)
{
if (resources != null)
{
foreach (var resource in resources)
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/" + type + "/" + name + "/").Replace("//", "/");
}
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
{
resource.Url = alias.BaseUrl + resource.Url;
}
// ensure resource does not exist already
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
{
pageresources.Add(new Resource
{
ResourceType = resource.ResourceType,
Url = resource.Url,
Integrity = resource.Integrity,
CrossOrigin = resource.CrossOrigin,
Bundle = resource.Bundle,
Location = resource.Location,
ES6Module = resource.ES6Module,
Content = resource.Content,
Level = level,
Namespace = name
});
}
}
}
return pageresources;
}
private void ManageStyleSheets(List<Resource> resources)
{
if (resources != null)
{
@ -570,21 +656,9 @@
int count = 0;
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(name) + "/").Replace("//", "/");
}
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
{
resource.Url = alias.BaseUrl + resource.Url;
}
if (!_styleSheets.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
{
count++;
string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" ";
_styleSheets += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + (!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") + (!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") + " type=\"text/css\"/>" + Environment.NewLine;
}
count++;
string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" ";
_styleSheets += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + (!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") + (!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") + " type=\"text/css\"/>" + Environment.NewLine;
}
}
}

View File

@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using System.Net;
using Oqtane.Modules;
using Oqtane.Infrastructure.Interfaces;
namespace Oqtane.Controllers
{
@ -319,52 +320,33 @@ namespace Oqtane.Controllers
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, ModuleDefinition moduleDefinition)
{
var tokenReplace = InitializeTokenReplace(rootPath, rootFolder, moduleDefinition);
// process folder
string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
folderPath = folderPath.Replace("[Owner]", moduleDefinition.Owner);
folderPath = folderPath.Replace("[Module]", moduleDefinition.Name);
var folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
folderPath = tokenReplace.ReplaceTokens(folderPath);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
FileInfo[] files = current.GetFiles("*.*");
tokenReplace.AddSource("Folder", folderPath);
var files = current.GetFiles("*.*");
if (files != null)
{
foreach (FileInfo file in files)
{
// process file
string filePath = Path.Combine(folderPath, file.Name);
filePath = filePath.Replace("[Owner]", moduleDefinition.Owner);
filePath = filePath.Replace("[Module]", moduleDefinition.Name);
var filePath = Path.Combine(folderPath, file.Name);
filePath = tokenReplace.ReplaceTokens(filePath);
tokenReplace.AddSource("File", Path.GetFileName(filePath));
string text = System.IO.File.ReadAllText(file.FullName);
text = text.Replace("[Owner]", moduleDefinition.Owner);
text = text.Replace("[Module]", moduleDefinition.Name);
text = text.Replace("[Description]", moduleDefinition.Description);
text = text.Replace("[RootPath]", rootPath);
text = text.Replace("[RootFolder]", rootFolder);
text = text.Replace("[ServerManagerType]", moduleDefinition.ServerManagerType);
text = text.Replace("[Folder]", folderPath);
text = text.Replace("[File]", Path.GetFileName(filePath));
if (moduleDefinition.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
text = text.Replace("[ClientReference]", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll</HintPath></Reference>");
text = text.Replace("[ServerReference]", $"<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll</HintPath></Reference>");
text = text.Replace("[SharedReference]", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll</HintPath></Reference>");
}
else
{
text = text.Replace("[FrameworkVersion]", moduleDefinition.Version);
text = text.Replace("[ClientReference]", "<PackageReference Include=\"Oqtane.Client\" Version=\"" + moduleDefinition.Version + "\" />");
text = text.Replace("[ServerReference]", "<PackageReference Include=\"Oqtane.Server\" Version=\"" + moduleDefinition.Version + "\" />");
text = text.Replace("[SharedReference]", "<PackageReference Include=\"Oqtane.Shared\" Version=\"" + moduleDefinition.Version + "\" />");
}
var text = System.IO.File.ReadAllText(file.FullName);
text = tokenReplace.ReplaceTokens(text);
System.IO.File.WriteAllText(filePath, text);
}
DirectoryInfo[] folders = current.GetDirectories();
var folders = current.GetDirectories();
foreach (DirectoryInfo folder in folders.Reverse())
{
@ -372,5 +354,51 @@ namespace Oqtane.Controllers
}
}
}
private ITokenReplace InitializeTokenReplace(string rootPath, string rootFolder, ModuleDefinition moduleDefinition)
{
var tokenReplace = _serviceProvider.GetService<ITokenReplace>();
tokenReplace.AddSource(() =>
{
return new Dictionary<string, object>
{
{ "RootPath", rootPath },
{ "RootFolder", rootFolder },
{ "Owner", moduleDefinition.Owner },
{ "Module", moduleDefinition.Name },
{ "Description", moduleDefinition.Description },
{ "ServerManagerType", moduleDefinition.ServerManagerType }
};
});
if (moduleDefinition.Version == "local")
{
tokenReplace.AddSource(() =>
{
return new Dictionary<string, object>()
{
{ "FrameworkVersion", Constants.Version },
{ "ClientReference", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll</HintPath></Reference>" },
{ "ServerReference", $"<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll</HintPath></Reference>" },
{ "SharedReference", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll</HintPath></Reference>" },
};
});
}
else
{
tokenReplace.AddSource(() =>
{
return new Dictionary<string, object>()
{
{ "FrameworkVersion", moduleDefinition.Version },
{ "ClientReference", $"<PackageReference Include=\"Oqtane.Client\" Version=\"{moduleDefinition.Version}\" />" },
{ "ServerReference", $"<PackageReference Include=\"Oqtane.Client\" Version=\"{moduleDefinition.Version}\" />" },
{ "SharedReference", $"<PackageReference Include=\"Oqtane.Client\" Version=\"{moduleDefinition.Version}\" />" },
};
});
}
return tokenReplace;
}
}
}

View File

@ -14,6 +14,8 @@ using System.Text.Json;
using System.Net;
using System.Reflection.Metadata;
using System;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Infrastructure.Interfaces;
// ReSharper disable StringIndexOfIsCultureSpecific.1
@ -29,8 +31,9 @@ namespace Oqtane.Controllers
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
private readonly IServiceProvider _serviceProvider;
public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IServiceProvider serviceProvider)
{
_themes = themes;
_installationManager = installationManager;
@ -39,6 +42,7 @@ namespace Oqtane.Controllers
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
_serviceProvider = serviceProvider;
}
// GET: api/<controller>
@ -208,54 +212,80 @@ namespace Oqtane.Controllers
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, Theme theme)
{
var tokenReplace = InitializeTokenReplace(rootPath, rootFolder, theme);
// process folder
string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
folderPath = folderPath.Replace("[Owner]", theme.Owner);
folderPath = folderPath.Replace("[Theme]", theme.Name);
var folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
folderPath = tokenReplace.ReplaceTokens(folderPath);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
FileInfo[] files = current.GetFiles("*.*");
tokenReplace.AddSource("Folder", folderPath);
var files = current.GetFiles("*.*");
if (files != null)
{
foreach (FileInfo file in files)
{
// process file
string filePath = Path.Combine(folderPath, file.Name);
filePath = filePath.Replace("[Owner]", theme.Owner);
filePath = filePath.Replace("[Theme]", theme.Name);
var filePath = Path.Combine(folderPath, file.Name);
filePath = tokenReplace.ReplaceTokens(filePath);
tokenReplace.AddSource("File", Path.GetFileName(filePath));
string text = System.IO.File.ReadAllText(file.FullName);
text = text.Replace("[Owner]", theme.Owner);
text = text.Replace("[Theme]", theme.Name);
text = text.Replace("[RootPath]", rootPath);
text = text.Replace("[RootFolder]", rootFolder);
text = text.Replace("[Folder]", folderPath);
text = text.Replace("[File]", Path.GetFileName(filePath));
if (theme.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
text = text.Replace("[ClientReference]", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll</HintPath></Reference>");
text = text.Replace("[SharedReference]", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll</HintPath></Reference>");
}
else
{
text = text.Replace("[FrameworkVersion]", theme.Version);
text = text.Replace("[ClientReference]", "<PackageReference Include=\"Oqtane.Client\" Version=\"" + theme.Version + "\" />");
text = text.Replace("[SharedReference]", "<PackageReference Include=\"Oqtane.Shared\" Version=\"" + theme.Version + "\" />");
}
var text = System.IO.File.ReadAllText(file.FullName);
text = tokenReplace.ReplaceTokens(text);
System.IO.File.WriteAllText(filePath, text);
}
DirectoryInfo[] folders = current.GetDirectories();
var folders = current.GetDirectories();
foreach (DirectoryInfo folder in folders.Reverse())
{
ProcessTemplatesRecursively(folder, rootPath, rootFolder, templatePath, theme);
}
}
}
private ITokenReplace InitializeTokenReplace(string rootPath, string rootFolder, Theme theme)
{
var tokenReplace = _serviceProvider.GetService<ITokenReplace>();
tokenReplace.AddSource(() =>
{
return new Dictionary<string, object>
{
{ "RootPath", rootPath },
{ "RootFolder", rootFolder },
{ "Owner", theme.Owner },
{ "Theme", theme.Name }
};
});
if (theme.Version == "local")
{
tokenReplace.AddSource(() =>
{
return new Dictionary<string, object>()
{
{ "FrameworkVersion", Constants.Version },
{ "ClientReference", $"<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll</HintPath></Reference>" },
{ "SharedReference", $"<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll</HintPath></Reference>" },
};
});
}
else
{
tokenReplace.AddSource(() =>
{
return new Dictionary<string, object>()
{
{ "FrameworkVersion", theme.Version },
{ "ClientReference", $"<PackageReference Include=\"Oqtane.Client\" Version=\"{theme.Version}\" />" },
{ "SharedReference", $"<PackageReference Include=\"Oqtane.Client\" Version=\"{theme.Version}\" />" },
};
});
}
return tokenReplace;
}
}
}

View File

@ -19,6 +19,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Oqtane.Infrastructure;
using Oqtane.Infrastructure.Interfaces;
using Oqtane.Managers;
using Oqtane.Models;
using Oqtane.Modules;
@ -150,6 +151,8 @@ namespace Microsoft.Extensions.DependencyInjection
// obsolete - replaced by ITenantManager
services.AddTransient<ITenantResolver, TenantResolver>();
services.AddTransient<ITokenReplace, TokenReplace>();
return services;
}

View File

@ -21,8 +21,9 @@ namespace Oqtane.Extensions
options.Password.RequireNonAlphanumeric = bool.Parse(sitesettings.GetValue("IdentityOptions:Password:RequireNonAlphanumeric", options.Password.RequireNonAlphanumeric.ToString()));
// lockout options
options.Lockout.MaxFailedAccessAttempts = int.Parse(sitesettings.GetValue("IdentityOptions:Password:MaxFailedAccessAttempts", options.Lockout.MaxFailedAccessAttempts.ToString()));
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.Parse(sitesettings.GetValue("IdentityOptions:Password:DefaultLockoutTimeSpan", options.Lockout.DefaultLockoutTimeSpan.ToString()));
options.Lockout.MaxFailedAccessAttempts = int.Parse(sitesettings.GetValue("IdentityOptions:Lockout:MaxFailedAccessAttempts", options.Lockout.MaxFailedAccessAttempts.ToString()));
options.Lockout.DefaultLockoutTimeSpan = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow) + TimeSpan.Parse(sitesettings.GetValue("IdentityOptions:Lockout:DefaultLockoutTimeSpan", options.Lockout.DefaultLockoutTimeSpan.ToString()));
options.Lockout.AllowedForNewUsers = options.Lockout.MaxFailedAccessAttempts > 0;
});
return builder;

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Oqtane.Interfaces;
namespace Oqtane.Infrastructure.Interfaces
{
public interface ITokenReplace
{
void AddSource(ITokenSource source);
void AddSource(Func<IDictionary<string, object>> sourceFunc);
void AddSource(IDictionary<string, object> source);
void AddSource(string key, object value);
void AddSource(string name, ITokenSource source);
void AddSource(string name, Func<IDictionary<string, object>> sourceFunc);
void AddSource(string name, IDictionary<string, object> source);
void AddSource(string name, string key, object value);
string ReplaceTokens(string source);
}
}

View File

@ -7,7 +7,6 @@ namespace Oqtane.Infrastructure
{
public string SiteKey { get; set; }
public List<string> Assemblies { get; set; } = new List<string>();
public List<Resource>Scripts { get; set; } = new List<Resource>();
public bool IsInitialized { get; set; } = false;
}
}

View File

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
@ -22,7 +21,6 @@ namespace Oqtane.Infrastructure
serverState = new ServerState();
serverState.SiteKey = siteKey;
serverState.Assemblies = new List<string>();
serverState.Scripts = new List<Resource>();
serverState.IsInitialized = false;
_serverStates.Add(serverState);
}

View File

@ -59,7 +59,7 @@ namespace Oqtane.SiteTemplates
},
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>CMS</b> and <b>Application Framework</b> that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a <b>fully dynamic</b> digital experience. Whether you are looking for a platform to <b>accelerate your web development</b> efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles and patterns.</p>" +
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org\" target=\"_new\">Join Our Community</a>&nbsp;&nbsp;<a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be shared across all of the hosting models without any modification.</p>" +
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is a modern front-end web framework based on HTML, CSS, and C# that helps you build web applications faster. Blazor provides a component-based architecture with server-side rendering and full client-side interactivity in a single solution, where you can switch between server-side and client-side rendering modes and even mix them in the same web page. For desktop or mobile scenarios, Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on all modern devices.</p>" +
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">ASP.NET</a>, the popular cross platform development framework from Microsoft that provides powerful tools and libraries for building modern software applications.</p>"
},
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Default,

View File

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using Oqtane.Infrastructure.Interfaces;
using Oqtane.Interfaces;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class TokenReplace : ITokenReplace
{
public const string GenericName = "generic";
private const string TokenExpression = "(?:(?<text>\\[\\])|\\[(?:(?<source>[^{}\\]\\[:]+):(?<key>[^\\]\\[\\|]+)|(?<key>[^\\]\\[\\|]+))(?:\\|(?:(?<format>[^\\]\\[]+)\\|(?<empty>[^\\]\\\\[]+))|\\|(?:(?<format>[^\\|\\]\\[]+)))?\\])|(?<text>\\[[^\\]\\[]+\\])|(?<text>\\[{0,1}[^\\]\\[]+\\]{0,1})";
private Regex TokenizerRegex = new Regex(TokenExpression, RegexOptions.Compiled | RegexOptions.Singleline);
private IDictionary<string, IDictionary<string, object>> _tokens;
private readonly ILogManager _logger;
public TokenReplace(ILogManager logger)
{
_tokens = new Dictionary<string, IDictionary<string, object>>();
_logger = logger;
}
public void AddSource(ITokenSource source)
{
this.AddSource(GenericName, source);
}
public void AddSource(Func<IDictionary<string, object>> sourceFunc)
{
this.AddSource(GenericName, sourceFunc);
}
public void AddSource(IDictionary<string, object> source)
{
this.AddSource(GenericName, source);
}
public void AddSource(string key, object value)
{
this.AddSource(GenericName, key, value);
}
public void AddSource(string name, ITokenSource source)
{
var tokens = source.GetTokens();
this.AddSource(name, tokens);
}
public void AddSource(string name, Func<IDictionary<string, object>> sourceFunc)
{
var tokens = sourceFunc();
this.AddSource(name, tokens);
}
public void AddSource(string name, IDictionary<string, object> source)
{
if(source != null)
{
foreach (var key in source.Keys)
{
this.AddSource(name, key, source[key]);
}
}
}
public void AddSource(string name, string key, object value)
{
if (string.IsNullOrWhiteSpace(name))
{
name = GenericName;
}
var source = _tokens.ContainsKey(name.ToLower()) ? _tokens[name.ToLower()] : null;
if(source == null)
{
source = new Dictionary<string, object>();
}
source[key] = value;
_tokens[name.ToLower()] = source;
}
public string ReplaceTokens(string source)
{
if (string.IsNullOrWhiteSpace(source))
{
return source;
}
var result = new StringBuilder();
foreach (Match match in this.TokenizerRegex.Matches(source))
{
var key = match.Result("${key}");
if (!string.IsNullOrWhiteSpace(key))
{
var sourceName = match.Result("${source}");
if (string.IsNullOrWhiteSpace(sourceName) || sourceName == "[")
{
sourceName = GenericName;
}
var format = match.Result("${format}");
var emptyReplacment = match.Result("${empty}");
var value = ReplaceTokenValue(sourceName, key, format);
if (string.IsNullOrWhiteSpace(value))
{
if(!string.IsNullOrWhiteSpace(emptyReplacment))
{
value = emptyReplacment;
}
else //keep the original content
{
value = match.Value;
}
}
result.Append(value);
}
else
{
result.Append(match.Result("${text}"));
}
}
return result.ToString();
}
private string ReplaceTokenValue(string sourceName, string key, string format)
{
if(!_tokens.ContainsKey(sourceName.ToLower()))
{
_logger.Log(Shared.LogLevel.Debug, this, Enums.LogFunction.Other, $"MissingSource:{sourceName}");
return string.Empty;
}
var tokens = _tokens[sourceName.ToLower()];
if(!tokens.ContainsKey(key))
{
_logger.Log(Shared.LogLevel.Debug, this, Enums.LogFunction.Other, $"MissingKey:{key}");
return string.Empty;
}
var value = tokens[key];
if(value == null)
{
return string.Empty;
}
//TODO: need to implement the format.
return value.ToString();
}
}
}

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;

View File

@ -17,6 +17,7 @@
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
<DefineConstants>$(DefineConstants);OQTANE;OQTANE3</DefineConstants>
<PreserveCompilationContext>true</PreserveCompilationContext>
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
</PropertyGroup>
<ItemGroup>

View File

@ -225,17 +225,6 @@ namespace Oqtane.Repository
}
}
}
// build list of scripts for site
if (moduledefinition.Resources != null)
{
foreach (var resource in moduledefinition.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site))
{
if (!serverState.Scripts.Contains(resource))
{
serverState.Scripts.Add(resource);
}
}
}
}
if (permissions.Count == 0)

View File

@ -197,17 +197,6 @@ namespace Oqtane.Repository
}
}
}
// build list of scripts for site
if (theme.Resources != null)
{
foreach (var resource in theme.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Site))
{
if (!serverState.Scripts.Contains(resource))
{
serverState.Scripts.Add(resource);
}
}
}
}
}
}

View File

@ -27,11 +27,11 @@ namespace Oqtane.Services
private readonly ILanguageRepository _languages;
private readonly IUserPermissions _userPermissions;
private readonly ISettingRepository _settings;
private readonly ITenantManager _tenantManager;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly IMemoryCache _cache;
private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public ServerSiteService(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache, IHttpContextAccessor accessor)
{
@ -43,11 +43,11 @@ namespace Oqtane.Services
_languages = languages;
_userPermissions = userPermissions;
_settings = settings;
_tenantManager = tenantManager;
_syncManager = syncManager;
_logger = logger;
_cache = cache;
_accessor = accessor;
_alias = tenantManager.GetAlias();
}
public async Task<List<Site>> GetSitesAsync()
@ -80,8 +80,9 @@ namespace Oqtane.Services
private Site GetSite(int siteid)
{
var alias = _tenantManager.GetAlias();
var site = _sites.GetSite(siteid);
if (site != null && site.SiteId == _alias.SiteId)
if (site != null && site.SiteId == alias.SiteId)
{
// site settings
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId)
@ -179,8 +180,9 @@ namespace Oqtane.Services
{
if (_accessor.HttpContext.User.IsInRole(RoleNames.Host))
{
var alias = _tenantManager.GetAlias();
site = _sites.AddSite(site);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Create);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Create);
_logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Create, "Site Added {Site}", site);
}
else
@ -194,17 +196,18 @@ namespace Oqtane.Services
{
if (_accessor.HttpContext.User.IsInRole(RoleNames.Admin))
{
var alias = _tenantManager.GetAlias();
var current = _sites.GetSite(site.SiteId, false);
if (site.SiteId == _alias.SiteId && site.TenantId == _alias.TenantId && current != null)
if (site.SiteId == alias.SiteId && site.TenantId == alias.TenantId && current != null)
{
site = _sites.UpdateSite(site);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update);
string action = SyncEventActions.Refresh;
if (current.RenderMode != site.RenderMode || current.Runtime != site.Runtime)
{
action = SyncEventActions.Reload;
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, action);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, action);
_logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site);
}
else
@ -224,11 +227,12 @@ namespace Oqtane.Services
{
if (_accessor.HttpContext.User.IsInRole(RoleNames.Host))
{
var alias = _tenantManager.GetAlias();
var site = _sites.GetSite(siteId);
if (site != null && site.SiteId == _alias.SiteId)
if (site != null && site.SiteId == alias.SiteId)
{
_sites.DeleteSite(siteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Delete);
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Delete);
_logger.Log(siteId, LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", siteId);
}
else

View File

@ -27,7 +27,7 @@
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
@ -41,7 +41,7 @@
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -197,7 +197,7 @@
}
}
@media (max-width: 767px) {
@media (max-width: 767.98px) {
.app-logo {
height: 80px;
display: flex;

View File

@ -96,7 +96,7 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
z-index: 1000;
}
@media (max-width: 767px) {
@media (max-width: 767.98px) {
.app-menu {
width: 100%;

View File

@ -30,7 +30,7 @@
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
@ -44,7 +44,7 @@
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -17,9 +17,9 @@ namespace [Owner].Theme.[Theme]
Resources = new List<Resource>()
{
// obtained from https://cdnjs.com/libraries
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css", Integrity = "sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css", Integrity = "sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", Integrity = "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", CrossOrigin = "anonymous" }
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js", Integrity = "sha512-X/YkDZyjTf4wyc2Vy16YGCPHwAY8rZJY+POgokZjQB2mhIRFJCckEGc6YyX9eNsPfn0PzThEuNs+uaomE5CO6A==", CrossOrigin = "anonymous" }
}
};

View File

@ -90,7 +90,7 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
mix-blend-mode: difference;
}
@media (max-width: 767px) {
@media (max-width: 767.98px) {
.app-menu {
width: 100%;

View File

@ -206,18 +206,25 @@ Oqtane.Interop = {
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;
}
if (path === scripts[s].href && scripts[s].es6module === true) {
element.type = "module";
}
if (path === scripts[s].href && scripts[s].location === 'body') {
document.body.appendChild(element);
return false; // return false to bypass default DOM insertion mechanism
if (path === scripts[s].href) {
if (scripts[s].integrity !== '') {
element.integrity = scripts[s].integrity;
}
if (scripts[s].crossorigin !== '') {
element.crossOrigin = scripts[s].crossorigin;
}
if (scripts[s].es6module === true) {
element.type = "module";
}
if (typeof scripts[s].dataAttributes !== "undefined" && scripts[s].dataAttributes !== null) {
for (var key in scripts[s].dataAttributes) {
element.setAttribute(key, scripts[s].dataAttributes[key]);
}
}
if (scripts[s].location === 'body') {
document.body.appendChild(element);
return false; // return false to bypass default DOM insertion mechanism
}
}
}
}

View File

@ -2,7 +2,7 @@ namespace Oqtane.Shared
{
public enum ResourceLevel
{
App,
Undefined,
Site,
Page,
Module

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Oqtane.Interfaces
{
public interface ITokenSource
{
IDictionary<string, object> GetTokens();
}
}

View File

@ -32,8 +32,8 @@ namespace Oqtane.Models
PathAndQuery = uri.PathAndQuery;
AliasPath = aliaspath;
PagePath = AbsolutePath;
ModuleId = "";
Action = "";
ModuleId = "-1";
Action = Constants.DefaultAction;
UrlParameters = "";
if (AliasPath.Length != 0 && PagePath.StartsWith("/" + AliasPath))
@ -49,7 +49,7 @@ namespace Oqtane.Models
pos = PagePath.IndexOf("/" + Constants.ModuleDelimiter + "/");
if (pos != -1)
{
ModuleId = PagePath.Substring(pos + 3);
ModuleId = PagePath.Substring(pos + 3);
PagePath = PagePath.Substring(0, pos);
}
if (ModuleId.Length != 0)
@ -57,8 +57,10 @@ namespace Oqtane.Models
pos = ModuleId.IndexOf("/");
if (pos != -1)
{
Action = ModuleId.Substring(pos + 1);
Action = ModuleId.Substring(pos + 1);
Action = (!string.IsNullOrEmpty(Action)) ? Action : Constants.DefaultAction;
ModuleId = ModuleId.Substring(0, pos);
ModuleId = (int.TryParse(ModuleId, out _)) ? ModuleId : "-1";
}
}
if (PagePath.StartsWith("/"))

View File

@ -5,7 +5,7 @@ namespace Oqtane.Shared
public class Constants
{
public static readonly string Version = "5.1.0";
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.1.0";
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0";
public const string PackageId = "Oqtane.Framework";
public const string ClientId = "Oqtane.Client";
public const string UpdaterPackageId = "Oqtane.Updater";