User experience improvements

This commit is contained in:
Shaun Walker
2021-04-17 19:18:24 -04:00
parent 1d3a79437c
commit cbe843bafc
84 changed files with 1020 additions and 710 deletions

View File

@ -0,0 +1,40 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@if (BreadCrumbPages.Any())
{
<span class="app-breadcrumbs">
<ol class="breadcrumb">
@foreach (var p in BreadCrumbPages)
{
if (p.PageId == PageState.Page.PageId)
{
<li class="breadcrumb-item active">
<a href="@NavigateUrl(p.Path)">@p.Name</a>
</li>
}
else
{
<li class="breadcrumb-item">
<a href="@NavigateUrl(p.Path)">@p.Name</a>
</li>
}
}
</ol>
</span>
}
@code {
protected IEnumerable<Page> BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList();
private IEnumerable<Page> GetBreadCrumbPages()
{
var page = PageState.Page;
do
{
yield return page;
page = PageState.Pages.FirstOrDefault(p => page != null && p.PageId == page.ParentId);
} while (page != null);
}
}

View File

@ -0,0 +1,613 @@
@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 IStringLocalizer<ControlPanel> Localizer
@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
<div class="app-controlpanel" style="@_display">
<div class="@CardClass">
<div class="@HeaderClass">
<span class="font-weight-bold">@Localizer["Control Panel"]</span>
<button type="button" class="close" @onclick="HideControlPanel" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="@BodyClass">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<div class="row">
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Navigate("Admin"))>@Localizer["Admin Dashboard"]</button>
</div>
</div>
<hr class="app-rule" />
<div class="row">
<div class="col text-center">
<label class="control-label">@Localizer["Page Management:"] </label>
</div>
</div>
<div class="row">
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Navigate("Add"))>@Localizer["Add"]</button>
</div>
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Navigate("Edit"))>@Localizer["Edit"]</button>
</div>
<div class="col">
<button class="btn btn-danger btn-block mx-auto" @onclick="ConfirmDelete">@Localizer["Delete"]</button>
</div>
</div>
<br />
<div class="row">
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
{
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Publish("unpublish"))>@Localizer["Unpublish Page"]</button>
</div>
}
else
{
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Publish("publish"))>@Localizer["Publish Page"]</button>
</div>
}
</div>
}
@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["Delete Page"]</h5>
<button type="button" class="close" @onclick="ConfirmDelete" aria-label="Close">&times;</button>
</div>
<div class="modal-body">
<p>Are You Sure You Want To Delete This Page?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" @onclick="DeletePage">@Localizer["Delete"]</button>
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@Localizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
<hr class="app-rule" />
<div class="row">
<div class="col text-center">
<label for="Module" class="control-label">@Localizer["Module Management:"] </label>
<select class="form-control" @bind="@ModuleType">
<option value="new">@Localizer["Add New Module"]</option>
<option value="existing">@Localizer["Add Existing Module"]</option>
</select>
@if (ModuleType == "new")
{
@if (_moduleDefinitions != null)
{
<select class="form-control" @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-control" @onchange="(e => ModuleChanged(e))">
@if (ModuleDefinitionName == "-")
{
<option value="-" selected>&lt;@Localizer["Select Module"]&gt;</option>
}
else
{
<option value="-">&lt;@Localizer["Select Module"]&gt;</option>
}
@foreach (var moduledefinition in _moduleDefinitions)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
}
}
</select>
@((MarkupString) Description)
}
}
else
{
<select class="form-control" @onchange="(e => PageChanged(e))">
<option value="-">&lt;@Localizer["Select Page"]&gt;</option>
@foreach (Page p in _pages)
{
<option value="@p.PageId">@p.Name</option>
}
</select>
<select class="form-control" @bind="@ModuleId">
<option value="-">&lt;@Localizer["Select Module"]&gt;</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>
@if (_pane.Length > 1)
{
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">@Localizer["Pane:"] </label>
<select class="form-control" @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="Container" class="control-label">@Localizer["Container:"] </label>
<select class="form-control" @bind="@ContainerType">
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<br />
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick="@AddModule">@Localizer["Add Module To Page"]</button>
@((MarkupString) Message)
</div>
</div>
</div>
}
@if (ShowLanguageSwitcher)
{
<LanguageSwitcher />
}
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
{
if (PageState.EditMode)
{
<button type="button" class="btn @ButtonClass active" data-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-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
}
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
<button type="button" class="btn @ButtonClass" @onclick="ShowControlPanel">
<span class="oi oi-cog"></span>
</button>
}
@code{
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 _display = "display: none;";
private string _category = "Common";
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 Category
{
get => _category;
private set
{
if (_category != value)
{
_category = value;
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
ModuleDefinitionName = "-";
Description = "";
StateHasChanged();
_ = UpdateSettingsAsync();
}
}
}
protected string Pane
{
get => _pane;
private set
{
if (_pane != value)
{
_pane = value;
_ = UpdateSettingsAsync();
}
}
}
protected string Description { get; private set; } = "";
protected string Title { get; private set; } = "";
protected string ContainerType { get; private set; } = "";
protected string Message { get; private set; } = "";
[Parameter]
public string ButtonClass { get; set; } = "btn-outline-secondary";
[Parameter]
public string CardClass { get; set; } = "card border-secondary mb-3";
[Parameter]
public string HeaderClass { get; set; } = "card-header";
[Parameter]
public string BodyClass { get; set; } = "card-body";
[Parameter]
public bool ShowLanguageSwitcher { get; set; } = true;
protected override async Task OnParametersSetAsync()
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
_pages?.Clear();
foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
_pages.Add(p);
}
}
await LoadSettingsAsync();
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();
}
}
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);
Description = "<br /><div class=\"alert alert-info\" role=\"alert\">" + moduleDefinition.Description + "</div>";
}
else
{
Description = "";
}
StateHasChanged();
}
private void PageChanged(ChangeEventArgs e)
{
PageId = (string)e.Value;
if (PageId != "-")
{
_modules = PageState.Modules
.Where(module => module.PageId == int.Parse(PageId)
&& !module.IsDeleted
&& UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
.ToList();
}
ModuleId = "-";
StateHasChanged();
}
private async Task AddModule()
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
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;
module.Permissions = PageState.Page.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 = int.MaxValue;
pageModule.ContainerType = ContainerType;
if (pageModule.ContainerType == PageState.Site.DefaultContainerType)
{
pageModule.ContainerType = "";
}
await PageModuleService.AddPageModuleAsync(pageModule);
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
Message = $"<br /><div class=\"alert alert-success\" role=\"alert\">{Localizer["Module Added To Page"]}</div>";
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
Message = $"<br /><div class=\"alert alert-warning\" role=\"alert\">{Localizer["You Must Select A Module"]}</div>";
}
}
else
{
Message = $"<br /><div class=\"alert alert-error\" role=\"alert\">{Localizer["Not Authorized"]}</div>";
}
}
private async Task ToggleEditMode(bool EditMode)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
if (EditMode)
{
PageState.EditMode = false;
}
else
{
PageState.EditMode = true;
}
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
}
else
{
if (PageState.Page.IsPersonalizable && PageState.User != null)
{
await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
PageState.EditMode = true;
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
}
}
}
private void ShowControlPanel()
{
Message = "";
_display = "width: 25%; min-width: 375px;";
StateHasChanged();
}
private void HideControlPanel()
{
Message = "";
_display = "width: 0%;";
StateHasChanged();
}
private void Navigate(string location)
{
HideControlPanel();
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(PageState.Page.Path, module.ModuleId, "Index", ""));
}
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(PageState.Page.Path, module.ModuleId, location, "cp=" + PageState.Page.PageId);
break;
case "Edit":
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString() + "&cp=" + PageState.Page.PageId);
break;
}
}
if (url != "")
{
NavigationManager.NavigateTo(url);
}
break;
}
}
private async void Publish(string action)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
List<PermissionString> permissions;
if (action == "publish")
{
// publish all modules
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
{
permissions = UserSecurity.GetPermissionStrings(module.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(module);
}
}
// publish page
var page = PageState.Page;
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
switch (action)
{
case "publish":
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
break;
case "unpublish":
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
break;
}
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
await PageService.UpdatePageAsync(page);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload"));
}
}
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);
}
}
private string settingCategory = "CP-category";
private string settingPane = "CP-pane";
private string _pane = "";
private async Task LoadSettingsAsync()
{
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
_category = SettingService.GetSetting(settings, settingCategory, "Common");
var pane = SettingService.GetSetting(settings, settingPane, "");
_pane = PageState.Page.Panes.Contains(pane) ? pane : PaneNames.Admin;
}
private async Task UpdateSettingsAsync()
{
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
SettingService.SetSetting(settings, settingCategory, _category);
SettingService.SetSetting(settings, settingPane, _pane);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
}
}

