commit
0633b2876c
|
@ -1,91 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Http
|
||||
@inject IInstallationService InstallationService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject SiteState SiteState
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@if (!_installation.Success)
|
||||
{
|
||||
<Installer />
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (string.IsNullOrEmpty(_installation.Message))
|
||||
{
|
||||
<div style="@_display">
|
||||
<CascadingAuthenticationState>
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter Runtime="@Runtime" RenderMode="@RenderMode" VisitorId="@VisitorId" OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
</CascadingAuthenticationState>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="app-alert">
|
||||
@_installation.Message
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string AntiForgeryToken { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Runtime { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string RenderMode { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int VisitorId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string RemoteIPAddress { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string AuthorizationToken { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
HttpContext HttpContext { get; set; }
|
||||
|
||||
private bool _initialized = false;
|
||||
private string _display = "display: none;";
|
||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
||||
|
||||
private PageState PageState { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SiteState.RemoteIPAddress = RemoteIPAddress;
|
||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||
SiteState.AuthorizationToken = AuthorizationToken;
|
||||
SiteState.IsPrerendering = (HttpContext != null) ? true : false;
|
||||
|
||||
_installation = await InstallationService.IsInstalled();
|
||||
if (_installation.Alias != null)
|
||||
{
|
||||
SiteState.Alias = _installation.Alias;
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
// prevents flash on initial page load
|
||||
_display = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeState(PageState pageState)
|
||||
{
|
||||
PageState = pageState;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
|
@ -7,16 +7,17 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
public static class OqtaneServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddOqtaneAuthorization(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneAuthentication(this IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorizationCore();
|
||||
services.AddCascadingAuthenticationState();
|
||||
services.AddScoped<IdentityAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<IdentityAuthenticationStateProvider>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneClientScopedServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<SiteState>();
|
||||
services.AddScoped<IInstallationService, InstallationService>();
|
||||
|
|
|
@ -260,8 +260,10 @@
|
|||
IsNewTenant = true,
|
||||
SiteName = Constants.DefaultSite,
|
||||
Register = _register,
|
||||
SiteTemplate = _template
|
||||
};
|
||||
SiteTemplate = _template,
|
||||
RenderMode = RenderModes.Static,
|
||||
Runtime = Runtimes.Server
|
||||
};
|
||||
|
||||
var installation = await InstallationService.Install(config);
|
||||
if (installation.Success)
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||
{
|
||||
string url = NavigateUrl(p.Path);
|
||||
<p class="col-md-2 mx-auto text-center mb-3">
|
||||
<div class="col-md-2 mx-auto text-center my-3">
|
||||
<NavLink class="nav-link text-body" href="@url" Match="NavLinkMatch.All">
|
||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>
|
||||
<p class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</p>
|
||||
<div class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</div>
|
||||
</NavLink>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
@ -27,6 +27,7 @@
|
|||
private List<Page> _pages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
public override string RenderMode => RenderModes.Static;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
|
|
|
@ -17,7 +17,6 @@ else
|
|||
|
||||
<Pager Items="@_jobs" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </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
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
@if (!twofactor)
|
||||
{
|
||||
|
@ -47,8 +50,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 +92,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 +109,6 @@
|
|||
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
_returnUrl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("name"))
|
||||
{
|
||||
_username = PageState.QueryString["name"];
|
||||
|
@ -168,11 +169,14 @@
|
|||
{
|
||||
if (firstRender && PageState.User == null && _allowsitelogin)
|
||||
{
|
||||
await username.FocusAsync();
|
||||
if (!string.IsNullOrEmpty(username.Id)) // ensure username is visible in UI
|
||||
{
|
||||
await username.FocusAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// redirect logged in user to specified page
|
||||
if (PageState.User != null)
|
||||
if (PageState.User != null && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
|
@ -208,12 +212,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 +259,7 @@
|
|||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(_returnUrl);
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
|
||||
private async Task Forgot()
|
||||
|
@ -323,7 +327,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
</TabStrip>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
|
||||
|
@ -155,6 +155,7 @@
|
|||
private DateTime modifiedon;
|
||||
private DateTime? _effectivedate = null;
|
||||
private DateTime? _expirydate = null;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_module = ModuleState.ModuleDefinition.Name;
|
||||
|
@ -197,7 +198,8 @@
|
|||
ModuleSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _moduleSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||
builder.AddComponentReferenceCapture(2, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
|
@ -216,7 +218,8 @@
|
|||
ContainerSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _containerSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||
builder.AddComponentReferenceCapture(2, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
|
@ -280,7 +283,7 @@
|
|||
await containerSettingsControl.UpdateSettings();
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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,16 +337,18 @@
|
|||
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); });
|
||||
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||
builder.AddComponentReferenceCapture(2, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
|
@ -374,36 +376,54 @@
|
|||
page.SiteId = PageState.Page.SiteId;
|
||||
page.Name = _name;
|
||||
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
{
|
||||
_path = _path.Substring(0, _path.Length - 1);
|
||||
}
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.ParentId = null;
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.ParentId = Int32.Parse(_parentid);
|
||||
var parent = PageState.Pages.Where(item => item.PageId == page.ParentId).FirstOrDefault();
|
||||
if (parent.Path == string.Empty)
|
||||
}
|
||||
|
||||
// path can be a link to an external url
|
||||
if (!_path.Contains("://"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
_path = _name;
|
||||
}
|
||||
|
||||
(_path, string parameters) = Utilities.ParsePath(_path);
|
||||
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
{
|
||||
_path = _path.Substring(0, _path.Length - 1);
|
||||
}
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
}
|
||||
page.Path += parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
page.Path = _path;
|
||||
}
|
||||
|
||||
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
@ -449,23 +469,23 @@
|
|||
// appearance
|
||||
page.Title = _title;
|
||||
page.Icon = (_icon == null ? string.Empty : _icon);
|
||||
page.ThemeType = _themetype;
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = _containertype;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
page.ThemeType = _themetype;
|
||||
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
|
||||
{
|
||||
page.ThemeType = string.Empty;
|
||||
}
|
||||
page.DefaultContainerType = _containertype;
|
||||
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
page.DefaultContainerType = string.Empty;
|
||||
}
|
||||
|
||||
// page content
|
||||
page.HeadContent = _headcontent;
|
||||
page.BodyContent = _bodycontent;
|
||||
|
||||
// permissions
|
||||
page.PermissionList = _permissionGrid.GetPermissionList();
|
||||
page.PermissionList = _permissionGrid.GetPermissionList();
|
||||
|
||||
page = await PageService.AddPageAsync(page);
|
||||
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
|
||||
|
@ -473,11 +493,18 @@
|
|||
await logger.LogInformation("Page Added {Page}", page);
|
||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||
{
|
||||
NavigationManager.NavigateTo(page.Path); // redirect to new page
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl()); // redirect to page management
|
||||
if (!page.Path.Contains("://"))
|
||||
{
|
||||
NavigationManager.NavigateTo(page.Path); // redirect to new page created
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("admin/pages"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -179,9 +179,12 @@
|
|||
<Label Class="col-sm-3" For="container" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="container" class="form-select" @bind="@_containertype" required>
|
||||
@foreach (var container in _containers)
|
||||
@if (_containers != null)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -234,7 +237,7 @@
|
|||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
@ThemeSettingsComponent
|
||||
@_themeSettingsComponent
|
||||
</TabPanel>
|
||||
<br />
|
||||
}
|
||||
|
@ -278,7 +281,7 @@
|
|||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
@ThemeSettingsComponent
|
||||
@_themeSettingsComponent
|
||||
</TabPanel>
|
||||
<br />
|
||||
}
|
||||
|
@ -317,7 +320,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;
|
||||
|
@ -377,7 +380,7 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
if (_path.Contains("/"))
|
||||
if (_path.Contains("/") & !_path.Contains("://"))
|
||||
{
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
@ -467,7 +470,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,16 +481,19 @@
|
|||
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); });
|
||||
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||
builder.AddComponentReferenceCapture(2, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
|
@ -514,36 +520,54 @@
|
|||
|
||||
_page.Name = _name;
|
||||
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_path = _name;
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
{
|
||||
_path = _path.Substring(0, _path.Length - 1);
|
||||
}
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
_page.ParentId = null;
|
||||
_page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
_page.ParentId = Int32.Parse(_parentid);
|
||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
}
|
||||
|
||||
// path can be a link to an external url
|
||||
if (!_path.Contains("://"))
|
||||
{
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
{
|
||||
_page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
_path = _name;
|
||||
}
|
||||
|
||||
(_path, string parameters) = Utilities.ParsePath(_path);
|
||||
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
{
|
||||
_path = _path.Substring(0, _path.Length - 1);
|
||||
}
|
||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||
}
|
||||
if (_parentid == "-1")
|
||||
{
|
||||
_page.Path = Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
_page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
|
||||
if (parent.Path == string.Empty)
|
||||
{
|
||||
_page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
_page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
|
||||
}
|
||||
}
|
||||
_page.Path += parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
_page.Path = _path;
|
||||
}
|
||||
|
||||
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
@ -630,11 +654,18 @@
|
|||
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());
|
||||
if (!_page.Path.Contains("://"))
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(), true); // redirect to page being edited
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("admin/pages"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -140,7 +140,7 @@ else
|
|||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
ShowProgressIndicator();
|
||||
foreach (Page page in _pages.Where(item => item.IsDeleted))
|
||||
{
|
||||
await PageService.DeletePageAsync(page.PageId);
|
||||
|
@ -149,7 +149,7 @@ else
|
|||
|
||||
await logger.LogInformation("Pages Permanently Deleted");
|
||||
await Load();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ else
|
|||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,21 +199,21 @@ else
|
|||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
ShowProgressIndicator();
|
||||
foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
|
||||
{
|
||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||
}
|
||||
await logger.LogInformation("Modules Permanently Deleted");
|
||||
await Load();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
private void OnPageChangePage(int page)
|
||||
|
|
|
@ -9,60 +9,68 @@
|
|||
|
||||
@if (PageState.Site.AllowRegistration)
|
||||
{
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@_username" maxlength="256" required />
|
||||
if (!_userCreated)
|
||||
{
|
||||
<AuthorizeView Roles="@RoleNames.Registered">
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@_username" maxlength="256" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</form>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
<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>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -80,12 +88,15 @@ else
|
|||
private string _confirm = string.Empty;
|
||||
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()
|
||||
|
@ -121,14 +132,8 @@ else
|
|||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||
}
|
||||
else // legacy behavior
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
}
|
||||
_userCreated = true;
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -160,7 +165,7 @@ else
|
|||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
|
|
|
@ -98,9 +98,12 @@
|
|||
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
|
||||
@foreach (var container in _containers)
|
||||
@if (_containers != null)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -110,9 +113,12 @@
|
|||
<div class="col-sm-9">
|
||||
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
|
||||
<option value="@Constants.DefaultAdminContainer"><@Localizer["DefaultAdminContainer"]></option>
|
||||
@foreach (var container in _containers)
|
||||
@if (_containers != null)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -311,27 +317,38 @@
|
|||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor runtime hosting model for the site" ResourceKey="Runtime">Runtime: </Label>
|
||||
<Label Class="col-sm-3" For="rendermode" HelpText="The default render mode for the site" ResourceKey="Rendermode">Render Mode: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="rendermode" class="form-select" @bind="@_rendermode" required>
|
||||
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
||||
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
||||
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The render mode for UI components which require interactivity" ResourceKey="Runtime">Interactivity: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
<option value="@Runtimes.Server">@(SharedLocalizer["Runtime" + @Runtimes.Server])</option>
|
||||
<option value="@Runtimes.WebAssembly">@(SharedLocalizer["Runtime" + @Runtimes.WebAssembly])</option>
|
||||
<option value="@Runtimes.Auto">@(SharedLocalizer["Runtime" + @Runtimes.Auto])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if the site should be prerendered (for search crawlers, etc...)" ResourceKey="Prerender">Prerender? </Label>
|
||||
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if interactive components should prerender their output" ResourceKey="Prerender">Prerender? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
||||
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
|
||||
<option value="">@SharedLocalizer["No"]</option>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="hybridenabled" HelpText="Specifies if the site can be integrated with an external .NET MAUI hybrid application" ResourceKey="HybridEnabled">Hybrid Enabled? </Label>
|
||||
<Label Class="col-sm-3" For="hybrid" HelpText="Specifies if the site can be integrated with an external .NET MAUI hybrid application" ResourceKey="Hybrid">Hybrid? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="hybridenabled" class="form-select" @bind="@_hybridenabled" required>
|
||||
<select id="hybrid" class="form-select" @bind="@_hybrid" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
|
@ -414,9 +431,10 @@
|
|||
private int _aliasid = -1;
|
||||
private string _aliasname;
|
||||
private string _defaultalias;
|
||||
private string _runtime = "";
|
||||
private string _prerender = "";
|
||||
private string _hybridenabled = "";
|
||||
private string _rendermode = RenderModes.Interactive;
|
||||
private string _runtime = Runtimes.Server;
|
||||
private string _prerender = "True";
|
||||
private string _hybrid = "False";
|
||||
private string _tenant = string.Empty;
|
||||
private string _database = string.Empty;
|
||||
private string _connectionstring = string.Empty;
|
||||
|
@ -500,9 +518,10 @@
|
|||
await GetAliases();
|
||||
|
||||
// hosting model
|
||||
_rendermode = site.RenderMode;
|
||||
_runtime = site.Runtime;
|
||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||
_hybridenabled = site.HybridEnabled.ToString();
|
||||
_prerender = site.Prerender.ToString();
|
||||
_hybrid = site.Hybrid.ToString();
|
||||
|
||||
// database
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
|
@ -566,9 +585,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));
|
||||
|
@ -582,7 +598,6 @@
|
|||
if (logofileid != _logofileid)
|
||||
{
|
||||
_logofileid = logofileid;
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
}
|
||||
int? faviconFieldId = _faviconfilemanager.GetFileId();
|
||||
|
@ -590,17 +605,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;
|
||||
|
||||
|
@ -608,44 +620,39 @@
|
|||
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
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender || site.HybridEnabled != bool.Parse(_hybridenabled))
|
||||
if (site.RenderMode != _rendermode || site.Runtime != _runtime || site.Prerender != bool.Parse(_prerender) || site.Hybrid != bool.Parse(_hybrid))
|
||||
{
|
||||
site.RenderMode = _rendermode;
|
||||
site.Runtime = _runtime;
|
||||
site.RenderMode = _runtime + _prerender;
|
||||
site.HybridEnabled = bool.Parse(_hybridenabled);
|
||||
reload = true; // needs to be reloaded on serve
|
||||
site.Prerender = bool.Parse(_prerender);
|
||||
site.Hybrid = bool.Parse(_hybrid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,15 +679,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
|
||||
|
|
|
@ -71,20 +71,22 @@ else
|
|||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The runtime hosting model" ResourceKey="Runtime">Runtime: </Label>
|
||||
<Label Class="col-sm-3" For="rendermode" HelpText="The default render mode for the site" ResourceKey="Rendermode">Render Mode: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
</select>
|
||||
<select id="rendermode" class="form-select" @bind="@_rendermode" required>
|
||||
<option value="@RenderModes.Interactive">@(SharedLocalizer["RenderMode" + @RenderModes.Interactive])</option>
|
||||
<option value="@RenderModes.Static">@(SharedLocalizer["RenderMode" + @RenderModes.Static])</option>
|
||||
<option value="@RenderModes.Headless">@(SharedLocalizer["RenderMode" + @RenderModes.Headless])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if the site should be prerendered (for search crawlers, etc...)" ResourceKey="Prerender">Prerender? </Label>
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The render mode for UI components which require interactivity" ResourceKey="Runtime">Interactivity: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="prerender" class="form-select" @bind="@_prerender" required>
|
||||
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
|
||||
<option value="">@SharedLocalizer["No"]</option>
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="@Runtimes.Server">@(SharedLocalizer["Runtime" + @Runtimes.Server])</option>
|
||||
<option value="@Runtimes.WebAssembly">@(SharedLocalizer["Runtime" + @Runtimes.WebAssembly])</option>
|
||||
<option value="@Runtimes.Auto">@(SharedLocalizer["Runtime" + @Runtimes.Auto])</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -201,8 +203,8 @@ else
|
|||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _sitetemplatetype = "-";
|
||||
private string _runtime = "Server";
|
||||
private string _prerender = "Prerendered";
|
||||
private string _rendermode = RenderModes.Static;
|
||||
private string _runtime = Runtimes.Server;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
|
@ -228,137 +230,137 @@ else
|
|||
_sitetemplatetype = Constants.DefaultSiteTemplate;
|
||||
}
|
||||
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
{
|
||||
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseName = "LocalDB";
|
||||
}
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
{
|
||||
_databaseName = _databases.Find(item => item.IsDefault).Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseName = "LocalDB";
|
||||
}
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
|
||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
_databaseName = (string)eventArgs.Value;
|
||||
_showConnectionString = false;
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private void DatabaseChanged(ChangeEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
_databaseName = (string)eventArgs.Value;
|
||||
_showConnectionString = false;
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
catch
|
||||
{
|
||||
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadDatabaseConfigComponent()
|
||||
{
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
if (database != null)
|
||||
{
|
||||
_databaseConfigType = Type.GetType(database.ControlType);
|
||||
DatabaseConfigComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _databaseConfigType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
private void LoadDatabaseConfigComponent()
|
||||
{
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
if (database != null)
|
||||
{
|
||||
_databaseConfigType = Type.GetType(database.ControlType);
|
||||
DatabaseConfigComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _databaseConfigType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void TenantChanged(ChangeEventArgs e)
|
||||
{
|
||||
_tenantid = (string)e.Value;
|
||||
if (string.IsNullOrEmpty(_tenantName))
|
||||
{
|
||||
_tenantName = _name;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
private void TenantChanged(ChangeEventArgs e)
|
||||
{
|
||||
_tenantid = (string)e.Value;
|
||||
if (string.IsNullOrEmpty(_tenantName))
|
||||
{
|
||||
_tenantName = _name;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (_themetype != "-")
|
||||
{
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
private async void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (_themetype != "-")
|
||||
{
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
_containers = new List<ThemeControl>();
|
||||
else
|
||||
{
|
||||
_containers = new List<ThemeControl>();
|
||||
_containertype = "-";
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Theme.LoadContainers"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveSite()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||
{
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||
var duplicates = new List<string>();
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (aliases.Exists(item => item.Name == name))
|
||||
{
|
||||
duplicates.Add(name);
|
||||
}
|
||||
}
|
||||
private async Task SaveSite()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||
{
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||
var duplicates = new List<string>();
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (aliases.Exists(item => item.Name == name))
|
||||
{
|
||||
duplicates.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicates.Count == 0)
|
||||
{
|
||||
InstallConfig config = new InstallConfig();
|
||||
if (duplicates.Count == 0)
|
||||
{
|
||||
InstallConfig config = new InstallConfig();
|
||||
|
||||
if (_tenantid == "+")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
|
||||
{
|
||||
// validate host credentials
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _hostusername;
|
||||
user.Password = _hostpassword;
|
||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||
user = await UserService.LoginUserAsync(user, false, false);
|
||||
if (_tenantid == "+")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
|
||||
{
|
||||
// validate host credentials
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _hostusername;
|
||||
user.Password = _hostpassword;
|
||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||
user = await UserService.LoginUserAsync(user, false, false);
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
var connectionString = String.Empty;
|
||||
if (_showConnectionString)
|
||||
{
|
||||
connectionString = _connectionString;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
}
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
var connectionString = String.Empty;
|
||||
if (_showConnectionString)
|
||||
{
|
||||
connectionString = _connectionString;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||
{
|
||||
connectionString = databaseConfigControl.GetConnectionString();
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionString != "")
|
||||
{
|
||||
config.TenantName = _tenantName;
|
||||
config.DatabaseType = database.DBType;
|
||||
config.ConnectionString = connectionString;
|
||||
config.HostUsername = _hostusername;
|
||||
config.HostUsername = _hostusername;
|
||||
config.HostPassword = _hostpassword;
|
||||
config.HostEmail = user.Email;
|
||||
config.HostName = user.DisplayName;
|
||||
|
@ -399,8 +401,8 @@ else
|
|||
config.DefaultContainer = _containertype;
|
||||
config.DefaultAdminContainer = "";
|
||||
config.SiteTemplate = _sitetemplatetype;
|
||||
config.RenderMode = _rendermode;
|
||||
config.Runtime = _runtime;
|
||||
config.RenderMode = _runtime + _prerender;
|
||||
|
||||
ShowProgressIndicator();
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ else
|
|||
{
|
||||
if (PageState.Alias.Name == name)
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
NavigationManager.NavigateTo(PageState.Alias.Path + "/");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
@ -596,7 +596,7 @@
|
|||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
ShowProgressIndicator();
|
||||
foreach(var Notification in notifications)
|
||||
{
|
||||
if (!Notification.IsDeleted)
|
||||
|
@ -612,7 +612,7 @@
|
|||
}
|
||||
await logger.LogInformation("Notifications Permanently Deleted");
|
||||
await LoadNotificationsAsync();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
@ -620,7 +620,7 @@
|
|||
{
|
||||
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,47 +2,102 @@
|
|||
@using System.Text.Json
|
||||
@inherits LocalizableComponent
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@if (_visible)
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
<div class="app-actiondialog">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Header</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="DisplayModal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@Message</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (!string.IsNullOrEmpty(Action))
|
||||
{
|
||||
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@SharedLocalizer["Cancel"]</button>
|
||||
@if (_visible)
|
||||
{
|
||||
<div class="app-actiondialog">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Header</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="DisplayModal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@Message</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (!string.IsNullOrEmpty(Action))
|
||||
{
|
||||
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@SharedLocalizer["Cancel"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_authorized)
|
||||
{
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
|
||||
}
|
||||
else
|
||||
@if (_authorized)
|
||||
{
|
||||
<button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_iconSpan) @Text</button>
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_iconSpan) @Text</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (_visible)
|
||||
{
|
||||
<div class="app-actiondialog">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Header</h5>
|
||||
<form method="post" @formname="@($"ActionDialogCloseForm{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="btn-close" aria-label="Close"></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@Message</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (!string.IsNullOrEmpty(Action))
|
||||
{
|
||||
<form method="post" @formname="@($"ActionDialogConfirmForm{Id}")" @onsubmit="Confirm" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="@Class">@((MarkupString)_iconSpan) @Text</button>
|
||||
</form>
|
||||
}
|
||||
<form method="post" @formname="@($"ActionDialogCancelForm{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="btn btn-secondary">@SharedLocalizer["Cancel"]</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_authorized)
|
||||
{
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form method="post" @formname="@($"ActionDialogActionForm{Id}")" @onsubmit="DisplayModal" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="@Class">@((MarkupString)_iconSpan) @Text</button>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _visible = false;
|
||||
private List<Permission> _permissions;
|
||||
private List<Permission> _permissions;
|
||||
private bool _editmode = false;
|
||||
private bool _authorized = false;
|
||||
private string _iconSpan = string.Empty;
|
||||
|
@ -62,11 +117,11 @@
|
|||
[Parameter]
|
||||
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
|
||||
|
||||
[Parameter]
|
||||
public string Permissions { get; set; } // deprecated - use PermissionList instead
|
||||
[Parameter]
|
||||
public string Permissions { get; set; } // deprecated - use PermissionList instead
|
||||
|
||||
[Parameter]
|
||||
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
|
||||
[Parameter]
|
||||
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
|
||||
|
||||
[Parameter]
|
||||
public string Class { get; set; } // optional
|
||||
|
@ -83,15 +138,18 @@
|
|||
[Parameter]
|
||||
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Permissions))
|
||||
{
|
||||
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
[Parameter]
|
||||
public string Id { get; set; } // optional - specifies a unique id for the compoment - required when there are multiple component instances on a page in static rendering
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Permissions))
|
||||
{
|
||||
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
|
@ -122,8 +180,13 @@
|
|||
Header = Localize(nameof(Header), Header);
|
||||
Message = Localize(nameof(Message), Message);
|
||||
|
||||
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||
_authorized = IsAuthorized();
|
||||
|
||||
if (PageState.QueryString.ContainsKey("dialog"))
|
||||
{
|
||||
_visible = (PageState.QueryString["dialog"] == Id);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAuthorized()
|
||||
|
@ -175,12 +238,22 @@
|
|||
private void DisplayModal()
|
||||
{
|
||||
_visible = !_visible;
|
||||
StateHasChanged();
|
||||
if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameters = new Dictionary<string, string>(PageState.QueryString);
|
||||
if (parameters.ContainsKey("dialog")) parameters.Remove("dialog");
|
||||
if (_visible) parameters.Add("dialog", Id);
|
||||
NavigationManager.NavigateTo(PageState.Route.AbsolutePath + Utilities.CreateQueryString(parameters));
|
||||
}
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
DisplayModal();
|
||||
OnClick();
|
||||
DisplayModal();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{
|
||||
if (Disabled)
|
||||
{
|
||||
<button type="button" class="@_classname" style="@_style" disabled>@((MarkupString)_iconSpan) @_text</button>
|
||||
<NavLink class="@($"{_classname} disabled")" href="@_url" style="@_style">@((MarkupString)_iconSpan) @_text</NavLink>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -97,10 +97,13 @@
|
|||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
_text = Action;
|
||||
if (!string.IsNullOrEmpty(Text))
|
||||
{
|
||||
_text = Text;
|
||||
_text = Localize(nameof(Text), _text);
|
||||
}
|
||||
else
|
||||
{
|
||||
_text = Localize(nameof(Action), Action);
|
||||
}
|
||||
|
||||
if (IconOnly && !string.IsNullOrEmpty(IconName))
|
||||
|
@ -150,7 +153,6 @@
|
|||
}
|
||||
|
||||
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
|
||||
_text = Localize(nameof(Text), _text);
|
||||
|
||||
_url = EditUrl(_path, _moduleId, Action, _parameters);
|
||||
if (!string.IsNullOrEmpty(ReturnUrl))
|
||||
|
|
|
@ -2,21 +2,27 @@
|
|||
@inherits ModuleControlBase
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
@if (!string.IsNullOrEmpty(Message))
|
||||
{
|
||||
<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))
|
||||
@((MarkupString)Message)
|
||||
@if (PageState != null)
|
||||
{
|
||||
@((MarkupString)" ")<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>
|
||||
}
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="DismissModal"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _message = string.Empty;
|
||||
private string _classname = string.Empty;
|
||||
private string _formname = "ModuleMessageForm";
|
||||
|
||||
[Parameter]
|
||||
public string Message { get; set; }
|
||||
|
@ -24,10 +30,32 @@
|
|||
[Parameter]
|
||||
public MessageType Type { get; set; }
|
||||
|
||||
public void RefreshMessage(string message, MessageType type)
|
||||
{
|
||||
Message = message;
|
||||
Type = type;
|
||||
|
||||
UpdateClassName();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (ModuleState != null)
|
||||
{
|
||||
_formname += ModuleState.PageModuleId.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_message = Message;
|
||||
if (!string.IsNullOrEmpty(_message))
|
||||
UpdateClassName();
|
||||
}
|
||||
|
||||
private void UpdateClassName()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Message))
|
||||
{
|
||||
_classname = GetMessageType(Type);
|
||||
}
|
||||
|
@ -57,7 +85,6 @@
|
|||
|
||||
private void DismissModal()
|
||||
{
|
||||
_message = "";
|
||||
StateHasChanged();
|
||||
Message = "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,63 +6,132 @@
|
|||
|
||||
@if (ItemList != null)
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(SearchProperties))
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
<div class="input-group my-3">
|
||||
<input id="search" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
<form autocomplete="off">
|
||||
<div class="input-group my-3">
|
||||
<input type="text" id="pagersearch" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
<form method="post" autocomplete="off" @formname="PagerForm" @onsubmit="Search" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<div class="input-group my-3">
|
||||
<input type="text" id="pagersearch" name="_search" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<button type="submit" class="btn btn-primary">@SharedLocalizer["Search"]</button>
|
||||
<a class="btn btn-secondary" href="@PageUrl(1, "")">@SharedLocalizer["Reset"]</a>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@if (Format == "Table" && Row != null)
|
||||
{
|
||||
<div class="table-responsive">
|
||||
|
@ -126,53 +195,106 @@
|
|||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(_startPage - 1, _search)"><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(((_page > 1) ? _page - 1 : _page), _search)"><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@for (int i = _startPage; i <= _endPage; i++)
|
||||
{
|
||||
var pager = i;
|
||||
if (pager == _page)
|
||||
{
|
||||
<li class="page-item app-pager-pointer active">
|
||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item app-pager-pointer">
|
||||
<a class="page-link" href="@PageUrl(pager, _search)">@pager</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(((_page < _pages) ? _page + 1 : _page), _search)"><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(_endPage + 1, _search)"><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
}
|
||||
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||
<a class="page-link" href="@PageUrl(_pages, _search)"><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,6 +359,12 @@
|
|||
[Parameter]
|
||||
public string SearchProperties { get; set; } // comma delimited list of property names to include in search
|
||||
|
||||
[Parameter]
|
||||
public string Parameters { get; set; } // optional - querystring parameters in the form of "id=x&name=y" used in static render mode
|
||||
|
||||
[SupplyParameterFromForm(FormName = "PagerForm")]
|
||||
public string _Search { get => ""; set => _search = value; }
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
|
@ -292,6 +420,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("search"))
|
||||
{
|
||||
_search = PageState.QueryString["search"];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
AllItems = Items; // only used in search
|
||||
|
@ -316,13 +449,20 @@
|
|||
_displayPages = int.Parse(DisplayPages);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(CurrentPage))
|
||||
if (PageState.QueryString.ContainsKey("page"))
|
||||
{
|
||||
_page = int.Parse(CurrentPage);
|
||||
_page = int.Parse(PageState.QueryString["page"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
_page = 1;
|
||||
if (!string.IsNullOrEmpty(CurrentPage))
|
||||
{
|
||||
_page = int.Parse(CurrentPage);
|
||||
}
|
||||
else
|
||||
{
|
||||
_page = 1;
|
||||
}
|
||||
}
|
||||
if (_page < 1) _page = 1;
|
||||
|
||||
|
@ -465,4 +605,25 @@
|
|||
}
|
||||
return string.Join(",", properties);
|
||||
}
|
||||
|
||||
private string PageUrl(int page, string search)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>(PageState.QueryString);
|
||||
if (parameters.ContainsKey("page")) parameters.Remove("page");
|
||||
parameters.Add("page", page.ToString());
|
||||
if (parameters.ContainsKey("search")) parameters.Remove("search");
|
||||
if (!string.IsNullOrEmpty(search))
|
||||
{
|
||||
parameters.Add("search", search);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Parameters))
|
||||
{
|
||||
foreach (var parameter in Utilities.ParseQueryString(Parameters))
|
||||
{
|
||||
if (parameters.ContainsKey(parameter.Key)) parameters.Remove(parameter.Key);
|
||||
parameters.Add(parameter.Key, parameter.Value);
|
||||
}
|
||||
}
|
||||
return PageState.Route.AbsolutePath + Utilities.CreateQueryString(parameters);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<div class="row" style="margin-bottom: 50px;">
|
||||
<div class="col">
|
||||
<TabStrip>
|
||||
<TabStrip ActiveTab="@_activetab">
|
||||
@if (AllowRichText)
|
||||
{
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||
|
@ -17,12 +17,6 @@
|
|||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>
|
||||
|
||||
@((MarkupString)" ")
|
||||
}
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
|
@ -85,7 +79,6 @@
|
|||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
|
||||
|
@ -121,6 +114,7 @@
|
|||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private string _activetab = "Rich";
|
||||
|
||||
[Parameter]
|
||||
public string Content { get; set; }
|
||||
|
@ -163,6 +157,12 @@
|
|||
_rawhtml = Content;
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
_originalrichhtml = "";
|
||||
|
||||
// Quill wraps content in <p> tags which can be used as a signal to set the active tab
|
||||
if (!string.IsNullOrEmpty(Content) && !Content.StartsWith("<p>") && AllowRawHtml)
|
||||
{
|
||||
_activetab = "Raw";
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
|
@ -208,19 +208,6 @@
|
|||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void RefreshRichText()
|
||||
{
|
||||
_richhtml = _rawhtml;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task RefreshRawHtml()
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
_rawhtml = await interop.GetHtml(_editorElement);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
// evaluate raw html content as first priority
|
||||
|
|
|
@ -4,19 +4,27 @@
|
|||
@inject IHtmlTextService HtmlTextService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@((MarkupString)content)
|
||||
|
||||
@if (PageState.EditMode)
|
||||
{
|
||||
<br />
|
||||
<ActionLink Action="Edit" EditMode="true" ResourceKey="Edit" />
|
||||
<br />
|
||||
<br />
|
||||
<div class="text-center mb-2">
|
||||
<ActionLink Action="Edit" EditMode="true" ResourceKey="Edit" />
|
||||
</div>
|
||||
}
|
||||
|
||||
@((MarkupString)content)
|
||||
|
||||
@if (PageState.EditMode && content.Length > 3000)
|
||||
{
|
||||
<div class="text-center mt-2">
|
||||
<ActionLink Action="Edit" EditMode="true" ResourceKey="Edit" />
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string content = "";
|
||||
|
||||
public override string RenderMode => RenderModes.Static;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
|
@ -9,7 +8,7 @@ using Oqtane.Shared;
|
|||
namespace Oqtane.Modules.HtmlText.Services
|
||||
{
|
||||
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
|
||||
public class HtmlTextService : ServiceBase, IHtmlTextService, IService
|
||||
public class HtmlTextService : ServiceBase, IHtmlTextService, IClientService
|
||||
{
|
||||
public HtmlTextService(HttpClient http, SiteState siteState) : base(http, siteState) {}
|
||||
|
||||
|
@ -30,9 +29,9 @@ namespace Oqtane.Modules.HtmlText.Services
|
|||
return await GetJsonAsync<Models.HtmlText>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlTextId}/{moduleId}", EntityNames.Module, moduleId));
|
||||
}
|
||||
|
||||
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||
public async Task<Models.HtmlText> AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||
{
|
||||
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
||||
return await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", EntityNames.Module, htmlText.ModuleId), htmlText);
|
||||
}
|
||||
|
||||
public async Task DeleteHtmlTextAsync(int htmlTextId, int moduleId)
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Oqtane.Modules.HtmlText.Services
|
|||
|
||||
Task<Models.HtmlText> GetHtmlTextAsync(int htmlTextId, int moduleId);
|
||||
|
||||
Task AddHtmlTextAsync(Models.HtmlText htmltext);
|
||||
Task<Models.HtmlText> AddHtmlTextAsync(Models.HtmlText htmltext);
|
||||
|
||||
Task DeleteHtmlTextAsync(int htmlTextId, int moduleId);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Oqtane.Modules
|
|||
protected Module ModuleState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public ModuleInstance ModuleInstance { get; set; }
|
||||
public RenderModeBoundary RenderModeBoundary { get; set; }
|
||||
|
||||
// optional interface properties
|
||||
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
|
||||
|
@ -50,6 +50,8 @@ namespace Oqtane.Modules
|
|||
|
||||
public virtual List<Resource> Resources { get; set; }
|
||||
|
||||
public virtual string RenderMode { get { return RenderModes.Interactive; } } // interactive by default
|
||||
|
||||
// url parameters
|
||||
public virtual string UrlParametersTemplate { get; set; }
|
||||
|
||||
|
@ -77,7 +79,7 @@ namespace Oqtane.Modules
|
|||
{
|
||||
if (PageState.Page.Resources != null)
|
||||
{
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site && item.Namespace == type.Namespace).ToList();
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Module && item.Namespace == type.Namespace).ToList();
|
||||
}
|
||||
}
|
||||
else // modulecontrolbase
|
||||
|
@ -87,22 +89,25 @@ 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>();
|
||||
var inline = 0;
|
||||
foreach (Resource resource in resources)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scripts.Any())
|
||||
|
@ -272,22 +277,22 @@ namespace Oqtane.Modules
|
|||
public void AddModuleMessage(string message, MessageType type, string position)
|
||||
{
|
||||
ClearModuleMessage();
|
||||
ModuleInstance.AddModuleMessage(message, type, position);
|
||||
RenderModeBoundary.AddModuleMessage(message, type, position);
|
||||
}
|
||||
|
||||
public void ClearModuleMessage()
|
||||
{
|
||||
ModuleInstance.AddModuleMessage("", MessageType.Undefined);
|
||||
RenderModeBoundary.AddModuleMessage("", MessageType.Undefined);
|
||||
}
|
||||
|
||||
public void ShowProgressIndicator()
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
RenderModeBoundary.ShowProgressIndicator();
|
||||
}
|
||||
|
||||
public void HideProgressIndicator()
|
||||
{
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
RenderModeBoundary.HideProgressIndicator();
|
||||
}
|
||||
|
||||
public void SetModuleTitle(string title)
|
||||
|
@ -487,5 +492,8 @@ namespace Oqtane.Modules
|
|||
{
|
||||
return Utilities.FileUrl(PageState.Alias, fileid, asAttachment);
|
||||
}
|
||||
|
||||
// Referencing ModuleInstance methods from ModuleBase is deprecated. Use the ModuleBase methods instead
|
||||
public ModuleInstance ModuleInstance { get { return new ModuleInstance(); } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>5.0.2</Version>
|
||||
<Version>5.1.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -12,21 +12,20 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
<IsPackable>true</IsPackable>
|
||||
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.1" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -41,10 +41,10 @@ namespace Oqtane.Client
|
|||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
// register auth services
|
||||
builder.Services.AddOqtaneAuthorization();
|
||||
builder.Services.AddOqtaneAuthentication();
|
||||
|
||||
// register scoped core services
|
||||
builder.Services.AddOqtaneScopedServices();
|
||||
builder.Services.AddOqtaneClientScopedServices();
|
||||
|
||||
var serviceProvider = builder.Services.BuildServiceProvider();
|
||||
|
||||
|
@ -220,6 +220,16 @@ namespace Oqtane.Client
|
|||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
implementationTypes = assembly.GetInterfaces<IClientService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
if (implementationType.AssemblyQualifiedName != null)
|
||||
{
|
||||
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
|
||||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Models;
|
||||
|
@ -16,12 +14,10 @@ namespace Oqtane.Providers
|
|||
public class IdentityAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly NavigationManager _navigationManager;
|
||||
|
||||
public IdentityAuthenticationStateProvider(IServiceProvider serviceProvider, NavigationManager navigationManager)
|
||||
public IdentityAuthenticationStateProvider(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_navigationManager = navigationManager;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -274,19 +274,19 @@
|
|||
<value>Select Theme</value>
|
||||
</data>
|
||||
<data name="Hosting.Heading" xml:space="preserve">
|
||||
<value>Hosting Model</value>
|
||||
<value>UI Component Settings</value>
|
||||
</data>
|
||||
<data name="Prerender.HelpText" xml:space="preserve">
|
||||
<value>Specifies if the site should be prerendered (for search crawlers, etc...)</value>
|
||||
<value>Specifies if interactive components should prerender their output</value>
|
||||
</data>
|
||||
<data name="Prerender.Text" xml:space="preserve">
|
||||
<value>Prerender? </value>
|
||||
</data>
|
||||
<data name="Runtime.HelpText" xml:space="preserve">
|
||||
<value>The Blazor runtime hosting model for the site</value>
|
||||
<data name="RenderMode.HelpText" xml:space="preserve">
|
||||
<value>The default render mode for the site</value>
|
||||
</data>
|
||||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Runtime: </value>
|
||||
<data name="RenderMode.Text" xml:space="preserve">
|
||||
<value>Render Mode:</value>
|
||||
</data>
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
|
@ -325,7 +325,7 @@
|
|||
<value>Default Alias: </value>
|
||||
</data>
|
||||
<data name="Aliases.Heading" xml:space="preserve">
|
||||
<value>Urls</value>
|
||||
<value>Site Urls</value>
|
||||
</data>
|
||||
<data name="AliasName" xml:space="preserve">
|
||||
<value>Url</value>
|
||||
|
@ -423,4 +423,10 @@
|
|||
<data name="HybridEnabled.Text" xml:space="preserve">
|
||||
<value>Hybrid Enabled?</value>
|
||||
</data>
|
||||
<data name="Runtime.HelpText" xml:space="preserve">
|
||||
<value>The render mode for UI components which require interactivity</value>
|
||||
</data>
|
||||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Interactivity:</value>
|
||||
</data>
|
||||
</root>
|
|
@ -222,17 +222,11 @@
|
|||
<data name="Error.Database.LoadConfig" xml:space="preserve">
|
||||
<value>Error loading Database Configuration Control</value>
|
||||
</data>
|
||||
<data name="Prerender.HelpText" xml:space="preserve">
|
||||
<value>Specifies if the site should be prerendered (for search crawlers, etc...)</value>
|
||||
<data name="RenderMode.HelpText" xml:space="preserve">
|
||||
<value>The default render mode for the site</value>
|
||||
</data>
|
||||
<data name="Prerender.Text" xml:space="preserve">
|
||||
<value>Prerender? </value>
|
||||
</data>
|
||||
<data name="Runtime.HelpText" xml:space="preserve">
|
||||
<value>The Blazor runtime hosting model</value>
|
||||
</data>
|
||||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Runtime: </value>
|
||||
<data name="RenderMode.Text" xml:space="preserve">
|
||||
<value>Render Mode: </value>
|
||||
</data>
|
||||
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||
|
@ -246,4 +240,10 @@
|
|||
<data name="EnterConnectionString" xml:space="preserve">
|
||||
<value>Enter Connection String</value>
|
||||
</data>
|
||||
<data name="Runtime.HelpText" xml:space="preserve">
|
||||
<value>The render mode for UI components which require interactivity</value>
|
||||
</data>
|
||||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Interactivity:</value>
|
||||
</data>
|
||||
</root>
|
|
@ -117,9 +117,6 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="SynchronizeContent" xml:space="preserve">
|
||||
<value>Synchronize Content</value>
|
||||
</data>
|
||||
<data name="InsertImage" xml:space="preserve">
|
||||
<value>Insert Image</value>
|
||||
</data>
|
||||
|
|
126
Oqtane.Client/Resources/Modules/Controls/StaticPager.resx
Normal file
126
Oqtane.Client/Resources/Modules/Controls/StaticPager.resx
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="PageOfPages" xml:space="preserve">
|
||||
<value>Page {0} of {1}</value>
|
||||
</data>
|
||||
<data name="SearchPlaceholder" xml:space="preserve">
|
||||
<value>Search: {0}</value>
|
||||
</data>
|
||||
</root>
|
|
@ -118,10 +118,10 @@
|
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Edit.Action" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<value>Edit Content</value>
|
||||
</data>
|
||||
<data name="Edit.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
<value>Edit Content</value>
|
||||
</data>
|
||||
<data name="Error.Content.Load" xml:space="preserve">
|
||||
<value>An Error Occurred Loading Content</value>
|
||||
|
|
|
@ -312,14 +312,14 @@
|
|||
<data name="Not Specified" xml:space="preserve">
|
||||
<value>Not Specified</value>
|
||||
</data>
|
||||
<data name="BlazorServer" xml:space="preserve">
|
||||
<value>Blazor Server</value>
|
||||
<data name="RuntimeServer" xml:space="preserve">
|
||||
<value>Server (SignalR)</value>
|
||||
</data>
|
||||
<data name="BlazorWebAssembly" xml:space="preserve">
|
||||
<value>Blazor WebAssembly</value>
|
||||
<data name="RuntimeWebAssembly" xml:space="preserve">
|
||||
<value>Client (WebAssembly)</value>
|
||||
</data>
|
||||
<data name="BlazorHybrid" xml:space="preserve">
|
||||
<value>Blazor Hybrid</value>
|
||||
<data name="StaticServer" xml:space="preserve">
|
||||
<value>Static Server Rendering</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
|
@ -441,4 +441,16 @@
|
|||
<data name="Message.EffectiveExpiryDateError" xml:space="preserve">
|
||||
<value>Effective Date cannot be after Expiry Date.</value>
|
||||
</data>
|
||||
<data name="RuntimeAuto" xml:space="preserve">
|
||||
<value>Auto</value>
|
||||
</data>
|
||||
<data name="RenderModeHeadless" xml:space="preserve">
|
||||
<value>Headless (API Only)</value>
|
||||
</data>
|
||||
<data name="RenderModeInteractive" xml:space="preserve">
|
||||
<value>Interactive</value>
|
||||
</data>
|
||||
<data name="RenderModeStatic" xml:space="preserve">
|
||||
<value>Static</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,5 +1,4 @@
|
|||
using Oqtane.Models;
|
||||
using Oqtane.UI;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Oqtane.Services
|
|||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly SiteState _siteState;
|
||||
private readonly IHttpClientFactory _factory;
|
||||
|
||||
protected ServiceBase(HttpClient httpClient, SiteState siteState)
|
||||
{
|
||||
|
@ -23,13 +24,31 @@ namespace Oqtane.Services
|
|||
_siteState = siteState;
|
||||
}
|
||||
|
||||
protected ServiceBase(IHttpClientFactory factory, SiteState siteState)
|
||||
{
|
||||
_factory = factory;
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
public HttpClient GetHttpClient()
|
||||
{
|
||||
if (!_httpClient.DefaultRequestHeaders.Contains(Constants.AntiForgeryTokenHeaderName) && _siteState != null && !string.IsNullOrEmpty(_siteState.AntiForgeryToken))
|
||||
if (_factory != null)
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Add(Constants.AntiForgeryTokenHeaderName, _siteState.AntiForgeryToken);
|
||||
var client = _factory.CreateClient("oqtane");
|
||||
if (!client.DefaultRequestHeaders.Contains(Constants.AntiForgeryTokenHeaderName) && _siteState != null && !string.IsNullOrEmpty(_siteState.AntiForgeryToken))
|
||||
{
|
||||
client.DefaultRequestHeaders.Add(Constants.AntiForgeryTokenHeaderName, _siteState.AntiForgeryToken);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_httpClient.DefaultRequestHeaders.Contains(Constants.AntiForgeryTokenHeaderName) && _siteState != null && !string.IsNullOrEmpty(_siteState.AntiForgeryToken))
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Add(Constants.AntiForgeryTokenHeaderName, _siteState.AntiForgeryToken);
|
||||
}
|
||||
return _httpClient;
|
||||
}
|
||||
return _httpClient;
|
||||
}
|
||||
|
||||
// should be used with new constructor
|
||||
|
|
|
@ -18,8 +18,7 @@ namespace Oqtane.Services
|
|||
|
||||
public async Task<List<Site>> GetSitesAsync()
|
||||
{
|
||||
List<Site> sites = await GetJsonAsync<List<Site>>(Apiurl);
|
||||
return sites.OrderBy(item => item.Name).ToList();
|
||||
return await GetJsonAsync<List<Site>>(Apiurl);
|
||||
}
|
||||
|
||||
public async Task<Site> GetSiteAsync(int siteId)
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><ModuleTitle /></h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="CloseModal"></button>
|
||||
<form method="post" class="app-form-inline" @formname="AdminContainerForm" @onsubmit="@CloseModal" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<button type="submit" class="btn-close" aria-label="Close"></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ModuleInstance />
|
||||
|
|
|
@ -31,9 +31,9 @@
|
|||
public override List<Resource> 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 = ThemePath() + "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", Location = ResourceLocation.Body }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +1,15 @@
|
|||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ModuleActionsBase
|
||||
@inherits ContainerBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
|
||||
{
|
||||
<div class="app-moduleactions py-2 px-3">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
|
||||
<ul class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);" role="button">
|
||||
@foreach (var action in Actions.Where(item => !item.Name.Contains("Pane")))
|
||||
{
|
||||
if (string.IsNullOrEmpty(action.Name))
|
||||
{
|
||||
<li class="dropdown-divider"></li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@if (Actions.Where(item => item.Name.Contains("Pane")).Any())
|
||||
{
|
||||
<li class="dropdown-submenu">
|
||||
<a class="dropdown-item" onclick="return subMenu(this)">
|
||||
<span class="@Icons.AccountLogin" aria-hidden="true"></span> Move To >
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var action in Actions.Where(item => item.Name.Contains("Pane")))
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
@if (PageState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
<ModuleActionsInteractive PageState="@PageState" ModuleState="@ModuleState" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<ModuleActionsInteractive PageState="@PageState" ModuleState="@ModuleState" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,18 +7,23 @@ using Oqtane.Models;
|
|||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
using System.Net;
|
||||
|
||||
// ReSharper disable UnassignedGetOnlyAutoProperty
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
namespace Oqtane.Themes.Controls
|
||||
{
|
||||
public class ModuleActionsBase : ContainerBase
|
||||
public class ModuleActionsBase : ComponentBase
|
||||
{
|
||||
[Inject] public NavigationManager NavigationManager { get; set; }
|
||||
[Inject] public IPageModuleService PageModuleService { get; set; }
|
||||
[Inject] public IModuleService ModuleService { get; set; }
|
||||
|
||||
[Parameter] public PageState PageState { get; set; }
|
||||
[Parameter] public Module ModuleState { get; set; }
|
||||
|
||||
public List<ActionViewModel> Actions;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
|
@ -88,7 +93,7 @@ namespace Oqtane.Themes.Controls
|
|||
private async Task<string> EditUrlAsync(string url, int moduleId, string import)
|
||||
{
|
||||
await Task.Yield();
|
||||
return EditUrl(moduleId, import);
|
||||
return Utilities.EditUrl(PageState.Alias.Path, PageState.Page.Path, moduleId, import, "");
|
||||
}
|
||||
|
||||
protected async Task ModuleAction(ActionViewModel action)
|
||||
|
@ -97,7 +102,7 @@ namespace Oqtane.Themes.Controls
|
|||
{
|
||||
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
|
||||
string url = NavigateUrl(true);
|
||||
string url = Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, "edit=true&refresh");
|
||||
|
||||
if (action.Action != null)
|
||||
{
|
||||
|
@ -130,7 +135,8 @@ namespace Oqtane.Themes.Controls
|
|||
private async Task<string> Settings(string url, PageModule pagemodule)
|
||||
{
|
||||
await Task.Yield();
|
||||
url = EditUrl(pagemodule.ModuleId, "Settings");
|
||||
var returnurl = Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, "edit=true");
|
||||
url = Utilities.EditUrl(PageState.Alias.Path, PageState.Page.Path, pagemodule.ModuleId, "Settings", "returnurl=" + WebUtility.UrlEncode(returnurl));
|
||||
return url;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ModuleActionsBase
|
||||
@attribute [OqtaneIgnore]
|
||||
|
||||
<div class="app-moduleactions py-2 px-3">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
|
||||
<ul class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);" role="button">
|
||||
@foreach (var action in Actions.Where(item => !item.Name.Contains("Pane")))
|
||||
{
|
||||
if (string.IsNullOrEmpty(action.Name))
|
||||
{
|
||||
<li class="dropdown-divider"></li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@if (Actions.Where(item => item.Name.Contains("Pane")).Any())
|
||||
{
|
||||
<li class="dropdown-submenu">
|
||||
<a class="dropdown-item" onclick="return subMenu(this)">
|
||||
<span class="@Icons.AccountLogin" aria-hidden="true"></span> Move To >
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var action in Actions.Where(item => item.Name.Contains("Pane")))
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
|
@ -4,6 +4,7 @@
|
|||
@attribute [OqtaneIgnore]
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject IStringLocalizerFactory LocalizerFactory
|
||||
@implements IDisposable
|
||||
|
||||
<span class="app-moduletitle">
|
||||
@((MarkupString)title)
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
@using System.Net
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IThemeService ThemeService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IPageService PageService
|
||||
@inject IPageModuleService PageModuleService
|
||||
@inject ILogService logger
|
||||
@inject ISettingService SettingService
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<ControlPanel> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@if (ShowLanguageSwitcher)
|
||||
{
|
||||
|
@ -22,223 +11,36 @@
|
|||
|
||||
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||
{
|
||||
if (PageState.EditMode)
|
||||
{
|
||||
<button type="button" class="btn @ButtonClass active" data-bs-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
|
||||
<span class="oi oi-pencil"></span>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn @ButtonClass" data-bs-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
|
||||
<span class="oi oi-pencil"></span>
|
||||
</button>
|
||||
}
|
||||
<form method="post" class="app-form-inline" @formname="EditModeForm" @onsubmit="@(async () => await ToggleEditMode(PageState.EditMode))" data-enhance>
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
@if (PageState.EditMode)
|
||||
{
|
||||
<button type="submit" class="btn @ButtonClass active" aria-pressed="true" autocomplete="off">
|
||||
<span class="oi oi-pencil"></span>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="submit" class="btn @ButtonClass" aria-pressed="false" autocomplete="off">
|
||||
<span class="oi oi-pencil"></span>
|
||||
</button>
|
||||
}
|
||||
</form>
|
||||
}
|
||||
|
||||
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
||||
<span class="oi oi-cog"></span>
|
||||
</button>
|
||||
|
||||
<div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel">
|
||||
<div class="@HeaderClass">
|
||||
<h5 id="offcanvasScrollingLabel" class="offcanvas-title">@Localizer["ControlPanel"]</h5>
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close" @onclick="ClearMessage"></button>
|
||||
</div>
|
||||
<div class="@BodyClass">
|
||||
<div class="container-fluid">
|
||||
@if (_canViewAdminDashboard)
|
||||
{
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
}
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label class="control-label">@Localizer["Page.Manage"] </label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex mb-2">
|
||||
<div class="col d-flex justify-content-between">
|
||||
@if (PageState.Page.UserId == null)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary col me-1" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary col" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
|
||||
<button type="button" class="btn btn-danger col ms-1" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
@if (UserSecurity.ContainsRole(PageState.Page.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
||||
{
|
||||
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("publish"))>@Localizer["Page.Publish"]</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
|
||||
@if (_deleteConfirmation)
|
||||
{
|
||||
<div class="app-admin-modal">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@Localizer["Confirm.Page.Delete"]</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Module" class="control-label">@Localizer["Module.Manage"]</label>
|
||||
<select class="form-select" @bind="@ModuleType">
|
||||
<option value="new">@Localizer["Module.AddNew"]</option>
|
||||
<option value="existing">@Localizer["Module.AddExisting"]</option>
|
||||
</select>
|
||||
@if (ModuleType == "new")
|
||||
{
|
||||
@if (_moduleDefinitions != null)
|
||||
{
|
||||
<select class="form-select mt-1" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == Category)
|
||||
{
|
||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@category">@category @Localizer["Modules"]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
<select class="form-select mt-1" @onchange="(e => ModuleChanged(e))">
|
||||
@if (ModuleDefinitionName == "-")
|
||||
{
|
||||
<option value="-" selected><@Localizer["Module.Select"]></option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="-"><@Localizer["Module.Select"]></option>
|
||||
}
|
||||
@foreach (var moduledefinition in _moduleDefinitions)
|
||||
{
|
||||
if (moduledefinition.IsEnabled && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.PermissionList))
|
||||
{
|
||||
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
|
||||
{
|
||||
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
|
||||
}
|
||||
}
|
||||
}
|
||||
</select>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="form-select mt-1" @onchange="(e => PageChanged(e))">
|
||||
<option value="-"><@Localizer["Page.Select"]></option>
|
||||
@foreach (Page p in _pages)
|
||||
{
|
||||
<option value="@p.PageId">@p.Name</option>
|
||||
}
|
||||
</select>
|
||||
<select class="form-select mt-1" @bind="@ModuleId">
|
||||
<option value="-"><@Localizer["Module.Select"]></option>
|
||||
@foreach (Module module in _modules)
|
||||
{
|
||||
<option value="@module.ModuleId">@module.Title</option>
|
||||
}
|
||||
</select>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Title" class="control-label">@Localizer["Title"]</label>
|
||||
<input type="text" name="Title" class="form-control" @bind="@Title" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Pane" class="control-label">@Localizer["Pane"]</label>
|
||||
<select class="form-select" @bind="@Pane">
|
||||
@foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
<option value="@pane">@pane Pane</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Insert" class="control-label">@Localizer["Location"]</label>
|
||||
<select class="form-select" @bind="@Location">
|
||||
<option value="@int.MinValue">@Localizer["LocationTop"]</option>
|
||||
<option value="@int.MaxValue">@Localizer["LocationBottom"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Container" class="control-label">@Localizer["Container"]</label>
|
||||
<select class="form-select" @bind="@ContainerType">
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="visibility" class="control-label">@Localizer["Visibility"]</label>
|
||||
<select class="form-select" @bind="@Visibility">
|
||||
<option value="view">@Localizer["VisibilityView"]</option>
|
||||
<option value="edit">@Localizer["VisibilityEdit"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
|
||||
@((MarkupString)Message)
|
||||
<hr class="app-rule" />
|
||||
}
|
||||
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-secondary col-12" @onclick=@(async () => await LogoutUser())>@Localizer["Logout"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (PageState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
<ControlPanelInteractive PageState="@PageState" SiteState="@SiteState" ButtonClass="@ButtonClass" ContainerClass="@ContainerClass" HeaderClass="@HeaderClass" BodyClass="@BodyClass" ShowLanguageSwitcher="@ShowLanguageSwitcher" LanguageDropdownAlignment="@LanguageDropdownAlignment" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<ControlPanelInteractive PageState="@PageState" SiteState="@SiteState" ButtonClass="@ButtonClass" ContainerClass="@ContainerClass" HeaderClass="@HeaderClass" BodyClass="@BodyClass" ShowLanguageSwitcher="@ShowLanguageSwitcher" LanguageDropdownAlignment="@LanguageDropdownAlignment" @rendermode="@InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, PageState.Site.Prerender)" />
|
||||
}
|
||||
}
|
||||
|
||||
@code{
|
||||
@code {
|
||||
[Parameter]
|
||||
public string ButtonClass { get; set; } = "btn-outline-secondary";
|
||||
|
||||
|
@ -259,84 +61,15 @@
|
|||
|
||||
private bool _canViewAdminDashboard = false;
|
||||
private bool _showEditMode = false;
|
||||
private bool _deleteConfirmation = false;
|
||||
private List<string> _categories = new List<string>();
|
||||
private List<ModuleDefinition> _allModuleDefinitions;
|
||||
private List<ModuleDefinition> _moduleDefinitions;
|
||||
private List<Page> _pages = new List<Page>();
|
||||
private List<Module> _modules = new List<Module>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _category = "Common";
|
||||
private string _pane = "";
|
||||
|
||||
protected string PageId { get; private set; } = "-";
|
||||
protected string ModuleId { get; private set; } = "-";
|
||||
protected string ModuleType { get; private set; } = "new";
|
||||
protected string ModuleDefinitionName { get; private set; } = "-";
|
||||
|
||||
protected string Title { get; private set; } = "";
|
||||
protected string ContainerType { get; private set; } = "";
|
||||
protected int Location { get; private set; } = int.MaxValue;
|
||||
protected string Visibility { get; private set; } = "view";
|
||||
protected string Message { get; private set; } = "";
|
||||
|
||||
private string settingCategory = "CP-category";
|
||||
private string settingPane = "CP-pane";
|
||||
|
||||
protected string Category
|
||||
{
|
||||
get => _category;
|
||||
private set
|
||||
{
|
||||
if (_category != value)
|
||||
{
|
||||
_category = value;
|
||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
|
||||
ModuleDefinitionName = "-";
|
||||
Message = "";
|
||||
StateHasChanged();
|
||||
_ = UpdateSettingsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected string Pane
|
||||
{
|
||||
get => _pane;
|
||||
private set
|
||||
{
|
||||
if (_pane != value)
|
||||
{
|
||||
_pane = value;
|
||||
_ = UpdateSettingsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_canViewAdminDashboard = CanViewAdminDashboard();
|
||||
|
||||
_showEditMode = false;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
_showEditMode = true;
|
||||
LoadSettingsAsync();
|
||||
|
||||
_pages?.Clear();
|
||||
foreach (Page p in PageState.Pages)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||
{
|
||||
_pages.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
_containers = ThemeService.GetContainerControls(themes, PageState.Page.ThemeType);
|
||||
ContainerType = PageState.Site.DefaultContainerType;
|
||||
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
|
||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -347,7 +80,7 @@
|
|||
_showEditMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,128 +100,7 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
private void CategoryChanged(ChangeEventArgs e)
|
||||
{
|
||||
Category = (string)e.Value;
|
||||
}
|
||||
|
||||
private void ModuleChanged(ChangeEventArgs e)
|
||||
{
|
||||
ModuleDefinitionName = (string)e.Value;
|
||||
if (ModuleDefinitionName != "-")
|
||||
{
|
||||
var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName);
|
||||
Message = "<div class=\"alert alert-info mt-2 text-center\" role=\"alert\">" + moduleDefinition.Description + "</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = "";
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void PageChanged(ChangeEventArgs e)
|
||||
{
|
||||
PageId = (string)e.Value;
|
||||
if (PageId != "-")
|
||||
{
|
||||
_modules = PageState.Modules
|
||||
.Where(module => module.PageId == int.Parse(PageId) &&
|
||||
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
|
||||
.ToList();
|
||||
}
|
||||
ModuleId = "-";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task AddModule()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
if ((ModuleType == "new" && ModuleDefinitionName != "-") || (ModuleType != "new" && ModuleId != "-"))
|
||||
{
|
||||
if (ModuleType == "new")
|
||||
{
|
||||
Module module = new Module();
|
||||
module.SiteId = PageState.Site.SiteId;
|
||||
module.PageId = PageState.Page.PageId;
|
||||
module.ModuleDefinitionName = ModuleDefinitionName;
|
||||
module.AllPages = false;
|
||||
|
||||
var permissions = new List<Permission>();
|
||||
if (Visibility == "view")
|
||||
{
|
||||
// set module view permissions to page view permissions
|
||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.View);
|
||||
}
|
||||
else
|
||||
{
|
||||
// set module view permissions to page edit permissions
|
||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.Edit);
|
||||
}
|
||||
// set module edit permissions to page edit permissions
|
||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.Edit, PermissionNames.Edit);
|
||||
module.PermissionList = permissions;
|
||||
|
||||
module = await ModuleService.AddModuleAsync(module);
|
||||
ModuleId = module.ModuleId.ToString();
|
||||
}
|
||||
|
||||
var pageModule = new PageModule
|
||||
{
|
||||
PageId = PageState.Page.PageId,
|
||||
ModuleId = int.Parse(ModuleId),
|
||||
Title = Title
|
||||
};
|
||||
if (pageModule.Title == "")
|
||||
{
|
||||
if (ModuleType == "new")
|
||||
{
|
||||
pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName)?.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(ModuleId))?.Title;
|
||||
}
|
||||
}
|
||||
|
||||
pageModule.Pane = Pane;
|
||||
pageModule.Order = Location;
|
||||
pageModule.ContainerType = ContainerType;
|
||||
|
||||
if (pageModule.ContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
pageModule.ContainerType = "";
|
||||
}
|
||||
|
||||
await PageModuleService.AddPageModuleAsync(pageModule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
|
||||
|
||||
Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
||||
Title = "";
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = $"<div class=\"alert alert-warning mt-2 text-center\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
|
||||
}
|
||||
}
|
||||
|
||||
private List<Permission> SetPermissions(List<Permission> permissions, int siteId, string modulePermission, string pagePermission)
|
||||
{
|
||||
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
|
||||
{
|
||||
permissions.Add(new Permission { SiteId = siteId, EntityName = EntityNames.Module, PermissionName = modulePermission, RoleId = permission.RoleId, UserId = permission.UserId, IsAuthorized = permission.IsAuthorized });
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
private async Task ToggleEditMode(bool EditMode)
|
||||
private async Task ToggleEditMode(bool editMode)
|
||||
{
|
||||
Page page = null;
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
||||
|
@ -498,13 +110,13 @@
|
|||
|
||||
if (_showEditMode)
|
||||
{
|
||||
if (EditMode)
|
||||
PageState.EditMode = !editMode;
|
||||
|
||||
if (PageState.User != null)
|
||||
{
|
||||
PageState.EditMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PageState.EditMode = true;
|
||||
// preserve edit mode for authenticated users
|
||||
var userSettings = new Dictionary<string, string> { { "CP-editmode", (PageState.EditMode) ? PageState.Page.PageId.ToString() : "-1" } };
|
||||
await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId);
|
||||
}
|
||||
|
||||
// preserve other querystring parameters
|
||||
|
@ -512,190 +124,14 @@
|
|||
PageState.QueryString.Add("edit", PageState.EditMode.ToString().ToLower());
|
||||
var url = PageState.Route.AbsolutePath + Utilities.CreateQueryString(PageState.QueryString);
|
||||
NavigationManager.NavigateTo(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
||||
{
|
||||
PageState.EditMode = true;
|
||||
NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Navigate(string location)
|
||||
{
|
||||
Module module;
|
||||
switch (location)
|
||||
{
|
||||
case "Admin":
|
||||
// get admin dashboard moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||
if (module != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl("admin", module.ModuleId, "Index", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||
}
|
||||
break;
|
||||
case "Add":
|
||||
case "Edit":
|
||||
string url = "";
|
||||
// get page management moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
|
||||
|
||||
if (module != null)
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
case "Add":
|
||||
url = EditUrl("admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}");
|
||||
break;
|
||||
case "Edit":
|
||||
url = EditUrl("admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (url != "")
|
||||
{
|
||||
NavigationManager.NavigateTo(url);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void Publish(string action)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
var permissions = PageState.Page.PermissionList;
|
||||
switch (action)
|
||||
{
|
||||
case "publish":
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
|
||||
}
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
|
||||
}
|
||||
break;
|
||||
case "unpublish":
|
||||
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.RemoveAll(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone);
|
||||
}
|
||||
|
||||
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.RemoveAll(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
PageState.Page.PermissionList = permissions;
|
||||
await PageService.UpdatePageAsync(PageState.Page);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfirmDelete()
|
||||
{
|
||||
_deleteConfirmation = !_deleteConfirmation;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeletePage()
|
||||
{
|
||||
ConfirmDelete();
|
||||
|
||||
var page = PageState.Page;
|
||||
try
|
||||
{
|
||||
if (page.UserId == null)
|
||||
{
|
||||
page.IsDeleted = true;
|
||||
await PageService.UpdatePageAsync(page);
|
||||
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||
NavigationManager.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
else // personalized page
|
||||
{
|
||||
await PageService.DeletePageAsync(page.PageId);
|
||||
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// the following code is duplicated from LoginBase
|
||||
private async Task LogoutUser()
|
||||
{
|
||||
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username);
|
||||
|
||||
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
|
||||
var url = route.PathAndQuery;
|
||||
|
||||
// verify if anonymous users can access page
|
||||
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList))
|
||||
{
|
||||
url = PageState.Alias.Path;
|
||||
}
|
||||
|
||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive logout
|
||||
await UserService.LogoutUserAsync(PageState.User);
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
||||
{
|
||||
PageState.EditMode = true;
|
||||
NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSettingsAsync()
|
||||
{
|
||||
_category = SettingService.GetSetting(PageState.User.Settings, settingCategory, "Common");
|
||||
var pane = SettingService.GetSetting(PageState.User.Settings, settingPane, "");
|
||||
if (PageState.Page.Panes.Contains(pane))
|
||||
{
|
||||
_pane = pane;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.Page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||
{
|
||||
_pane = PaneNames.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pane = PaneNames.Admin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
settings = SettingService.SetSetting(settings, settingCategory, _category);
|
||||
settings = SettingService.SetSetting(settings, settingPane, _pane);
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
}
|
||||
|
||||
private void ClearMessage()
|
||||
{
|
||||
|
||||
Message = "";
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,604 @@
|
|||
@using System.Net
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject SiteState ComponentSiteState
|
||||
@inject IUserService UserService
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IThemeService ThemeService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IPageService PageService
|
||||
@inject IPageModuleService PageModuleService
|
||||
@inject ILogService logger
|
||||
@inject ISettingService SettingService
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject ILogService LoggingService
|
||||
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
||||
<span class="oi oi-cog"></span>
|
||||
</button>
|
||||
|
||||
<div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel">
|
||||
<div class="@HeaderClass">
|
||||
<h5 id="offcanvasScrollingLabel" class="offcanvas-title">@Localizer["ControlPanel"]</h5>
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close" @onclick="ClearMessage"></button>
|
||||
</div>
|
||||
<div class="@BodyClass">
|
||||
<div class="container-fluid">
|
||||
@if (_canViewAdminDashboard)
|
||||
{
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
}
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label class="control-label">@Localizer["Page.Manage"] </label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex mb-2">
|
||||
<div class="col d-flex justify-content-between">
|
||||
@if (PageState.Page.UserId == null)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary col me-1" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary col" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
|
||||
<button type="button" class="btn btn-danger col ms-1" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
@if (UserSecurity.ContainsRole(PageState.Page.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
||||
{
|
||||
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("publish"))>@Localizer["Page.Publish"]</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
|
||||
@if (_deleteConfirmation)
|
||||
{
|
||||
<div class="app-admin-modal">
|
||||
<div class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@Localizer["Confirm.Page.Delete"]</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Module" class="control-label">@Localizer["Module.Manage"]</label>
|
||||
<select class="form-select" @bind="@_moduleType">
|
||||
<option value="new">@Localizer["Module.AddNew"]</option>
|
||||
<option value="existing">@Localizer["Module.AddExisting"]</option>
|
||||
</select>
|
||||
@if (_moduleType == "new")
|
||||
{
|
||||
@if (_moduleDefinitions != null)
|
||||
{
|
||||
<select class="form-select mt-1" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == _category)
|
||||
{
|
||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@category">@category @Localizer["Modules"]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
<select class="form-select mt-1" @onchange="(e => ModuleChanged(e))">
|
||||
@if (_moduleDefinitionName == "-")
|
||||
{
|
||||
<option value="-" selected><@Localizer["Module.Select"]></option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="-"><@Localizer["Module.Select"]></option>
|
||||
}
|
||||
@foreach (var moduledefinition in _moduleDefinitions)
|
||||
{
|
||||
if (moduledefinition.IsEnabled && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.PermissionList))
|
||||
{
|
||||
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
|
||||
{
|
||||
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
|
||||
}
|
||||
}
|
||||
}
|
||||
</select>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="form-select mt-1" @onchange="(e => PageChanged(e))">
|
||||
<option value="-"><@Localizer["Page.Select"]></option>
|
||||
@foreach (Page p in _pages)
|
||||
{
|
||||
<option value="@p.PageId">@p.Name</option>
|
||||
}
|
||||
</select>
|
||||
<select class="form-select mt-1" @bind="@_moduleId">
|
||||
<option value="-"><@Localizer["Module.Select"]></option>
|
||||
@foreach (Module module in _modules)
|
||||
{
|
||||
<option value="@module.ModuleId">@module.Title</option>
|
||||
}
|
||||
</select>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Title" class="control-label">@Localizer["Title"]</label>
|
||||
<input type="text" name="Title" class="form-control" @bind="@_title" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Pane" class="control-label">@Localizer["Pane"]</label>
|
||||
<select class="form-select" @bind="@_pane">
|
||||
@foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
<option value="@pane">@pane Pane</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Insert" class="control-label">@Localizer["Location"]</label>
|
||||
<select class="form-select" @bind="@_location">
|
||||
<option value="@int.MinValue">@Localizer["LocationTop"]</option>
|
||||
<option value="@int.MaxValue">@Localizer["LocationBottom"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Container" class="control-label">@Localizer["Container"]</label>
|
||||
<select class="form-select" @bind="@_containerType">
|
||||
@if (_containers != null)
|
||||
{
|
||||
foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="visibility" class="control-label">@Localizer["Visibility"]</label>
|
||||
<select class="form-select" @bind="@_visibility">
|
||||
<option value="view">@Localizer["VisibilityView"]</option>
|
||||
<option value="edit">@Localizer["VisibilityEdit"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
|
||||
@((MarkupString)_message)
|
||||
<hr class="app-rule" />
|
||||
}
|
||||
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-secondary col-12" @onclick=@(async () => await LogoutUser())>@Localizer["Logout"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public SiteState SiteState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public PageState PageState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string ButtonClass { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string ContainerClass { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string HeaderClass { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string BodyClass { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ShowLanguageSwitcher { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string LanguageDropdownAlignment { get; set; }
|
||||
|
||||
private bool _canViewAdminDashboard = false;
|
||||
private bool _deleteConfirmation = false;
|
||||
private List<string> _categories = new List<string>();
|
||||
private List<ModuleDefinition> _allModuleDefinitions;
|
||||
private List<ModuleDefinition> _moduleDefinitions;
|
||||
private List<Page> _pages = new List<Page>();
|
||||
private List<Module> _modules = new List<Module>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
|
||||
private string _category = "Common";
|
||||
private string _pane = "";
|
||||
protected string _pageId { get; private set; } = "-";
|
||||
protected string _moduleId { get; private set; } = "-";
|
||||
protected string _moduleType { get; private set; } = "new";
|
||||
protected string _moduleDefinitionName { get; private set; } = "-";
|
||||
|
||||
protected string _title { get; private set; } = "";
|
||||
protected string _containerType { get; private set; } = "";
|
||||
protected int _location { get; private set; } = int.MaxValue;
|
||||
protected string _visibility { get; private set; } = "view";
|
||||
protected string _message { get; private set; } = "";
|
||||
|
||||
private string settingCategory = "CP-category";
|
||||
private string settingPane = "CP-pane";
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// repopulate the SiteState service based on the values passed in the SiteState parameter (this is how state is marshalled across the render mode boundary)
|
||||
ComponentSiteState.Hydrate(SiteState);
|
||||
|
||||
_canViewAdminDashboard = CanViewAdminDashboard();
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
LoadSettingsAsync();
|
||||
|
||||
_pages?.Clear();
|
||||
foreach (Page p in PageState.Pages)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||
{
|
||||
_pages.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
||||
_containerType = PageState.Site.DefaultContainerType;
|
||||
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
|
||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanViewAdminDashboard()
|
||||
{
|
||||
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||
if (admin != null)
|
||||
{
|
||||
foreach (var page in PageState.Pages.Where(item => item.ParentId == admin?.PageId))
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CategoryChanged(ChangeEventArgs e)
|
||||
{
|
||||
_category = (string)e.Value;
|
||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
|
||||
_moduleDefinitionName = "-";
|
||||
_message = "";
|
||||
}
|
||||
|
||||
private void ModuleChanged(ChangeEventArgs e)
|
||||
{
|
||||
_moduleDefinitionName = (string)e.Value;
|
||||
if (_moduleDefinitionName != "-")
|
||||
{
|
||||
var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName);
|
||||
_message = "<div class=\"alert alert-info mt-2 text-center\" role=\"alert\">" + moduleDefinition.Description + "</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = "";
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void PageChanged(ChangeEventArgs e)
|
||||
{
|
||||
_pageId = (string)e.Value;
|
||||
if (_pageId != "-")
|
||||
{
|
||||
_modules = PageState.Modules
|
||||
.Where(module => module.PageId == int.Parse(_pageId) &&
|
||||
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
|
||||
.ToList();
|
||||
}
|
||||
_moduleId = "-";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task AddModule()
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
if ((_moduleType == "new" && _moduleDefinitionName != "-") || (_moduleType != "new" && _moduleId != "-"))
|
||||
{
|
||||
if (_moduleType == "new")
|
||||
{
|
||||
Module module = new Module();
|
||||
module.SiteId = PageState.Site.SiteId;
|
||||
module.PageId = PageState.Page.PageId;
|
||||
module.ModuleDefinitionName = _moduleDefinitionName;
|
||||
module.AllPages = false;
|
||||
|
||||
var permissions = new List<Permission>();
|
||||
if (_visibility == "view")
|
||||
{
|
||||
// set module view permissions to page view permissions
|
||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.View);
|
||||
}
|
||||
else
|
||||
{
|
||||
// set module view permissions to page edit permissions
|
||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.Edit);
|
||||
}
|
||||
// set module edit permissions to page edit permissions
|
||||
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.Edit, PermissionNames.Edit);
|
||||
module.PermissionList = permissions;
|
||||
|
||||
module = await ModuleService.AddModuleAsync(module);
|
||||
_moduleId = module.ModuleId.ToString();
|
||||
}
|
||||
|
||||
var pageModule = new PageModule
|
||||
{
|
||||
PageId = PageState.Page.PageId,
|
||||
ModuleId = int.Parse(_moduleId),
|
||||
Title = _title
|
||||
};
|
||||
if (pageModule.Title == "")
|
||||
{
|
||||
if (_moduleType == "new")
|
||||
{
|
||||
pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName)?.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(_moduleId))?.Title;
|
||||
}
|
||||
}
|
||||
|
||||
pageModule.Pane = _pane;
|
||||
pageModule.Order = _location;
|
||||
pageModule.ContainerType = _containerType;
|
||||
|
||||
if (pageModule.ContainerType == PageState.Site.DefaultContainerType)
|
||||
{
|
||||
pageModule.ContainerType = "";
|
||||
}
|
||||
|
||||
await PageModuleService.AddPageModuleAsync(pageModule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
|
||||
await UpdateSettingsAsync();
|
||||
|
||||
_message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
||||
_title = "";
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = $"<div class=\"alert alert-warning mt-2 text-center\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
|
||||
}
|
||||
}
|
||||
|
||||
private List<Permission> SetPermissions(List<Permission> permissions, int siteId, string modulePermission, string pagePermission)
|
||||
{
|
||||
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
|
||||
{
|
||||
permissions.Add(new Permission { SiteId = siteId, EntityName = EntityNames.Module, PermissionName = modulePermission, RoleId = permission.RoleId, UserId = permission.UserId, IsAuthorized = permission.IsAuthorized });
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
private void Navigate(string location)
|
||||
{
|
||||
Module module;
|
||||
switch (location)
|
||||
{
|
||||
case "Admin":
|
||||
// get admin dashboard moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||
if (module != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin", module.ModuleId, "Index", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||
}
|
||||
break;
|
||||
case "Add":
|
||||
case "Edit":
|
||||
string url = "";
|
||||
// get page management moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
|
||||
if (module != null)
|
||||
{
|
||||
url = Utilities.EditUrl(PageState.Alias.Path, "admin/pages", module.ModuleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}");
|
||||
NavigationManager.NavigateTo(url);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void Publish(string action)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
var permissions = PageState.Page.PermissionList;
|
||||
switch (action)
|
||||
{
|
||||
case "publish":
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
|
||||
}
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
|
||||
}
|
||||
break;
|
||||
case "unpublish":
|
||||
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.RemoveAll(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone);
|
||||
}
|
||||
|
||||
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.RemoveAll(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
PageState.Page.PermissionList = permissions;
|
||||
await PageService.UpdatePageAsync(PageState.Page);
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, "refresh"));
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfirmDelete()
|
||||
{
|
||||
_deleteConfirmation = !_deleteConfirmation;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeletePage()
|
||||
{
|
||||
ConfirmDelete();
|
||||
|
||||
var page = PageState.Page;
|
||||
try
|
||||
{
|
||||
if (page.UserId == null)
|
||||
{
|
||||
page.IsDeleted = true;
|
||||
await PageService.UpdatePageAsync(page);
|
||||
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "", ""));
|
||||
}
|
||||
else // personalized page
|
||||
{
|
||||
await PageService.DeletePageAsync(page.PageId);
|
||||
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, PageState.Page.Path, ""));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// the following code is duplicated from LoginBase
|
||||
private async Task LogoutUser()
|
||||
{
|
||||
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username);
|
||||
|
||||
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
|
||||
var url = route.PathAndQuery;
|
||||
|
||||
// verify if anonymous users can access page
|
||||
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList))
|
||||
{
|
||||
url = PageState.Alias.Path;
|
||||
}
|
||||
|
||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||
{
|
||||
if (PageState.User != null)
|
||||
{
|
||||
// hybrid apps utilize an interactive logout
|
||||
await UserService.LogoutUserAsync(PageState.User);
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSettingsAsync()
|
||||
{
|
||||
_category = SettingService.GetSetting(PageState.User?.Settings, settingCategory, "Common");
|
||||
var pane = SettingService.GetSetting(PageState.User?.Settings, settingPane, "");
|
||||
if (PageState.Page.Panes.Contains(pane))
|
||||
{
|
||||
_pane = pane;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.Page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||
{
|
||||
_pane = PaneNames.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pane = PaneNames.Admin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateSettingsAsync()
|
||||
{
|
||||
if (PageState.User != null)
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
settings = SettingService.SetSetting(settings, settingCategory, _category);
|
||||
settings = SettingService.SetSetting(settings, settingPane, _pane);
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearMessage()
|
||||
{
|
||||
_message = "";
|
||||
}
|
||||
}
|
|
@ -9,12 +9,23 @@
|
|||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
|
||||
@if (PageState.Runtime == Runtime.Hybrid)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form method="post" class="app-form-inline" action="@logouturl" @formname="LogoutForm">
|
||||
<input type="hidden" name="__RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<input type="hidden" name="returnurl" value="@returnurl" />
|
||||
<button type="submit" class="btn btn-primary">@Localizer["Logout"]</button>
|
||||
</form>
|
||||
}
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
@if (ShowLogin)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="LoginUser">@SharedLocalizer["Login"]</button>
|
||||
<a href="@loginurl" class="btn btn-primary">@SharedLocalizer["Login"]</a>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
|
|
@ -21,30 +21,65 @@ namespace Oqtane.Themes.Controls
|
|||
[Inject] public IJSRuntime jsRuntime { get; set; }
|
||||
[Inject] public IServiceProvider ServiceProvider { get; set; }
|
||||
|
||||
protected void LoginUser()
|
||||
private bool allowexternallogin;
|
||||
private bool allowsitelogin;
|
||||
protected string loginurl;
|
||||
protected string logouturl;
|
||||
protected string returnurl;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
var allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
var allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
|
||||
var returnurl = "";
|
||||
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery); // remember current url
|
||||
}
|
||||
else
|
||||
{
|
||||
returnurl = PageState.QueryString["returnurl"]; // use existing value
|
||||
}
|
||||
allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
|
||||
// set login url
|
||||
if (allowexternallogin && !allowsitelogin)
|
||||
{
|
||||
// external login
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + returnurl), true);
|
||||
loginurl = Utilities.TenantUrl(PageState.Alias, "/pages/external");
|
||||
}
|
||||
else
|
||||
{
|
||||
// local login
|
||||
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
|
||||
loginurl = NavigateUrl("login");
|
||||
}
|
||||
|
||||
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
// remember current url
|
||||
loginurl += "?returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use existing value
|
||||
loginurl += "?returnurl=" + PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
// set logout url
|
||||
logouturl = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
||||
|
||||
// verify anonymous users can access current page
|
||||
if (UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList) && Utilities.IsPageModuleVisible(PageState.Page.EffectiveDate, PageState.Page.ExpiryDate))
|
||||
{
|
||||
returnurl = PageState.Route.PathAndQuery;
|
||||
}
|
||||
else
|
||||
{
|
||||
returnurl = PageState.Alias.Path;
|
||||
}
|
||||
}
|
||||
|
||||
protected void LoginUser()
|
||||
{
|
||||
if (allowexternallogin && !allowsitelogin)
|
||||
{
|
||||
// external login
|
||||
NavigationManager.NavigateTo(loginurl, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// local login
|
||||
NavigationManager.NavigateTo(loginurl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,30 +87,20 @@ namespace Oqtane.Themes.Controls
|
|||
{
|
||||
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username);
|
||||
|
||||
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
|
||||
var url = route.PathAndQuery;
|
||||
|
||||
// verify if anonymous users can access page
|
||||
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList) || !Utilities.IsPageModuleVisible(PageState.Page.EffectiveDate, PageState.Page.ExpiryDate))
|
||||
{
|
||||
url = PageState.Alias.Path;
|
||||
}
|
||||
|
||||
|
||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||
if (PageState.Runtime == Runtime.Hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive logout
|
||||
await UserService.LogoutUserAsync(PageState.User);
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
NavigationManager.NavigateTo(returnurl, true);
|
||||
}
|
||||
else
|
||||
else // this condition is only valid for legacy Login button inheriting from LoginBase
|
||||
{
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = returnurl };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||
await interop.SubmitForm(logouturl, fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,11 @@
|
|||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@inject IFileService FileService
|
||||
|
||||
@if (file != null)
|
||||
@if (PageState.Site.LogoFileId != null)
|
||||
{
|
||||
<span class="app-logo">
|
||||
<a href="@PageState.Alias.Path">
|
||||
<img class="img-fluid" src="@file.Url" alt="@PageState.Site.Name" />
|
||||
<img class="img-fluid" src="@Utilities.FileUrl(PageState.Alias, PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name" />
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
|
||||
@code {
|
||||
private File file = null;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PageState.Site.LogoFileId != null && file?.FileId != PageState.Site.LogoFileId.Value)
|
||||
{
|
||||
file = await FileService.GetFileAsync(PageState.Site.LogoFileId.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,9 +6,16 @@
|
|||
<div class="dropdown-menu" aria-labelledby="@($"navbarDropdown{ParentPage.PageId}")">
|
||||
@foreach (var childPage in GetChildPages())
|
||||
{
|
||||
var _attributes = new Dictionary<string, object>();
|
||||
_attributes.Add("href", GetUrl(childPage));
|
||||
var _target = GetTarget(childPage);
|
||||
if (!string.IsNullOrEmpty(_target))
|
||||
{
|
||||
_attributes.Add("target", _target);
|
||||
}
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<a class="nav-link active px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link active px-3" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
|
@ -17,7 +24,7 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
<a class="nav-link px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link px-3" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
|
@ -32,12 +39,19 @@ else
|
|||
<ul class="navbar-nav mr-auto">
|
||||
@foreach (var childPage in GetChildPages())
|
||||
{
|
||||
var _attributes = new Dictionary<string, object>();
|
||||
_attributes.Add("href", GetUrl(childPage));
|
||||
var _target = GetTarget(childPage);
|
||||
if (!string.IsNullOrEmpty(_target))
|
||||
{
|
||||
_attributes.Add("target", _target);
|
||||
}
|
||||
if (!Pages.Any(e => e.ParentId == childPage.PageId))
|
||||
{
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link active" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
|
@ -48,7 +62,7 @@ else
|
|||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
|
@ -62,7 +76,7 @@ else
|
|||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item dropdown active">
|
||||
<a class="nav-link dropdown-toggle" href="@GetUrl(childPage)" target="@GetTarget(childPage)" id="@($"navbarDropdown{childPage.PageId}")" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<a class="nav-link dropdown-toggle" id="@($"navbarDropdown{childPage.PageId}")" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @attributes="_attributes">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
</a>
|
||||
|
@ -72,7 +86,7 @@ else
|
|||
else
|
||||
{
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="@GetUrl(childPage)" target="@GetTarget(childPage)" id="@($"navbarDropdown{childPage.PageId}")" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<a class="nav-link dropdown-toggle" id="@($"navbarDropdown{childPage.PageId}")" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @attributes="_attributes">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
</a>
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
{
|
||||
foreach (var childPage in GetChildPages())
|
||||
{
|
||||
var _attributes = new Dictionary<string, object>();
|
||||
_attributes.Add("href", GetUrl(childPage));
|
||||
var _target = GetTarget(childPage);
|
||||
if (!string.IsNullOrEmpty(_target))
|
||||
{
|
||||
_attributes.Add("target", _target);
|
||||
}
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link active" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
|
@ -19,7 +26,7 @@
|
|||
else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
|
@ -38,10 +45,17 @@ else
|
|||
<ul class="nav flex-column">
|
||||
@foreach (var childPage in GetChildPages())
|
||||
{
|
||||
var _attributes = new Dictionary<string, object>();
|
||||
_attributes.Add("href", GetUrl(childPage));
|
||||
var _target = GetTarget(childPage);
|
||||
if (!string.IsNullOrEmpty(_target))
|
||||
{
|
||||
_attributes.Add("target", _target);
|
||||
}
|
||||
if (childPage.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link active" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link active" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name <span class="visually-hidden-focusable">(current)</span>
|
||||
|
@ -52,7 +66,7 @@ else
|
|||
else
|
||||
{
|
||||
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
|
||||
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
|
||||
<a class="nav-link" @attributes="_attributes">
|
||||
<span class="w-100" data-bs-toggle="collapse" data-bs-target=".navbar-collapse.show">
|
||||
<span class="@childPage.Icon" aria-hidden="true" />
|
||||
@childPage.Name
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="UpdateProfile">@context.User.Identity.Name</button>
|
||||
<a href="@NavigateUrl("profile", "returnurl=" + _returnurl)" class="btn btn-primary">@context.User.Identity.Name</a>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
@if (ShowRegister && PageState.Site.AllowRegistration)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="RegisterUser">@Localizer["Register"]</button>
|
||||
<a href="@NavigateUrl("register", "returnurl=" + _returnurl)" class="btn btn-primary">@Localizer["Register"]</a>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
@ -31,17 +31,16 @@
|
|||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
|
||||
private void RegisterUser()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("register", "returnurl=" + _returnurl));
|
||||
}
|
||||
|
||||
private void UpdateProfile()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("profile", "returnurl=" + _returnurl));
|
||||
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
// remember current url
|
||||
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use existing value
|
||||
_returnurl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ 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" },
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
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", Location = ResourceLocation.Body }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ using Oqtane.UI;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Themes
|
||||
|
@ -46,7 +45,7 @@ namespace Oqtane.Themes
|
|||
{
|
||||
if (PageState.Page.Resources != null)
|
||||
{
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site && item.Namespace == type.Namespace).ToList();
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Page && item.Namespace == type.Namespace).ToList();
|
||||
}
|
||||
}
|
||||
else // themecontrolbase, containerbase
|
||||
|
@ -63,15 +62,18 @@ namespace Oqtane.Themes
|
|||
var inline = 0;
|
||||
foreach (Resource resource in resources)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scripts.Any())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@using System.ComponentModel
|
||||
@namespace Oqtane.UI
|
||||
@inject SiteState SiteState
|
||||
@implements IDisposable
|
||||
|
||||
@if (ComponentType != null && _visible)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
@namespace Oqtane.UI
|
||||
@using System.ComponentModel
|
||||
@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))
|
||||
{
|
||||
|
@ -15,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;
|
||||
|
@ -45,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)
|
23
Oqtane.Client/UI/InteractiveRenderMode.cs
Normal file
23
Oqtane.Client/UI/InteractiveRenderMode.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.UI
|
||||
{
|
||||
public static class InteractiveRenderMode
|
||||
{
|
||||
public static IComponentRenderMode GetInteractiveRenderMode(string runtime, bool prerender)
|
||||
{
|
||||
switch (runtime)
|
||||
{
|
||||
case Runtimes.Server:
|
||||
return new InteractiveServerRenderMode(prerender: prerender);
|
||||
case Runtimes.WebAssembly:
|
||||
return new InteractiveWebAssemblyRenderMode(prerender: prerender);
|
||||
case Runtimes.Auto:
|
||||
return new InteractiveAutoRenderMode(prerender: prerender);
|
||||
}
|
||||
return new InteractiveServerRenderMode(prerender: prerender);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +1,18 @@
|
|||
@namespace Oqtane.UI
|
||||
@inject IStringLocalizer<ModuleInstance> Localizer
|
||||
@inject ILogService LoggingService
|
||||
@inherits ErrorBoundary
|
||||
@inject SiteState SiteState
|
||||
|
||||
@if (CurrentException is null)
|
||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
if (_message != "" && _messagePosition == "top")
|
||||
{
|
||||
<ModuleMessage Message="@_message" Type="@_messageType" />
|
||||
}
|
||||
@if (ModuleType != null)
|
||||
{
|
||||
<DynamicComponent Type="@ModuleType" Parameters="@ModuleParameters"></DynamicComponent>
|
||||
@if (_progressIndicator)
|
||||
{
|
||||
<div class="app-progress-indicator"></div>
|
||||
}
|
||||
}
|
||||
if (_message != "" && _messagePosition == "bottom")
|
||||
{
|
||||
<ModuleMessage Message="@_message" Type="@_messageType" />
|
||||
}
|
||||
<StreamRenderingDisabled ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(_error))
|
||||
{
|
||||
<ModuleMessage Message="@_error" Type="@MessageType.Error"/>
|
||||
}
|
||||
<StreamRenderingEnabled ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" />
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _message;
|
||||
private string _error;
|
||||
private MessageType _messageType;
|
||||
private string _messagePosition;
|
||||
private bool _progressIndicator = false;
|
||||
|
||||
private Type ModuleType { get; set; }
|
||||
private IDictionary<string, object> ModuleParameters { get; set; }
|
||||
// this component is on the static side of the render mode boundary
|
||||
// it passes state as serializable parameters across the boundary
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
|
@ -46,82 +20,24 @@ else
|
|||
[CascadingParameter]
|
||||
private Module ModuleState { get; set; }
|
||||
|
||||
private ModuleMessage ModuleMessage { get; set; }
|
||||
|
||||
protected override bool ShouldRender()
|
||||
{
|
||||
return PageState?.RenderId == ModuleState?.RenderId;
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_message = "";
|
||||
if (ShouldRender())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleType))
|
||||
{
|
||||
ModuleType = Type.GetType(ModuleState.ModuleType);
|
||||
if (ModuleType != null)
|
||||
{
|
||||
ModuleParameters = new Dictionary<string, object> { { "ModuleInstance", this } };
|
||||
return;
|
||||
}
|
||||
// module does not exist with typename specified
|
||||
_message = string.Format(Localizer["Error.Module.InvalidName"], Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0));
|
||||
_messageType = MessageType.Error;
|
||||
_messagePosition = "top";
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Error.Module.InvalidType"], ModuleState.ModuleDefinitionName);
|
||||
_messageType = MessageType.Error;
|
||||
_messagePosition = "top";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("AddModuleMessage is deprecated. Use AddModuleMessage in ModuleBase instead.", false)]
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
AddModuleMessage(message, type, "top");
|
||||
}
|
||||
|
||||
[Obsolete("AddModuleMessage is deprecated. Use ModuleBase.AddModuleMessage instead.", false)]
|
||||
public void AddModuleMessage(string message, MessageType type, string position)
|
||||
{
|
||||
_message = message;
|
||||
_messageType = type;
|
||||
_messagePosition = position;
|
||||
_progressIndicator = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void ShowProgressIndicator()
|
||||
{
|
||||
_progressIndicator = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void HideProgressIndicator()
|
||||
{
|
||||
_progressIndicator = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
protected override async Task OnErrorAsync(Exception exception)
|
||||
{
|
||||
// retrieve friendly localized error
|
||||
_error = Localizer["Error.Module.Exception"];
|
||||
// log error
|
||||
string category = GetType().AssemblyQualifiedName;
|
||||
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
||||
await LoggingService.Log(null, ModuleState.PageId, ModuleState.ModuleId, PageState.User?.UserId, category, feature, LogFunction.Other, LogLevel.Error, exception, "An Unexpected Error Has Occurred In {ModuleDefinitionName}: {Error}", ModuleState.ModuleDefinitionName, exception.Message);
|
||||
await base.OnErrorAsync(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
public new void Recover()
|
||||
{
|
||||
_error = "";
|
||||
base.Recover();
|
||||
}
|
||||
|
||||
[Obsolete("ShowProgressIndicator is deprecated. Use ShowProgressIndicator in ModuleBase instead.", false)]
|
||||
public void ShowProgressIndicator()
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("HideProgressIndicator is deprecated. Use HideProgressIndicator in ModuleBase instead.", false)]
|
||||
public void HideProgressIndicator()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,14 @@ namespace Oqtane.UI
|
|||
public string Action { get; set; }
|
||||
public bool EditMode { get; set; }
|
||||
public DateTime LastSyncDate { get; set; }
|
||||
public string RenderMode { get; set; }
|
||||
public Shared.Runtime Runtime { get; set; }
|
||||
public int VisitorId { get; set; }
|
||||
public string RemoteIPAddress { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
public bool IsInternalNavigation { get; set; }
|
||||
public Guid RenderId { get; set; }
|
||||
public bool Refresh { get; set; }
|
||||
|
||||
public List<Page> Pages
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
@ -123,7 +123,7 @@ else
|
|||
|
||||
private void CreateComponent(RenderTreeBuilder builder, Module module, int key)
|
||||
{
|
||||
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
|
||||
builder.OpenComponent(0, typeof(ContainerBuilder));
|
||||
builder.AddAttribute(1, "ModuleState", module);
|
||||
if (key != -1)
|
||||
{
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
@namespace Oqtane.UI
|
||||
|
||||
@code {
|
||||
// panelayouts are deprecated - this component is included for backward compatibility
|
||||
}
|
165
Oqtane.Client/UI/RenderModeBoundary.razor
Normal file
165
Oqtane.Client/UI/RenderModeBoundary.razor
Normal file
|
@ -0,0 +1,165 @@
|
|||
@namespace Oqtane.UI
|
||||
@inject SiteState ComponentSiteState
|
||||
@inject IStringLocalizer<ModuleInstance> Localizer
|
||||
@inject ILogService LoggingService
|
||||
@inherits ErrorBoundary
|
||||
|
||||
<CascadingValue Value="@PageState">
|
||||
<CascadingValue Value="@ModuleState">
|
||||
@if (CurrentException is null)
|
||||
{
|
||||
@if (ModuleType != null)
|
||||
{
|
||||
@((MarkupString)$"<!-- rendermode: {ModuleState.RenderMode} -->")
|
||||
<ModuleMessage @ref="moduleMessageTop" Message="@_messageContent" Type="@_messageType" />
|
||||
@DynamicComponent
|
||||
@if (_progressIndicator)
|
||||
{
|
||||
<div class="app-progress-indicator"></div>
|
||||
}
|
||||
<ModuleMessage @ref="moduleMessageBottom" Message="@_messageContent" Type="@_messageType" />
|
||||
}
|
||||
}
|
||||
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; }
|
||||
|
||||
private string _messageContent;
|
||||
private MessageType _messageType;
|
||||
private string _messagePosition;
|
||||
private bool _progressIndicator = false;
|
||||
private string _error;
|
||||
private ModuleMessage moduleMessageTop;
|
||||
private ModuleMessage moduleMessageBottom;
|
||||
|
||||
[Parameter]
|
||||
public SiteState SiteState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public PageState PageState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Module ModuleState { get; set; }
|
||||
|
||||
protected override bool ShouldRender()
|
||||
{
|
||||
return PageState?.RenderId == ModuleState?.RenderId;
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_messageContent = "";
|
||||
|
||||
if (ShouldRender())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleType))
|
||||
{
|
||||
ModuleType = Type.GetType(ModuleState.ModuleType);
|
||||
if (ModuleType != null)
|
||||
{
|
||||
// repopulate the SiteState service based on the values passed in the SiteState parameter (this is how state is marshalled across the render mode boundary)
|
||||
ComponentSiteState.Hydrate(SiteState);
|
||||
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, ModuleType);
|
||||
builder.AddAttribute(1, "RenderModeBoundary", this);
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// module does not exist with typename specified
|
||||
_messageContent = string.Format(Localizer["Error.Module.InvalidName"], Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0));
|
||||
_messageType = MessageType.Error;
|
||||
_messagePosition = "top";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_messageContent = string.Format(Localizer["Error.Module.InvalidType"], ModuleState.ModuleDefinitionName);
|
||||
_messageType = MessageType.Error;
|
||||
_messagePosition = "top";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
AddModuleMessage(message, type, "top");
|
||||
}
|
||||
|
||||
public void AddModuleMessage(string message, MessageType type, string position)
|
||||
{
|
||||
_messageContent = message;
|
||||
_messageType = type;
|
||||
_messagePosition = position;
|
||||
_progressIndicator = false;
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void ShowProgressIndicator()
|
||||
{
|
||||
_progressIndicator = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void HideProgressIndicator()
|
||||
{
|
||||
_progressIndicator = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void DismissMessage()
|
||||
{
|
||||
_messageContent = "";
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
var updateTop = string.IsNullOrEmpty(_messageContent) || _messagePosition == "top";
|
||||
var updateBottom = string.IsNullOrEmpty(_messageContent) || _messagePosition == "bottom";
|
||||
|
||||
if (updateTop && moduleMessageTop != null)
|
||||
{
|
||||
moduleMessageTop.RefreshMessage(_messageContent, _messageType);
|
||||
}
|
||||
|
||||
if (updateBottom && moduleMessageBottom != null)
|
||||
{
|
||||
moduleMessageBottom.RefreshMessage(_messageContent, _messageType);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnErrorAsync(Exception exception)
|
||||
{
|
||||
// retrieve friendly localized error
|
||||
_error = Localizer["Error.Module.Exception"];
|
||||
// log error
|
||||
string category = GetType().AssemblyQualifiedName;
|
||||
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
||||
await LoggingService.Log(null, ModuleState.PageId, ModuleState.ModuleId, PageState.User?.UserId, category, feature, LogFunction.Other, LogLevel.Error, exception, "An Unexpected Error Has Occurred In {ModuleDefinitionName}: {Error}", ModuleState.ModuleDefinitionName, exception.Message);
|
||||
await base.OnErrorAsync(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
public new void Recover()
|
||||
{
|
||||
_error = "";
|
||||
base.Recover();
|
||||
}
|
||||
}
|
106
Oqtane.Client/UI/Routes.razor
Normal file
106
Oqtane.Client/UI/Routes.razor
Normal file
|
@ -0,0 +1,106 @@
|
|||
@namespace Oqtane.UI
|
||||
@using Microsoft.AspNetCore.Http
|
||||
@inject IInstallationService InstallationService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject SiteState SiteState
|
||||
@* the following StreamRendering attribute is required - if it is removed the framework will not render the content in static rendering *@
|
||||
@attribute [StreamRendering]
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@if (!_installed)
|
||||
{
|
||||
<Installer />
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RenderMode == RenderModes.Static)
|
||||
{
|
||||
<CascadingValue Value="@_pageState">
|
||||
<SiteRouter RenderMode="@RenderMode" Runtime="@Runtime" OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div style="@_display">
|
||||
<CascadingValue Value="@_pageState">
|
||||
<SiteRouter RenderMode="@RenderMode" Runtime="@Runtime" OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public PageState PageState { get; set; } = null;
|
||||
|
||||
[Parameter]
|
||||
public string RenderMode { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Runtime { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string AntiForgeryToken { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public string AuthorizationToken { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public string Platform { get; set; } = "";
|
||||
|
||||
[CascadingParameter]
|
||||
HttpContext HttpContext { get; set; }
|
||||
|
||||
private bool _initialized = false;
|
||||
private bool _installed = false;
|
||||
private string _display = "display: none;";
|
||||
|
||||
private PageState _pageState { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||
SiteState.AuthorizationToken = AuthorizationToken;
|
||||
SiteState.RemoteIPAddress = (_pageState != null) ? _pageState.RemoteIPAddress : "";
|
||||
SiteState.Platform = Platform;
|
||||
SiteState.IsPrerendering = (HttpContext != null) ? true : false;
|
||||
|
||||
if (Runtime == Runtimes.Hybrid)
|
||||
{
|
||||
var installation = await InstallationService.IsInstalled();
|
||||
_installed = installation.Success;
|
||||
if (installation.Alias != null)
|
||||
{
|
||||
SiteState.Alias = installation.Alias;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState != null)
|
||||
{
|
||||
_pageState = PageState;
|
||||
SiteState.Alias = PageState.Alias;
|
||||
_installed = true;
|
||||
}
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
// prevents flash on initial interactive page load
|
||||
_display = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeState(PageState pageState)
|
||||
{
|
||||
_pageState = pageState;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Oqtane.UI
|
||||
{
|
||||
[Obsolete("This enum is deprecated and will be removed in the upcoming major release, please use Oqtane.Shared.Runtime instead.")]
|
||||
public enum Runtime
|
||||
{
|
||||
Server,
|
||||
WebAssembly
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
@using System.Diagnostics.CodeAnalysis
|
||||
@using System.Net
|
||||
@using Microsoft.AspNetCore.Http
|
||||
@namespace Oqtane.UI
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject SiteState SiteState
|
||||
|
@ -11,8 +12,10 @@
|
|||
@inject IUserService UserService
|
||||
@inject IUrlMappingService UrlMappingService
|
||||
@inject ILogService LogService
|
||||
@inject ISettingService SettingService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@implements IHandleAfterRender
|
||||
@implements IDisposable
|
||||
|
||||
@if (!string.IsNullOrEmpty(_error))
|
||||
{
|
||||
|
@ -26,16 +29,13 @@
|
|||
private bool _isInternalNavigation = false;
|
||||
private bool _navigationInterceptionEnabled;
|
||||
private PageState _pagestate;
|
||||
private string _error = "";
|
||||
|
||||
[Parameter]
|
||||
public string Runtime { get; set; }
|
||||
private string _error = "";
|
||||
|
||||
[Parameter]
|
||||
public string RenderMode { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int VisitorId { get; set; }
|
||||
public string Runtime { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
PageState PageState { get; set; }
|
||||
|
@ -52,9 +52,9 @@
|
|||
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
if (PageState != null)
|
||||
if (PageState != null && !PageState.Refresh)
|
||||
{
|
||||
builder.OpenComponent(0, Type.GetType(Constants.PageComponent));
|
||||
builder.OpenComponent(0, typeof(ThemeBuilder));
|
||||
builder.CloseComponent();
|
||||
}
|
||||
};
|
||||
|
@ -67,7 +67,7 @@
|
|||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PageState == null)
|
||||
if (PageState == null || PageState.Refresh)
|
||||
{
|
||||
await Refresh();
|
||||
}
|
||||
|
@ -99,12 +99,12 @@
|
|||
var editmode = false;
|
||||
var refresh = false;
|
||||
var lastsyncdate = DateTime.MinValue;
|
||||
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
||||
var visitorId = -1;
|
||||
_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 = "";
|
||||
|
@ -144,66 +144,43 @@
|
|||
refresh = true;
|
||||
}
|
||||
|
||||
// verify user is authenticated for current site
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == "sitekey" && item.Value == SiteState.Alias.SiteKey))
|
||||
{
|
||||
// get user
|
||||
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
|
||||
if (PageState != null)
|
||||
{
|
||||
editmode = PageState.EditMode;
|
||||
lastsyncdate = PageState.LastSyncDate;
|
||||
}
|
||||
if (PageState?.Page.Path != route.PagePath)
|
||||
{
|
||||
editmode = false; // reset edit mode when navigating to different page
|
||||
}
|
||||
if (querystring.ContainsKey("edit") && querystring["edit"] == "true")
|
||||
{
|
||||
editmode = true; // querystring can set edit mode
|
||||
visitorId = PageState.VisitorId;
|
||||
}
|
||||
|
||||
// get user
|
||||
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||
if (PageState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
// verify user is authenticated for current site
|
||||
if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == "sitekey" && item.Value == SiteState.Alias.SiteKey))
|
||||
// process any sync events (for synchrozing the client application with the server)
|
||||
var sync = await SyncService.GetSyncEventsAsync(lastsyncdate);
|
||||
lastsyncdate = sync.SyncDate;
|
||||
if (sync.SyncEvents.Any())
|
||||
{
|
||||
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
|
||||
if (user != null)
|
||||
// reload client application if server was restarted
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Host))
|
||||
{
|
||||
user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// refresh PageState when site information has changed
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Refresh && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||
{
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = PageState.User;
|
||||
}
|
||||
|
||||
// process any sync events
|
||||
var sync = await SyncService.GetSyncEventsAsync(lastsyncdate);
|
||||
lastsyncdate = sync.SyncDate;
|
||||
if (sync.SyncEvents.Any())
|
||||
{
|
||||
// reload client application if server was restarted
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Host))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// reload client application if site runtime/rendermode was modified
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// reload client application if current user auth information has changed
|
||||
if (user != null && sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.User && item.EntityId == user.UserId))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// refresh PageState when site information has changed
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Refresh && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||
{
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,6 +197,12 @@
|
|||
|
||||
if (site != null)
|
||||
{
|
||||
if (Runtime == Runtimes.Hybrid && !site.Hybrid)
|
||||
{
|
||||
_error = "Hybrid Integration Is Not Enabled For This Site";
|
||||
return;
|
||||
}
|
||||
|
||||
if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
|
||||
{
|
||||
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
||||
|
@ -255,6 +238,7 @@
|
|||
{
|
||||
// redirect to the personalized page
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, personalized.Path, ""), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,6 +248,24 @@
|
|||
// check if user is authorized to view page
|
||||
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList) && (Utilities.IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate) || UserSecurity.IsAuthorized(user, PermissionNames.Edit, page.PermissionList)))
|
||||
{
|
||||
// edit mode
|
||||
if (user != null)
|
||||
{
|
||||
if (querystring.ContainsKey("editmode") && querystring["edit"] == "true")
|
||||
{
|
||||
editmode = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
editmode = (page.PageId == ((user.Settings.ContainsKey("CP-editmode")) ? int.Parse(user.Settings["CP-editmode"]) : -1));
|
||||
if (!editmode)
|
||||
{
|
||||
var userSettings = new Dictionary<string, string> { { "CP-editmode", "-1" } };
|
||||
await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load additional metadata for current page
|
||||
page = ProcessPage(page, site, user, SiteState.Alias);
|
||||
|
||||
|
@ -285,16 +287,21 @@
|
|||
Action = action,
|
||||
EditMode = editmode,
|
||||
LastSyncDate = lastsyncdate,
|
||||
Runtime = runtime,
|
||||
VisitorId = VisitorId,
|
||||
RenderMode = RenderMode,
|
||||
Runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime),
|
||||
VisitorId = visitorId,
|
||||
RemoteIPAddress = SiteState.RemoteIPAddress,
|
||||
ReturnUrl = returnurl,
|
||||
IsInternalNavigation = _isInternalNavigation,
|
||||
RenderId = Guid.NewGuid()
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = false
|
||||
};
|
||||
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
await ScrollToFragment(_pagestate.Uri);
|
||||
|
||||
if (PageState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
await ScrollToFragment(_pagestate.Uri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -317,7 +324,7 @@
|
|||
}
|
||||
else // not mapped
|
||||
{
|
||||
if (user == null && Utilities.IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate))
|
||||
if (user == null)
|
||||
{
|
||||
// redirect to login page if user not logged in as they may need to be authenticated
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
|
||||
|
@ -357,17 +364,15 @@
|
|||
page.ThemeType = site.DefaultThemeType;
|
||||
}
|
||||
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||
Type themetype = Type.GetType(page.ThemeType);
|
||||
if (themetype == null || theme == null)
|
||||
if (theme == null)
|
||||
{
|
||||
// fallback to default Oqtane theme
|
||||
page.ThemeType = Constants.DefaultTheme;
|
||||
themetype = Type.GetType(Constants.DefaultTheme);
|
||||
theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||
}
|
||||
|
||||
Type themetype = Type.GetType(page.ThemeType);
|
||||
string panes = "";
|
||||
if (themetype != null && theme != null)
|
||||
if (themetype != null)
|
||||
{
|
||||
// get resources for theme (ITheme)
|
||||
page.Resources = ManagePageResources(page.Resources, theme.Resources, ResourceLevel.Page, alias, "Themes", Utilities.GetTypeName(theme.ThemeName));
|
||||
|
@ -410,8 +415,10 @@
|
|||
{
|
||||
var paneindex = new Dictionary<string, int>();
|
||||
|
||||
foreach (Module module in modules)
|
||||
foreach (Module module in modules.Where(item => item.PageId == page.PageId || item.ModuleId == moduleid))
|
||||
{
|
||||
var typename = Constants.ErrorModule;
|
||||
|
||||
// initialize module control properties
|
||||
module.SecurityAccessLevel = SecurityAccessLevel.Host;
|
||||
module.ControlTitle = "";
|
||||
|
@ -420,110 +427,108 @@
|
|||
module.PaneModuleIndex = -1;
|
||||
module.PaneModuleCount = 0;
|
||||
|
||||
if (module.PageId == page.PageId || module.ModuleId == moduleid)
|
||||
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
|
||||
{
|
||||
var typename = Constants.ErrorModule;
|
||||
page.Resources = ManagePageResources(page.Resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
|
||||
|
||||
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(Runtime)))
|
||||
// handle default action
|
||||
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
||||
{
|
||||
typename = module.ModuleDefinition.ControlTypeTemplate;
|
||||
action = module.ModuleDefinition.DefaultAction;
|
||||
}
|
||||
|
||||
// handle default action
|
||||
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
||||
// get typename template
|
||||
typename = module.ModuleDefinition.ControlTypeTemplate;
|
||||
if (module.ModuleDefinition.ControlTypeRoutes != "")
|
||||
{
|
||||
// process custom action routes
|
||||
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
action = module.ModuleDefinition.DefaultAction;
|
||||
}
|
||||
|
||||
// check if the module defines custom action routes
|
||||
if (module.ModuleDefinition.ControlTypeRoutes != "")
|
||||
{
|
||||
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
||||
if (route.StartsWith(action + "="))
|
||||
{
|
||||
if (route.StartsWith(action + "="))
|
||||
{
|
||||
typename = route.Replace(action + "=", "");
|
||||
}
|
||||
typename = route.Replace(action + "=", "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get module resources
|
||||
page.Resources = ManagePageResources(page.Resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
|
||||
}
|
||||
// create typename
|
||||
if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
else
|
||||
{
|
||||
typename = typename.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
|
||||
// ensure component exists and implements IModuleControl
|
||||
module.ModuleType = "";
|
||||
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
|
||||
}
|
||||
// ensure component exists and implements IModuleControl
|
||||
module.ModuleType = "";
|
||||
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
|
||||
}
|
||||
|
||||
// get additional metadata from IModuleControl interface
|
||||
if (moduletype != null && module.ModuleType != "")
|
||||
if (moduletype != null && module.ModuleType != "")
|
||||
{
|
||||
// retrieve module component resources
|
||||
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
module.RenderMode = moduleobject.RenderMode;
|
||||
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||
{
|
||||
// retrieve module component resources
|
||||
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||
// settings components are embedded within a framework settings module
|
||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
||||
if (moduletype != null)
|
||||
{
|
||||
// settings components are embedded within a framework settings module
|
||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
||||
if (moduletype != null)
|
||||
{
|
||||
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||
}
|
||||
}
|
||||
|
||||
// additional metadata needed for admin components
|
||||
if (module.ModuleId == moduleid && action != "")
|
||||
{
|
||||
module.ControlTitle = moduleobject.Title;
|
||||
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
|
||||
module.Actions = moduleobject.Actions;
|
||||
module.UseAdminContainer = moduleobject.UseAdminContainer;
|
||||
moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources, ResourceLevel.Module, alias, "Modules", moduletype.Namespace);
|
||||
}
|
||||
}
|
||||
|
||||
// validate that module's pane exists in current page
|
||||
if (page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||
// additional metadata needed for admin components
|
||||
if (module.ModuleId == moduleid && action != "")
|
||||
{
|
||||
// fallback to default pane if it exists
|
||||
if (page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||
{
|
||||
module.Pane = PaneNames.Default;
|
||||
}
|
||||
else // otherwise admin pane (legacy)
|
||||
{
|
||||
module.Pane = PaneNames.Admin;
|
||||
}
|
||||
module.ControlTitle = moduleobject.Title;
|
||||
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
|
||||
module.Actions = moduleobject.Actions;
|
||||
module.UseAdminContainer = moduleobject.UseAdminContainer;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate module position within pane
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
// validate that module's pane exists in current page
|
||||
if (page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||
{
|
||||
// fallback to default pane if it exists
|
||||
if (page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||
{
|
||||
paneindex[module.Pane.ToLower()] += 1;
|
||||
module.Pane = PaneNames.Default;
|
||||
}
|
||||
else
|
||||
else // otherwise admin pane (legacy)
|
||||
{
|
||||
paneindex.Add(module.Pane.ToLower(), 0);
|
||||
module.Pane = PaneNames.Admin;
|
||||
}
|
||||
}
|
||||
|
||||
module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
|
||||
// calculate module position within pane
|
||||
if (paneindex.ContainsKey(module.Pane.ToLower()))
|
||||
{
|
||||
paneindex[module.Pane.ToLower()] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
paneindex.Add(module.Pane.ToLower(), 0);
|
||||
}
|
||||
|
||||
// container fallback
|
||||
if (string.IsNullOrEmpty(module.ContainerType))
|
||||
{
|
||||
module.ContainerType = defaultcontainertype;
|
||||
}
|
||||
module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
|
||||
|
||||
// container fallback
|
||||
if (string.IsNullOrEmpty(module.ContainerType))
|
||||
{
|
||||
module.ContainerType = defaultcontainertype;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,9 +563,7 @@
|
|||
// 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(resource.Clone(level, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
Oqtane.Client/UI/StreamRenderingDisabled.razor
Normal file
21
Oqtane.Client/UI/StreamRenderingDisabled.razor
Normal 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; }
|
||||
}
|
21
Oqtane.Client/UI/StreamRenderingEnabled.razor
Normal file
21
Oqtane.Client/UI/StreamRenderingEnabled.razor
Normal 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; }
|
||||
}
|
|
@ -34,14 +34,14 @@
|
|||
var headcontent = "";
|
||||
|
||||
// favicon
|
||||
var favicon = "favicon.ico";
|
||||
var favicontype = "x-icon";
|
||||
if (PageState.Site.FaviconFileId != null)
|
||||
{
|
||||
favicon = Utilities.FileUrl(PageState.Alias, PageState.Site.FaviconFileId.Value);
|
||||
favicontype = favicon.Substring(favicon.LastIndexOf(".") + 1);
|
||||
headcontent += $"<link id=\"app-favicon\" rel=\"icon\" href=\"{Utilities.FileUrl(PageState.Alias, PageState.Site.FaviconFileId.Value)}\" />\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
headcontent += $"<link id=\"app-favicon\" rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\" />\n";
|
||||
}
|
||||
headcontent += $"<link id=\"app-favicon\" rel=\"shortcut icon\" type=\"image/{favicontype}\" href=\"{favicon}\" />\n";
|
||||
|
||||
// head content
|
||||
AddHeadContent(headcontent, PageState.Site.HeadContent);
|
||||
|
@ -57,8 +57,7 @@
|
|||
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
var themeType = Type.GetType(PageState.Page.ThemeType);
|
||||
builder.OpenComponent(0, themeType);
|
||||
builder.OpenComponent(0, Type.GetType(PageState.Page.ThemeType));
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
|
@ -73,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))
|
||||
{
|
||||
|
@ -145,6 +144,7 @@
|
|||
string integrity = "";
|
||||
string crossorigin = "";
|
||||
string type = "";
|
||||
var dataAttributes = new Dictionary<string, string>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (attribute.Contains("="))
|
||||
|
@ -167,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,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
|
||||
{
|
||||
|
@ -194,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>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
@using System.Net.Http.Json
|
||||
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using Microsoft.JSInterop
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
|
||||
@using Oqtane.Client
|
||||
@using Oqtane.Models
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.2</Version>
|
||||
<Version>5.1.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.2</Version>
|
||||
<Version>5.1.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -33,9 +33,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.2</Version>
|
||||
<Version>5.1.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -33,7 +33,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.2</Version>
|
||||
<Version>5.1.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -33,7 +33,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
<DynamicComponent Type="@ComponentType"></DynamicComponent>
|
||||
@using Oqtane.Shared;
|
||||
|
||||
<DynamicComponent Type="@ComponentType" Parameters="@Parameters"></DynamicComponent>
|
||||
|
||||
@code {
|
||||
Type ComponentType = Type.GetType("Oqtane.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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@using System.Text.Json;
|
||||
@using System.Text.Json.Nodes;
|
||||
@using Oqtane.Shared;
|
||||
|
||||
@if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
|
@ -11,19 +12,16 @@ else
|
|||
}
|
||||
|
||||
@code {
|
||||
Type ComponentType = Type.GetType("Oqtane.App, Oqtane.Client");
|
||||
Type ComponentType = Type.GetType("Oqtane.UI.Routes, Oqtane.Client");
|
||||
private IDictionary<string, object> Parameters { get; set; }
|
||||
private string message = "";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Parameters = new Dictionary<string, object>();
|
||||
Parameters.Add(new KeyValuePair<string, object>("AntiForgeryToken", ""));
|
||||
Parameters.Add(new KeyValuePair<string, object>("Runtime", "Hybrid"));
|
||||
Parameters.Add(new KeyValuePair<string, object>("RenderMode", "Hybrid"));
|
||||
Parameters.Add(new KeyValuePair<string, object>("VisitorId", -1));
|
||||
Parameters.Add(new KeyValuePair<string, object>("RemoteIPAddress", ""));
|
||||
Parameters.Add(new KeyValuePair<string, object>("AuthorizationToken", ""));
|
||||
Parameters.Add(new KeyValuePair<string, object>("RenderMode", RenderModes.Interactive));
|
||||
Parameters.Add(new KeyValuePair<string, object>("Runtime", Runtimes.Hybrid));
|
||||
Parameters.Add(new KeyValuePair<string, object>("Platform", DeviceInfo.Current.Platform.ToString()));
|
||||
|
||||
if (MauiConstants.UseAppSettings)
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@ public static class MauiProgram
|
|||
builder.Services.AddMauiBlazorWebView();
|
||||
#if DEBUG
|
||||
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
var apiurl = LoadAppSettings();
|
||||
|
||||
|
@ -44,10 +44,10 @@ public static class MauiProgram
|
|||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
// register auth services
|
||||
builder.Services.AddOqtaneAuthorization();
|
||||
builder.Services.AddOqtaneAuthentication();
|
||||
|
||||
// register scoped core services
|
||||
builder.Services.AddOqtaneScopedServices();
|
||||
builder.Services.AddOqtaneClientScopedServices();
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
|
@ -255,6 +255,16 @@ public static class MauiProgram
|
|||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
implementationTypes = assembly.GetInterfaces<IClientService>();
|
||||
foreach (var implementationType in implementationTypes)
|
||||
{
|
||||
if (implementationType.AssemblyQualifiedName != null)
|
||||
{
|
||||
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
|
||||
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>5.0.2</Version>
|
||||
<Version>5.1.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
|
@ -14,7 +14,7 @@
|
|||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>5.0.2</ApplicationDisplayVersion>
|
||||
<ApplicationDisplayVersion>5.1.0</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||
|
@ -65,14 +65,15 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.3" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -228,3 +228,7 @@ app {
|
|||
.app-fas {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.app-form-inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
<title>Oqtane Maui</title>
|
||||
<base href="/" />
|
||||
<link id="app-favicon" rel="shortcut icon" type="image/x-icon" href="" />
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/loadjs.min.js"></script>
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
<link id="app-stylesheet-page" />
|
||||
<link id="app-stylesheet-module" />
|
||||
|
@ -25,6 +23,8 @@
|
|||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/loadjs.min.js"></script>
|
||||
<script src="js/interop.js"></script>
|
||||
|
||||
<script src="_framework/blazor.webview.js" autostart="false"></script>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -11,8 +11,8 @@
|
|||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.0.2/Oqtane.Framework.5.0.2.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.1.0/Oqtane.Framework.5.1.0.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.2</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.2.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.1.0.Install.zip" -Force
|
|
@ -1 +1 @@
|
|||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.2.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.1.0.Upgrade.zip" -Force
|
722
Oqtane.Server/Components/App.razor
Normal file
722
Oqtane.Server/Components/App.razor
Normal file
|
@ -0,0 +1,722 @@
|
|||
@namespace Oqtane.Components
|
||||
@using System.Net
|
||||
@using System.Security.Claims
|
||||
@using Microsoft.AspNetCore.Http
|
||||
@using Microsoft.AspNetCore.Http.Extensions
|
||||
@using Microsoft.AspNetCore.Antiforgery
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@using Microsoft.Net.Http.Headers
|
||||
@using Microsoft.Extensions.Primitives
|
||||
@using Oqtane.Client
|
||||
@using Oqtane.UI
|
||||
@using Oqtane.Repository
|
||||
@using Oqtane.Infrastructure
|
||||
@using Oqtane.Security
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Extensions
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IAntiforgery Antiforgery
|
||||
@inject IConfigManager ConfigManager
|
||||
@inject ITenantManager TenantManager
|
||||
@inject ISiteService SiteService
|
||||
@inject IPageRepository PageRepository
|
||||
@inject IThemeRepository ThemeRepository
|
||||
@inject ILanguageRepository LanguageRepository
|
||||
@inject ILocalizationManager LocalizationManager
|
||||
@inject IAliasRepository AliasRepository
|
||||
@inject IUrlMappingRepository UrlMappingRepository
|
||||
@inject IVisitorRepository VisitorRepository
|
||||
@inject IJwtManager JwtManager
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
<!DOCTYPE html>
|
||||
<html lang="@_language">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
@if (!string.IsNullOrEmpty(_PWAScript))
|
||||
{
|
||||
<link id="app-manifest" rel="manifest" />
|
||||
}
|
||||
@((MarkupString)_styleSheets)
|
||||
<link id="app-stylesheet-page" />
|
||||
<link id="app-stylesheet-module" />
|
||||
@if (_renderMode == RenderModes.Static)
|
||||
{
|
||||
<Head RenderMode="@_renderMode" Runtime="@_runtime" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<Head RenderMode="@_renderMode" Runtime="@_runtime" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(_runtime, _prerender)" />
|
||||
}
|
||||
@((MarkupString)_headResources)
|
||||
</head>
|
||||
<body>
|
||||
@if (string.IsNullOrEmpty(_message))
|
||||
{
|
||||
@if (_renderMode == RenderModes.Static)
|
||||
{
|
||||
<Routes PageState="@_pageState" RenderMode="@_renderMode" Runtime="@_runtime" AntiForgeryToken="@_antiForgeryToken" AuthorizationToken="@_authorizationToken" Platform="@_platform" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<Routes PageState="@_pageState" RenderMode="@_renderMode" Runtime="@_runtime" AntiForgeryToken="@_antiForgeryToken" AuthorizationToken="@_authorizationToken" Platform="@_platform" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(_runtime, _prerender)" />
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(_reconnectScript))
|
||||
{
|
||||
@((MarkupString)_reconnectScript)
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(_PWAScript))
|
||||
{
|
||||
@((MarkupString)_PWAScript)
|
||||
}
|
||||
@((MarkupString)_bodyResources)
|
||||
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/loadjs.min.js"></script>
|
||||
<script src="js/interop.js"></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="app-alert">@_message</div>
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private string _renderMode = RenderModes.Interactive;
|
||||
private string _runtime = Runtimes.Server;
|
||||
private bool _prerender = true;
|
||||
private int _visitorId = -1;
|
||||
private string _antiForgeryToken = "";
|
||||
private string _remoteIPAddress = "";
|
||||
private string _platform = "";
|
||||
private string _authorizationToken = "";
|
||||
private string _language = "en";
|
||||
private string _headResources = "";
|
||||
private string _bodyResources = "";
|
||||
private string _styleSheets = "";
|
||||
private string _PWAScript = "";
|
||||
private string _reconnectScript = "";
|
||||
private string _message = "";
|
||||
private PageState _pageState;
|
||||
|
||||
// CascadingParameter is required to access HttpContext
|
||||
[CascadingParameter]
|
||||
HttpContext Context { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_antiForgeryToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
|
||||
_remoteIPAddress = Context.Connection.RemoteIpAddress?.ToString() ?? "";
|
||||
_platform = System.Runtime.InteropServices.RuntimeInformation.OSDescription;
|
||||
|
||||
// if framework is installed
|
||||
if (ConfigManager.IsInstalled())
|
||||
{
|
||||
var alias = TenantManager.GetAlias();
|
||||
if (alias != null)
|
||||
{
|
||||
var url = WebUtility.UrlDecode(Context.Request.GetEncodedUrl());
|
||||
|
||||
if (!alias.IsDefault)
|
||||
{
|
||||
HandleDefaultAliasRedirect(alias, url);
|
||||
}
|
||||
|
||||
var site = await SiteService.GetSiteAsync(alias.SiteId);
|
||||
if (site != null && (!site.IsDeleted || url.Contains("admin/site")) && site.RenderMode != RenderModes.Headless)
|
||||
{
|
||||
_renderMode = site.RenderMode;
|
||||
_runtime = site.Runtime;
|
||||
_prerender = site.Prerender;
|
||||
|
||||
Route route = new Route(url, alias.Path);
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
// personalized pages need to be retrieved using path
|
||||
page = PageRepository.GetPage(route.PagePath, site.SiteId);
|
||||
}
|
||||
if (page == null || page.IsDeleted)
|
||||
{
|
||||
HandlePageNotFound(site, page, route);
|
||||
}
|
||||
|
||||
if (site.VisitorTracking)
|
||||
{
|
||||
TrackVisitor(site.SiteId);
|
||||
}
|
||||
|
||||
// get jwt token for downstream APIs
|
||||
if (Context.User.Identity.IsAuthenticated)
|
||||
{
|
||||
CreateJwtToken(alias);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
if (_renderMode == RenderModes.Interactive && _runtime == Runtimes.Server)
|
||||
{
|
||||
_reconnectScript = CreateReconnectScript();
|
||||
}
|
||||
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
|
||||
{
|
||||
_PWAScript = CreatePWAScript(alias, site, route);
|
||||
}
|
||||
_headResources += ParseScripts(site.HeadContent);
|
||||
_bodyResources += ParseScripts(site.BodyContent);
|
||||
|
||||
// set culture if not specified
|
||||
string culture = Context.Request.Cookies[CookieRequestCultureProvider.DefaultCookieName];
|
||||
if (culture == null)
|
||||
{
|
||||
// get default language for site
|
||||
if (site.Languages.Any())
|
||||
{
|
||||
// use default language if specified otherwise use first language in collection
|
||||
culture = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code;
|
||||
}
|
||||
else
|
||||
{
|
||||
culture = LocalizationManager.GetDefaultCulture();
|
||||
}
|
||||
SetLocalizationCookie(culture);
|
||||
}
|
||||
|
||||
// set language for page
|
||||
if (!string.IsNullOrEmpty(culture))
|
||||
{
|
||||
// localization cookie value in form of c=en|uic=en
|
||||
_language = culture.Split('|')[0];
|
||||
_language = _language.Replace("c=", "");
|
||||
}
|
||||
|
||||
// create initial PageState
|
||||
_pageState = new PageState
|
||||
{
|
||||
Alias = alias,
|
||||
Site = site,
|
||||
Page = page,
|
||||
User = null,
|
||||
Uri = new Uri(url, UriKind.Absolute),
|
||||
Route = route,
|
||||
QueryString = Utilities.ParseQueryString(route.Query),
|
||||
UrlParameters = route.UrlParameters,
|
||||
ModuleId = -1,
|
||||
Action = "",
|
||||
EditMode = false,
|
||||
LastSyncDate = DateTime.MinValue,
|
||||
RenderMode = _renderMode,
|
||||
Runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), _runtime),
|
||||
VisitorId = _visitorId,
|
||||
RemoteIPAddress = _remoteIPAddress,
|
||||
ReturnUrl = "",
|
||||
IsInternalNavigation = false,
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = true
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = "Site Is Disabled";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
|
||||
}
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void HandleDefaultAliasRedirect(Alias alias, string url)
|
||||
{
|
||||
// get aliases for site and tenant
|
||||
var aliases = AliasRepository.GetAliases().Where(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId);
|
||||
// get first default alias
|
||||
var defaultAlias = aliases.Where(item => item.IsDefault).FirstOrDefault();
|
||||
if (defaultAlias != null)
|
||||
{
|
||||
// redirect to default alias
|
||||
NavigationManager.NavigateTo(url.Replace(alias.Name, defaultAlias.Name), true);
|
||||
}
|
||||
else // no default alias specified - use first alias
|
||||
{
|
||||
defaultAlias = aliases.FirstOrDefault();
|
||||
if (defaultAlias != null && alias.Name.Trim() != defaultAlias.Name.Trim())
|
||||
{
|
||||
// redirect to first alias
|
||||
NavigationManager.NavigateTo(url.Replace(alias.Name, defaultAlias.Name), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePageNotFound(Site site, Page page, Route route)
|
||||
{
|
||||
// page not found - look for url mapping
|
||||
var urlMapping = UrlMappingRepository.GetUrlMapping(site.SiteId, route.PagePath);
|
||||
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
|
||||
{
|
||||
// redirect to mapped url
|
||||
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
else // no url mapping exists
|
||||
{
|
||||
if (route.PagePath != "404")
|
||||
{
|
||||
// redirect to 404 page
|
||||
NavigationManager.NavigateTo(route.SiteUrl + "/404", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TrackVisitor(int SiteId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get request attributes
|
||||
string useragent = (Context.Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Context.Request.Headers[HeaderNames.UserAgent] : "(none)";
|
||||
useragent = (useragent.Length > 256) ? useragent.Substring(0, 256) : useragent;
|
||||
string language = (Context.Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Context.Request.Headers[HeaderNames.AcceptLanguage] : "";
|
||||
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
|
||||
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
|
||||
language = (language.Trim().Length == 0) ? "??" : language;
|
||||
|
||||
// filter
|
||||
var settings = Context.GetSiteSettings();
|
||||
var filter = settings.GetValue("VisitorFilter", Constants.DefaultVisitorFilter);
|
||||
foreach (string term in filter.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
if (_remoteIPAddress.ToLower().Contains(term) || useragent.ToLower().Contains(term) || language.ToLower().Contains(term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// get other request attributes
|
||||
string url = Context.Request.GetEncodedUrl();
|
||||
string referrer = (Context.Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Context.Request.Headers[HeaderNames.Referer] : "";
|
||||
int? userid = Context.User.UserId();
|
||||
userid = (userid == -1) ? null : userid;
|
||||
|
||||
// check if cookie already exists
|
||||
Visitor visitor = null;
|
||||
bool addcookie = false;
|
||||
var VisitorCookie = Constants.VisitorCookiePrefix + SiteId.ToString();
|
||||
if (!int.TryParse(Context.Request.Cookies[VisitorCookie], out _visitorId))
|
||||
{
|
||||
// if enabled use IP Address correlation
|
||||
_visitorId = -1;
|
||||
var correlate = bool.Parse(settings.GetValue("VisitorCorrelation", "true"));
|
||||
if (correlate)
|
||||
{
|
||||
visitor = VisitorRepository.GetVisitor(SiteId, _remoteIPAddress);
|
||||
if (visitor != null)
|
||||
{
|
||||
_visitorId = visitor.VisitorId;
|
||||
addcookie = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_visitorId == -1)
|
||||
{
|
||||
// create new visitor
|
||||
visitor = new Visitor();
|
||||
visitor.SiteId = SiteId;
|
||||
visitor.IPAddress = _remoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
visitor.Referrer = referrer;
|
||||
visitor.UserId = userid;
|
||||
visitor.Visits = 1;
|
||||
visitor.CreatedOn = DateTime.UtcNow;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
visitor = VisitorRepository.AddVisitor(visitor);
|
||||
_visitorId = visitor.VisitorId;
|
||||
addcookie = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (visitor == null)
|
||||
{
|
||||
// get visitor if it was not previously loaded
|
||||
visitor = VisitorRepository.GetVisitor(_visitorId);
|
||||
}
|
||||
if (visitor != null)
|
||||
{
|
||||
// update visitor
|
||||
visitor.IPAddress = _remoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
if (!string.IsNullOrEmpty(referrer))
|
||||
{
|
||||
visitor.Referrer = referrer;
|
||||
}
|
||||
if (userid != null)
|
||||
{
|
||||
visitor.UserId = userid;
|
||||
}
|
||||
visitor.Visits += 1;
|
||||
visitor.VisitedOn = DateTime.UtcNow;
|
||||
VisitorRepository.UpdateVisitor(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove cookie if VisitorId does not exist
|
||||
Context.Response.Cookies.Delete(VisitorCookie);
|
||||
}
|
||||
}
|
||||
|
||||
// append cookie
|
||||
if (addcookie)
|
||||
{
|
||||
Context.Response.Cookies.Append(
|
||||
VisitorCookie,
|
||||
_visitorId.ToString(),
|
||||
new CookieOptions()
|
||||
{
|
||||
Expires = DateTimeOffset.UtcNow.AddYears(1),
|
||||
IsEssential = true
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// error tracking visitor
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateJwtToken(Alias alias)
|
||||
{
|
||||
var sitesettings = Context.GetSiteSettings();
|
||||
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
|
||||
if (!string.IsNullOrEmpty(secret))
|
||||
{
|
||||
_authorizationToken = JwtManager.GenerateToken(alias, (ClaimsIdentity)Context.User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
|
||||
}
|
||||
}
|
||||
|
||||
private string CreatePWAScript(Alias alias, Site site, Route route)
|
||||
{
|
||||
return
|
||||
"<script>" + Environment.NewLine +
|
||||
" // PWA Manifest" + Environment.NewLine +
|
||||
" setTimeout(() => {" + Environment.NewLine +
|
||||
" var manifest = {" + Environment.NewLine +
|
||||
" \"name\": \"" + site.Name + "\"," + Environment.NewLine +
|
||||
" \"short_name\": \"" + site.Name + "\"," + Environment.NewLine +
|
||||
" \"start_url\": \"" + route.SiteUrl + "/\"," + Environment.NewLine +
|
||||
" \"display\": \"standalone\"," + Environment.NewLine +
|
||||
" \"background_color\": \"#fff\"," + Environment.NewLine +
|
||||
" \"description\": \"" + site.Name + "\"," + Environment.NewLine +
|
||||
" \"icons\": [{" + Environment.NewLine +
|
||||
" \"src\": \"" + route.RootUrl + Utilities.FileUrl(alias, site.PwaAppIconFileId.Value) + "\"," + Environment.NewLine +
|
||||
" \"sizes\": \"192x192\"," + Environment.NewLine +
|
||||
" \"type\": \"image/png\"" + Environment.NewLine +
|
||||
" }, {" + Environment.NewLine +
|
||||
" \"src\": \"" + route.RootUrl + Utilities.FileUrl(alias, site.PwaSplashIconFileId.Value) + "\"," + Environment.NewLine +
|
||||
" \"sizes\": \"512x512\"," + Environment.NewLine +
|
||||
" \"type\": \"image/png\"" + Environment.NewLine +
|
||||
" }]" + Environment.NewLine +
|
||||
" };" + Environment.NewLine +
|
||||
" const serialized = JSON.stringify(manifest);" + Environment.NewLine +
|
||||
" const blob = new Blob([serialized], {type: 'application/javascript'});" + Environment.NewLine +
|
||||
" const url = URL.createObjectURL(blob);" + Environment.NewLine +
|
||||
" document.getElementById('app-manifest').setAttribute('href', url);" + Environment.NewLine +
|
||||
" }, 1000);" + Environment.NewLine +
|
||||
"</script>" + Environment.NewLine +
|
||||
"<script>" + Environment.NewLine +
|
||||
" // PWA Service Worker" + Environment.NewLine +
|
||||
" if ('serviceWorker' in navigator) {" + Environment.NewLine +
|
||||
" navigator.serviceWorker.register('/service-worker.js').then(function(registration) {" + Environment.NewLine +
|
||||
" console.log('ServiceWorker Registration Successful');" + Environment.NewLine +
|
||||
" }).catch (function(err) {" + Environment.NewLine +
|
||||
" console.log('ServiceWorker Registration Failed ', err);" + Environment.NewLine +
|
||||
" });" + Environment.NewLine +
|
||||
" };" + Environment.NewLine +
|
||||
"</script>";
|
||||
}
|
||||
|
||||
private string CreateReconnectScript()
|
||||
{
|
||||
return
|
||||
"<script>" + Environment.NewLine +
|
||||
" // Blazor Server Reconnect" + Environment.NewLine +
|
||||
" new MutationObserver((mutations, observer) => {" + Environment.NewLine +
|
||||
" if (document.querySelector('#components-reconnect-modal h5 a')) {" + Environment.NewLine +
|
||||
" async function attemptReload() {" + Environment.NewLine +
|
||||
" await fetch('');" + Environment.NewLine +
|
||||
" location.reload();" + Environment.NewLine +
|
||||
" }" + Environment.NewLine +
|
||||
" observer.disconnect();" + Environment.NewLine +
|
||||
" attemptReload();" + Environment.NewLine +
|
||||
" setInterval(attemptReload, 5000);" + Environment.NewLine +
|
||||
" }" + Environment.NewLine +
|
||||
" }).observe(document.body, { childList: true, subtree: true });" + Environment.NewLine +
|
||||
"</script>";
|
||||
}
|
||||
|
||||
private string ParseScripts(string content)
|
||||
{
|
||||
// iterate scripts
|
||||
var scripts = "";
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
var index = content.IndexOf("<script");
|
||||
while (index >= 0)
|
||||
{
|
||||
scripts += content.Substring(index, content.IndexOf("</script>", index) + 9 - index);
|
||||
index = content.IndexOf("<script", index + 1);
|
||||
}
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
|
||||
private void AddScript(Resource resource, Alias alias)
|
||||
{
|
||||
var script = CreateScript(resource, alias);
|
||||
if (resource.Location == Shared.ResourceLocation.Head)
|
||||
{
|
||||
if (!_headResources.Contains(script))
|
||||
{
|
||||
_headResources += script + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_bodyResources.Contains(script))
|
||||
{
|
||||
_bodyResources += script + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateScript(Resource resource, Alias alias)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
if (!resource.Reload)
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||
return "<script" +
|
||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||
((resource.ES6Module) ? " type=\"module\"" : "") +
|
||||
" src =\"" + url + "\"></script>"; // src at end of element due to enhanced navigation patch algorithm
|
||||
}
|
||||
else
|
||||
{
|
||||
// use custom element which can execute script on every page transition
|
||||
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// inline script
|
||||
return "<script>" + resource.Content + "</script>";
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLocalizationCookie(string culture)
|
||||
{
|
||||
Context.Response.Cookies.Append(
|
||||
CookieRequestCultureProvider.DefaultCookieName,
|
||||
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
else
|
||||
{
|
||||
// fallback to default Oqtane theme
|
||||
theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == Constants.DefaultTheme));
|
||||
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.Where(item => item.PageId == page.PageId || item.ModuleId == moduleid))
|
||||
{
|
||||
var typename = "";
|
||||
if (module.ModuleDefinition != null)
|
||||
{
|
||||
resources = AddResources(resources, module.ModuleDefinition.Resources, ResourceLevel.Module, alias, "Modules", Utilities.GetTypeName(module.ModuleDefinition.ModuleDefinitionName));
|
||||
|
||||
// handle default action
|
||||
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
|
||||
{
|
||||
action = module.ModuleDefinition.DefaultAction;
|
||||
}
|
||||
|
||||
// get typename template
|
||||
typename = module.ModuleDefinition.ControlTypeTemplate;
|
||||
if (module.ModuleDefinition.ControlTypeRoutes != "")
|
||||
{
|
||||
// process custom action routes
|
||||
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (route.StartsWith(action + "="))
|
||||
{
|
||||
typename = route.Replace(action + "=", "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create typename
|
||||
if (Constants.DefaultModuleActions.Contains(action, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
else
|
||||
{
|
||||
typename = typename.Replace(Constants.ActionToken, action);
|
||||
}
|
||||
|
||||
// ensure component exists and implements IModuleControl
|
||||
module.ModuleType = "";
|
||||
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", moduletype.Namespace);
|
||||
if (action.ToLower() == "settings" && module.ModuleDefinition != null)
|
||||
{
|
||||
// settings components are embedded within a framework settings module
|
||||
moduletype = Type.GetType(module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, action), false, true);
|
||||
if (moduletype != null)
|
||||
{
|
||||
obj = Activator.CreateInstance(moduletype) as IModuleControl;
|
||||
resources = AddResources(resources, obj.Resources, ResourceLevel.Module, alias, "Modules", moduletype.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(resource.Clone(level, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return pageresources;
|
||||
}
|
||||
|
||||
private void ManageStyleSheets(List<Resource> resources)
|
||||
{
|
||||
if (resources != null)
|
||||
{
|
||||
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
|
||||
int count = 0;
|
||||
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
count++;
|
||||
string id = "id=\"app-stylesheet-" + ResourceLevel.Page.ToString().ToLower() + "-" + batch + "-" + count.ToString("00") + "\" ";
|
||||
_styleSheets += "<link " + id + "rel=\"stylesheet\"" +
|
||||
(!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||
(!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||
" href=\"" + resource.Url + "\" type=\"text/css\"/>" + Environment.NewLine; // href at end of element due to enhanced navigation patch algorithm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ManageScripts(List<Resource> resources, Alias alias)
|
||||
{
|
||||
if (resources != null)
|
||||
{
|
||||
foreach (var resource in resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Static)
|
||||
{
|
||||
AddScript(resource, alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
Oqtane.Server/Components/_Imports.razor
Normal file
29
Oqtane.Server/Components/_Imports.razor
Normal file
|
@ -0,0 +1,29 @@
|
|||
@using System
|
||||
@using System.Linq
|
||||
@using System.Collections.Generic
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using Microsoft.JSInterop
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
|
||||
@using Oqtane.Client
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Modules
|
||||
@using Oqtane.Modules.Controls
|
||||
@using Oqtane.Providers
|
||||
@using Oqtane.Security
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Themes.Controls
|
||||
@using Oqtane.UI
|
||||
@using Oqtane.Enums
|
||||
@using Oqtane.Installer
|
||||
@using Oqtane.Interfaces
|
|
@ -59,7 +59,7 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid)
|
||||
{
|
||||
alias = _aliases.AddAlias(alias);
|
||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(alias, EntityNames.Alias, alias.AliasId, SyncEventActions.Create);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias);
|
||||
}
|
||||
else
|
||||
|
@ -79,7 +79,7 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid && alias.AliasId == id && _aliases.GetAlias(alias.AliasId, false) != null)
|
||||
{
|
||||
alias = _aliases.UpdateAlias(alias);
|
||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(alias, EntityNames.Alias, alias.AliasId, SyncEventActions.Update);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias);
|
||||
}
|
||||
else
|
||||
|
@ -100,7 +100,7 @@ namespace Oqtane.Controllers
|
|||
if (alias != null)
|
||||
{
|
||||
_aliases.DeleteAlias(id);
|
||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(alias, EntityNames.Alias, alias.AliasId, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Alias Deleted {AliasId}", id);
|
||||
|
||||
var aliases = _aliases.GetAliases();
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
file = CreateFile(file.Name, folder.FolderId, filepath);
|
||||
file = _files.AddFile(file);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Added {File}", file);
|
||||
}
|
||||
else
|
||||
|
@ -234,7 +234,7 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
|
||||
file = _files.UpdateFile(file);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.File, file.FileId, SyncEventActions.Update);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
|
||||
}
|
||||
else
|
||||
|
@ -266,7 +266,7 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
|
||||
_files.DeleteFile(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.File, file.FileId, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
|
||||
}
|
||||
else
|
||||
|
@ -341,7 +341,7 @@ namespace Oqtane.Controllers
|
|||
if (file != null)
|
||||
{
|
||||
file = _files.AddFile(file);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -429,7 +429,7 @@ namespace Oqtane.Controllers
|
|||
file = _files.UpdateFile(file);
|
||||
}
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Upload Succeeded {File}", Path.Combine(folderPath, upload));
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -586,7 +586,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
if (asAttachment)
|
||||
{
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, "Download");
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.File, file.FileId, "Download");
|
||||
return PhysicalFile(filepath, file.GetMimeType(), file.Name);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -173,7 +173,7 @@ namespace Oqtane.Controllers
|
|||
folder.Path = folder.Path + "/";
|
||||
}
|
||||
folder = _folders.AddFolder(folder);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Create);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
|
||||
}
|
||||
else
|
||||
|
@ -225,7 +225,7 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
|
||||
folder = _folders.UpdateFolder(folder);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
|
||||
}
|
||||
else
|
||||
|
@ -259,7 +259,7 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
folder.Order = order;
|
||||
_folders.UpdateFolder(folder);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
|
||||
}
|
||||
order += 2;
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ namespace Oqtane.Controllers
|
|||
Directory.Delete(_folders.GetFolderPath(folder));
|
||||
}
|
||||
_folders.DeleteFolder(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace Oqtane.Controllers
|
|||
var assemblyList = new List<ClientAssembly>();
|
||||
|
||||
var site = _sites.GetSite(alias.SiteId);
|
||||
if (site != null && (site.Runtime == "WebAssembly" || site.HybridEnabled))
|
||||
if (site != null && (site.Runtime == Runtimes.WebAssembly || site.Hybrid))
|
||||
{
|
||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
|
@ -201,7 +201,7 @@ namespace Oqtane.Controllers
|
|||
private byte[] GetZIP(string list, Alias alias)
|
||||
{
|
||||
var site = _sites.GetSite(alias.SiteId);
|
||||
if (site != null && (site.Runtime == "WebAssembly" || site.HybridEnabled))
|
||||
if (site != null && (site.Runtime == Runtimes.WebAssembly || site.Hybrid))
|
||||
{
|
||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
|
|
|
@ -109,8 +109,8 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
||||
{
|
||||
_languages.UpdateLanguage(language);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Language, language.LanguageId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Updated {Language}", language);
|
||||
}
|
||||
else
|
||||
|
@ -127,8 +127,8 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
||||
{
|
||||
language = _languages.AddLanguage(language);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Language, language.LanguageId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
||||
}
|
||||
else
|
||||
|
@ -148,8 +148,8 @@ namespace Oqtane.Controllers
|
|||
if (language != null && language.SiteId == _alias.SiteId)
|
||||
{
|
||||
_languages.DeleteLanguage(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Language, language.LanguageId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -136,8 +136,8 @@ namespace Oqtane.Controllers
|
|||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Page, module.PageId, PermissionNames.Edit))
|
||||
{
|
||||
module = _modules.AddModule(module);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Module, module.ModuleId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
|
||||
}
|
||||
else
|
||||
|
@ -187,8 +187,8 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Module, module.ModuleId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
||||
}
|
||||
else
|
||||
|
@ -209,8 +209,8 @@ namespace Oqtane.Controllers
|
|||
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||
{
|
||||
_modules.DeleteModule(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Module, module.ModuleId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
|
||||
}
|
||||
else
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user