View File

@ -0,0 +1,44 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@using Oqtane.Models
@inject ILanguageService LanguageService
@inject NavigationManager NavigationManager
@if (_supportedCultures?.Count() > 1)
{
<div class="btn-group" role="group">
<button id="btnCultures" type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="oi oi-globe"></span>
</button>
<div class="dropdown-menu" aria-labelledby="btnCultures">
@foreach (var culture in _supportedCultures)
{
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="#" @onclick="@(async e => await SetCultureAsync(culture.Name))">@culture.DisplayName</a>
}
</div>
</div>
}
@code{
private IEnumerable<Culture> _supportedCultures;
protected override async Task OnParametersSetAsync()
{
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
}
private async Task SetCultureAsync(string culture)
{
if (culture != CultureInfo.CurrentUICulture.Name)
{
var interop = new Interop(JSRuntime);
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
}
}
}

View File

@ -0,0 +1,17 @@
@namespace Oqtane.Themes.Controls
@inherits LoginBase
@inject IStringLocalizer<Login> Localizer
<span class="app-login">
<AuthorizeView>
<Authorizing>
<text>...</text>
</Authorizing>
<Authorized>
<button type="button" class="btn btn-primary" @onclick="LogoutUser">@Localizer["Logout"]</button>
</Authorized>
<NotAuthorized>
<button type="button" class="btn btn-primary" @onclick="LoginUser">@Localizer["Login"]</button>
</NotAuthorized>
</AuthorizeView>
</span>

View File

@ -0,0 +1,50 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Providers;
using Oqtane.Services;
using Oqtane.UI;
namespace Oqtane.Themes.Controls
{
public class LoginBase : ThemeControlBase
{
[Inject] public NavigationManager NavigationManager {get;set;}
[Inject]public IUserService UserService {get;set;}
[Inject]public IJSRuntime jsRuntime {get;set;}
[Inject]public IServiceProvider ServiceProvider {get;set;}
protected void LoginUser()
{
var returnurl = PageState.Alias.Path;
if (PageState.Page.Path != "/")
{
returnurl += "/" + PageState.Page.Path;
}
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
}
protected async Task LogoutUser()
{
await UserService.LogoutUserAsync(PageState.User);
PageState.User = null;
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
{
// server-side Blazor
var interop = new Interop(jsRuntime);
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = (PageState.Alias.Path + "/" + PageState.Page.Path) };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/logout/", fields);
}
else
{
// client-side Blazor
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload"));
}
}
}
}

View File

@ -0,0 +1,12 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@if (PageState.Site.LogoFileId != null)
{
<span class="app-logo">
<a href="@PageState.Alias.Path">
<img class="img-fluid" src="@ContentUrl(PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name" />
</a>
</span>
}

View File

@ -0,0 +1,20 @@
@namespace Oqtane.Themes.Controls
@switch (Orientation)
{
case "Horizontal":
<MenuHorizontal/>
break;
default: // Vertical
{
<MenuVertical/>
break;
}
}
@code{
[Parameter]
public string Orientation { get; set; }
}

View File

@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
using Oqtane.Security;
using Oqtane.Shared;
namespace Oqtane.Themes.Controls
{
public class MenuBase : ThemeControlBase
{
protected IEnumerable<Page> MenuPages => GetMenuPages().ToList();
protected string GetTarget(Page page)
{
return page.Url != null && page.Url.StartsWith("http") ? "_new" : string.Empty;
}
protected string GetUrl(Page page)
{
return string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
}
private IEnumerable<Page> GetMenuPages()
{
var securityLevel = int.MaxValue;
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted))
{
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
securityLevel = int.MaxValue;
yield return p;
}
else
{
if (securityLevel == int.MaxValue)
{
securityLevel = p.Level;
}
}
}
}
}
}

View File

@ -0,0 +1,17 @@
@namespace Oqtane.Themes.Controls
@inherits MenuBase
@if (MenuPages.Any())
{
<span class="app-menu-toggler">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
</span>
<div class="app-menu">
<div class="collapse navbar-collapse" id="Menu">
<MenuItemsHorizontal ParentPage="null" Pages="MenuPages" />
</div>
</div>
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Components;
using Oqtane.Models;
using Oqtane.UI;
namespace Oqtane.Themes.Controls
{
public abstract class MenuItemsBase : MenuBase
{
[Parameter()]
public Page ParentPage { get; set; }
[Parameter()]
public IEnumerable<Page> Pages { get; set; }
protected IEnumerable<Page> GetChildPages()
{
return Pages
.Where(e => e.ParentId == ParentPage?.PageId)
.OrderBy(e => e.Order)
.AsEnumerable();
}
}
}

View File

@ -0,0 +1,77 @@
@namespace Oqtane.Themes.Controls
@inherits MenuItemsBase
@if (ParentPage != null)
{
<div class="dropdown-menu" aria-labelledby="@($"navbarDropdown{ParentPage.PageId}")">
@foreach (var childPage in GetChildPages())
{
if (childPage.PageId == PageState.Page.PageId)
{
<a class="nav-link active px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name <span class="sr-only">(current)</span>
</a>
}
else
{
<a class="nav-link px-3" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name
</a>
}
}
</div>
}
else
{
<ul class="navbar-nav mr-auto">
@foreach (var childPage in GetChildPages())
{
if (!Pages.Any(e => e.ParentId == childPage.PageId))
{
if (childPage.PageId == PageState.Page.PageId)
{
<li class="nav-item active">
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name <span class="sr-only">(current)</span>
</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name
</a>
</li>
}
}
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-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name <span class="sr-only">(current)</span>
</a>
<MenuItemsHorizontal ParentPage="childPage" Pages="Pages" />
</li>
}
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-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name
</a>
<MenuItemsHorizontal ParentPage="childPage" Pages="Pages" />
</li>
}
}
}
</ul>
}

View File

@ -0,0 +1,61 @@
@namespace Oqtane.Themes.Controls
@inherits MenuItemsBase
@if (ParentPage != null)
{
foreach (var childPage in GetChildPages())
{
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)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name <span class="sr-only">(current)</span>
</a>
</li>
}
else
{
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name
</a>
</li>
}
if (Pages.Any(e => e.ParentId == childPage.PageId))
{
<MenuItemsVertical ParentPage="childPage" Pages="Pages" />
}
}
}
else
{
<ul class="nav flex-column">
@foreach (var childPage in GetChildPages())
{
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)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name <span class="sr-only">(current)</span>
</a>
</li>
}
else
{
<li class="nav-item px-3" style="margin-left: @(childPage.Level * 15)px;">
<a class="nav-link" href="@GetUrl(childPage)" target="@GetTarget(childPage)">
<span class="@childPage.Icon" aria-hidden="true" />
@childPage.Name
</a>
</li>
}
if (Pages.Any(e => e.ParentId == childPage.PageId))
{
<MenuItemsVertical ParentPage="childPage" Pages="Pages" />
}
}
</ul>
}

View File

@ -0,0 +1,16 @@
@namespace Oqtane.Themes.Controls
@inherits MenuBase
@if (MenuPages.Any())
{
<span class="app-menu-toggler">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
</span>
<div class="app-menu">
<div class="collapse navbar-collapse" id="Menu">
<MenuItemsVertical ParentPage="null" Pages="MenuPages" />
</div>
</div>
}

View File

@ -0,0 +1,37 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@inject IStringLocalizer<UserProfile> Localizer
@inject NavigationManager NavigationManager
<span class="app-profile">
<AuthorizeView>
<Authorizing>
<text>...</text>
</Authorizing>
<Authorized>
<button type="button" class="btn btn-primary" @onclick="UpdateProfile">@context.User.Identity.Name</button>
</Authorized>
<NotAuthorized>
@if (PageState.Site.AllowRegistration)
{
<button type="button" class="btn btn-primary" @onclick="RegisterUser">@Localizer["Register"]</button>
}
</NotAuthorized>
</AuthorizeView>
</span>
@code {
private void RegisterUser()
{
NavigationManager.NavigateTo(NavigateUrl("register"));
}
private void UpdateProfile()
{
NavigationManager.NavigateTo(NavigateUrl("profile"));
}
}