2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 .NET Foundation
|
Copyright (c) 2018-2020 .NET Foundation
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0-preview3.20168.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0-preview5.20216.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0-preview3.20168.3" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0-preview5.20216.8" PrivateAssets="all" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="3.2.0-preview3.20175.8" />
|
<PackageReference Include="System.Net.Http.Json" Version="3.2.0-preview5.20210.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="3.2.0-preview3.20168.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="3.2.0-preview5.20216.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.2" />
|
||||||
|
@ -1,14 +1,27 @@
|
|||||||
@namespace Oqtane.Themes.BlazorTheme
|
@namespace Oqtane.Themes.BlazorTheme
|
||||||
@inherits ThemeBase
|
@inherits ThemeBase
|
||||||
|
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<Breadcrumbs />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div align="center"><Logo /></div>
|
<nav class="navbar">
|
||||||
|
<Logo />
|
||||||
|
<button class="navbar-toggler" aria-expanded="false" aria-controls="navbarSupportedContent"
|
||||||
|
aria-label="Toggle navigation" type="button" data-toggle="collapse"
|
||||||
|
data-target="#navbarSupportedContent">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
<Menu Orientation="Vertical" />
|
<Menu Orientation="Vertical" />
|
||||||
</div>
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="top-row px-4">
|
<div class="top-row px-4">
|
||||||
<Breadcrumbs /> <div class="ml-md-auto"><UserProfile /> <Login /> <ControlPanel /></div>
|
<div class="ml-md-auto"><UserProfile /> <Login /> <ControlPanel /></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row px-4">
|
<div class="row px-4">
|
||||||
|
@ -1,32 +1,34 @@
|
|||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@inherits ThemeControlBase
|
@inherits ThemeControlBase
|
||||||
|
|
||||||
@if (breadcrumbs != string.Empty)
|
@if (BreadCrumbPages.Any())
|
||||||
{
|
{
|
||||||
@((MarkupString)breadcrumbs)
|
<ol class="breadcrumb">
|
||||||
|
@foreach (var p in BreadCrumbPages)
|
||||||
|
{
|
||||||
|
<li class="breadcrumb-item @ActiveClass(p)">
|
||||||
|
<a href="@NavigateUrl(p.Path)">@p.Name</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ol>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
string breadcrumbs = string.Empty;
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected IEnumerable<Page> BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList();
|
||||||
|
|
||||||
|
protected string ActiveClass(Page page)
|
||||||
{
|
{
|
||||||
breadcrumbs = string.Empty;
|
return (page.PageId == PageState.Page.PageId) ? " active" : string.Empty;
|
||||||
int? pageid = PageState.Page.PageId;
|
|
||||||
for (int i = PageState.Pages.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var p = PageState.Pages[i];
|
|
||||||
if (p.PageId == pageid)
|
|
||||||
{
|
|
||||||
breadcrumbs = "<li class=\"breadcrumb-item" + ((p.PageId == PageState.Page.PageId) ? " active" : string.Empty) +
|
|
||||||
"\"><a href=\"" + NavigateUrl(p.Path) + "\">" + p.Name + "</a></li>" + breadcrumbs;
|
|
||||||
pageid = p.ParentId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (breadcrumbs != "")
|
private IEnumerable<Page> GetBreadCrumbPages()
|
||||||
{
|
{
|
||||||
breadcrumbs = "<ol class=\"breadcrumb\">" + breadcrumbs + "</ol>";
|
var page = PageState.Page;
|
||||||
}
|
do
|
||||||
|
{
|
||||||
|
yield return page;
|
||||||
|
page = PageState.Pages.FirstOrDefault(p => page != null && p.PageId == page.ParentId);
|
||||||
|
} while (page != null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@inherits ThemeControlBase
|
@inherits LoginBase
|
||||||
@inject NavigationManager NavigationManager
|
|
||||||
@inject IUserService UserService
|
|
||||||
@inject IJSRuntime jsRuntime
|
|
||||||
@inject IServiceProvider ServiceProvider
|
|
||||||
|
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorizing>
|
<Authorizing>
|
||||||
@ -16,38 +12,3 @@
|
|||||||
<button type="button" class="btn btn-primary" @onclick="LoginUser">Login</button>
|
<button type="button" class="btn btn-primary" @onclick="LoginUser">Login</button>
|
||||||
</NotAuthorized>
|
</NotAuthorized>
|
||||||
</AuthorizeView>
|
</AuthorizeView>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private void LoginUser()
|
|
||||||
{
|
|
||||||
var returnurl = PageState.Alias.Path;
|
|
||||||
if (PageState.Page.Path != "/")
|
|
||||||
{
|
|
||||||
returnurl += "/" + PageState.Page.Path;
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl("login", "returnurl=" + returnurl));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LogoutUser()
|
|
||||||
{
|
|
||||||
await UserService.LogoutUserAsync(PageState.User);
|
|
||||||
|
|
||||||
if (PageState.Runtime == 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("/pages/logout/", fields);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// client-side Blazor
|
|
||||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
|
||||||
authstateprovider.NotifyAuthenticationChanged();
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
49
Oqtane.Client/Themes/Controls/LoginBase.cs
Normal file
49
Oqtane.Client/Themes/Controls/LoginBase.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
if (PageState.Runtime == 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("/pages/logout/", fields);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// client-side Blazor
|
||||||
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||||
|
authstateprovider.NotifyAuthenticationChanged();
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,17 +2,20 @@
|
|||||||
@inherits ThemeControlBase
|
@inherits ThemeControlBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
@((MarkupString)logo)
|
@if (PageState.Site.LogoFileId != null)
|
||||||
|
{
|
||||||
|
<a href="@Href">
|
||||||
|
<img class="img-fluid" src="@ContentUrl(PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name"/>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
string logo = "";
|
string Href
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
|
||||||
{
|
{
|
||||||
if (PageState.Site.LogoFileId != null)
|
get
|
||||||
{
|
{
|
||||||
var uri = new Uri(NavigationManager.Uri);
|
var uri = new Uri(NavigationManager.Uri);
|
||||||
logo = "<a href=\"" + uri.Scheme + "://" + uri.Authority + "\"><img src=\"" + ContentUrl(PageState.Site.LogoFileId.Value) + "\" alt=\"" + PageState.Site.Name + "\"/></a>";
|
return $"{uri.Scheme}://{uri.Authority}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,133 +1,20 @@
|
|||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@inherits ThemeControlBase
|
|
||||||
|
|
||||||
@if (menu != string.Empty)
|
@switch (Orientation)
|
||||||
{
|
{
|
||||||
<div class="app-menu">
|
case "Horizontal":
|
||||||
@((MarkupString)menu)
|
<MenuHorizontal/>
|
||||||
</div>
|
break;
|
||||||
|
default: // Vertical
|
||||||
|
{
|
||||||
|
<MenuVertical/>
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@code{
|
@code{
|
||||||
private string menu = string.Empty;
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Orientation { get; set; }
|
public string Orientation { get; set; }
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
|
||||||
{
|
|
||||||
switch (Orientation)
|
|
||||||
{
|
|
||||||
case "Horizontal":
|
|
||||||
CreateHorizontalMenu();
|
|
||||||
break;
|
|
||||||
default: // Vertical
|
|
||||||
CreateVerticalMenu();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateVerticalMenu()
|
|
||||||
{
|
|
||||||
var level = -1;
|
|
||||||
var securitylevel = int.MaxValue;
|
|
||||||
|
|
||||||
menu = "<ul class=\"nav flex-column\">\n";
|
|
||||||
|
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted))
|
|
||||||
{
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions) && p.Level <= securitylevel)
|
|
||||||
{
|
|
||||||
securitylevel = int.MaxValue;
|
|
||||||
|
|
||||||
menu += "<li class=\"nav-item px-3\">";
|
|
||||||
if (string.IsNullOrEmpty(p.Url))
|
|
||||||
{
|
|
||||||
menu += "<a href=\"" + NavigateUrl(p.Path) + "\" class=\"nav-link\" style=\"padding-left: " + ((p.Level + 1) * 15).ToString() + "px !important;\">";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string target = String.Empty;
|
|
||||||
if (p.Url.StartsWith("http"))
|
|
||||||
{
|
|
||||||
target = " target=\"_new\"";
|
|
||||||
}
|
|
||||||
menu += "<a href=\"" + p.Url + "\" class=\"nav-link\" style=\"padding-left: " + ((p.Level + 1) * 15).ToString() + "px !important;\"" + target + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.HasChildren)
|
|
||||||
{
|
|
||||||
menu += "<i class=\"oi oi-chevron-right\"></i>";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.Icon != string.Empty)
|
|
||||||
{
|
|
||||||
menu += "<span class=\"oi oi-" + p.Icon + "\" aria-hidden=\"true\"></span>";
|
|
||||||
}
|
|
||||||
|
|
||||||
menu += p.Name;
|
|
||||||
menu += "</a>\n";
|
|
||||||
menu += "</li>\n";
|
|
||||||
|
|
||||||
level = p.Level;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (securitylevel == int.MaxValue)
|
|
||||||
{
|
|
||||||
securitylevel = p.Level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu += "</ul>";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateHorizontalMenu()
|
|
||||||
{
|
|
||||||
var url = String.Empty;
|
|
||||||
var target = String.Empty;
|
|
||||||
|
|
||||||
menu = "<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>";
|
|
||||||
menu += "<div class=\"collapse navbar-collapse\" id=\"Menu\">";
|
|
||||||
menu += "<ul class=\"navbar-nav mr-auto\">";
|
|
||||||
|
|
||||||
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation && !item.IsDeleted))
|
|
||||||
{
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions) && p.ParentId == PageState.Page.ParentId && p.Level == PageState.Page.Level)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(p.Url))
|
|
||||||
{
|
|
||||||
url = NavigateUrl(p.Path);
|
|
||||||
target = String.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
url = p.Url;
|
|
||||||
if (p.Url.StartsWith("http"))
|
|
||||||
{
|
|
||||||
target = " target=\"_new\"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.PageId == PageState.Page.PageId)
|
|
||||||
{
|
|
||||||
menu += "<li class=\"nav-item active\">" +
|
|
||||||
"<a class=\"nav-link\" href=\"" + NavigateUrl(p.Path) + "\">" +
|
|
||||||
((p.Icon != string.Empty) ? "<span class=\"oi oi-" + p.Icon + "\" aria-hidden=\"true\"></span> " : string.Empty) +
|
|
||||||
p.Name + " <span class=\"sr-only\">(current)</span></a></li>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
menu += "<li class=\"nav-item\">" +
|
|
||||||
"<a class=\"nav-link\" href=\"" + NavigateUrl(p.Path) + "\">" +
|
|
||||||
((p.Icon != string.Empty) ? "<span class=\"oi oi-" + p.Icon + "\" aria-hidden=\"true\"></span> " : string.Empty) +
|
|
||||||
p.Name + "</a></li>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu += "</ul>";
|
|
||||||
menu += "</div>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
43
Oqtane.Client/Themes/Controls/MenuBase.cs
Normal file
43
Oqtane.Client/Themes/Controls/MenuBase.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
Oqtane.Client/Themes/Controls/MenuHorizontal.Razor
Normal file
42
Oqtane.Client/Themes/Controls/MenuHorizontal.Razor
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
@namespace Oqtane.Themes.Controls
|
||||||
|
@inherits MenuBase
|
||||||
|
@if (MenuPages.Any())
|
||||||
|
{
|
||||||
|
<div class="app-menu">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="Menu">
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
@foreach (var p in MenuPages)
|
||||||
|
{
|
||||||
|
if (p.PageId == PageState.Page.PageId)
|
||||||
|
{
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="@NavigateUrl(p.Path)">
|
||||||
|
@if (p.Icon != string.Empty)
|
||||||
|
{
|
||||||
|
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||||
|
}
|
||||||
|
@p.Name<span class="sr-only">(current)</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="@NavigateUrl(p.Path)">
|
||||||
|
@if (p.Icon != string.Empty)
|
||||||
|
{
|
||||||
|
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||||
|
}
|
||||||
|
@p.Name
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
27
Oqtane.Client/Themes/Controls/MenuVertical.razor
Normal file
27
Oqtane.Client/Themes/Controls/MenuVertical.razor
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@namespace Oqtane.Themes.Controls
|
||||||
|
@inherits MenuBase
|
||||||
|
@if (MenuPages.Any())
|
||||||
|
{
|
||||||
|
<div class="app-menu">
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
@foreach (var p in MenuPages)
|
||||||
|
{
|
||||||
|
<li class="nav-item px-3">
|
||||||
|
<a href="@GetUrl(p)" class="nav-link" style="padding-left:@((p.Level + 1) * 15)px !important;" target="@GetTarget(p)">
|
||||||
|
|
||||||
|
@if (p.HasChildren)
|
||||||
|
{
|
||||||
|
<i class="oi oi-chevron-right"></i>
|
||||||
|
}
|
||||||
|
@if (p.Icon != string.Empty)
|
||||||
|
{
|
||||||
|
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||||
|
}
|
||||||
|
@p.Name
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
@ -1,135 +1,20 @@
|
|||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@inherits ContainerBase
|
@inherits ModuleActionsBase
|
||||||
@inject NavigationManager NavigationManager
|
|
||||||
@inject IUserService UserService
|
|
||||||
@inject IPageModuleService PageModuleService
|
|
||||||
|
|
||||||
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
|
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
|
||||||
{
|
{
|
||||||
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
|
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
|
||||||
<div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
|
<div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
|
||||||
@foreach (var action in actions)
|
@foreach (var action in Actions)
|
||||||
{
|
{
|
||||||
if (action.Action != "")
|
if (string.IsNullOrEmpty(action.Name))
|
||||||
{
|
{
|
||||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action.Action))">@action.Name</a>
|
<div class="dropdown-divider"></div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="dropdown-divider"></div>
|
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">@action.Name</a>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
|
||||||
private List<ActionViewModel> actions;
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
|
||||||
{
|
|
||||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
|
|
||||||
{
|
|
||||||
actions = new List<ActionViewModel>();
|
|
||||||
actions.Add(new ActionViewModel { Action = "settings", Name = "Manage Settings" });
|
|
||||||
|
|
||||||
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "")
|
|
||||||
{
|
|
||||||
actions.Add(new ActionViewModel { Action = "import", Name = "Import Content" });
|
|
||||||
actions.Add(new ActionViewModel { Action = "export", Name = "Export Content" });
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.Add(new ActionViewModel { Action = "delete", Name = "Delete Module" });
|
|
||||||
actions.Add(new ActionViewModel { Action = "", Name = "" });
|
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex > 0)
|
|
||||||
{
|
|
||||||
actions.Add(new ActionViewModel { Action = "<<", Name = "Move To Top" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex > 0)
|
|
||||||
{
|
|
||||||
actions.Add(new ActionViewModel { Action = "<", Name = "Move Up" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
|
||||||
{
|
|
||||||
actions.Add(new ActionViewModel { Action = ">", Name = "Move Down" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
|
||||||
{
|
|
||||||
actions.Add(new ActionViewModel { Action = ">>", Name = "Move To Bottom" });
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string pane in PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
|
||||||
if (pane != ModuleState.Pane)
|
|
||||||
{
|
|
||||||
actions.Add(new ActionViewModel { Action = pane, Name = "Move To " + pane + " Pane" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task ModuleAction(string action)
|
|
||||||
{
|
|
||||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
|
|
||||||
{
|
|
||||||
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
|
||||||
|
|
||||||
string url = NavigateUrl();
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case "<<":
|
|
||||||
pagemodule.Order = 0;
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
break;
|
|
||||||
case "<":
|
|
||||||
pagemodule.Order -= 3;
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
break;
|
|
||||||
case ">":
|
|
||||||
pagemodule.Order += 3;
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
break;
|
|
||||||
case ">>":
|
|
||||||
pagemodule.Order = int.MaxValue;
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
break;
|
|
||||||
case "settings":
|
|
||||||
url = EditUrl(pagemodule.ModuleId, "Settings");
|
|
||||||
break;
|
|
||||||
case "import":
|
|
||||||
url = EditUrl(pagemodule.ModuleId, "Import");
|
|
||||||
break;
|
|
||||||
case "export":
|
|
||||||
url = EditUrl(pagemodule.ModuleId, "Export");
|
|
||||||
break;
|
|
||||||
case "delete":
|
|
||||||
pagemodule.IsDeleted = true;
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
break;
|
|
||||||
default: // move to pane
|
|
||||||
string pane = pagemodule.Pane;
|
|
||||||
pagemodule.Pane = action;
|
|
||||||
pagemodule.Order = int.MaxValue; // add to bottom of pane
|
|
||||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
|
||||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pane);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
NavigationManager.NavigateTo(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ActionViewModel
|
|
||||||
{
|
|
||||||
public string Action { set; get; }
|
|
||||||
public string Name { set; get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
164
Oqtane.Client/Themes/Controls/ModuleActionsBase.cs
Normal file
164
Oqtane.Client/Themes/Controls/ModuleActionsBase.cs
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Security;
|
||||||
|
using Oqtane.Services;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
// ReSharper disable UnassignedGetOnlyAutoProperty
|
||||||
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
|
||||||
|
namespace Oqtane.Themes.Controls
|
||||||
|
{
|
||||||
|
public class ModuleActionsBase : ContainerBase
|
||||||
|
{
|
||||||
|
[Inject] public NavigationManager NavigationManager { get; set; }
|
||||||
|
[Inject] public IPageModuleService PageModuleService { get; set; }
|
||||||
|
|
||||||
|
protected List<ActionViewModel> Actions;
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
Actions = GetActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual List<ActionViewModel> GetActions()
|
||||||
|
{
|
||||||
|
var actionList = new List<ActionViewModel>();
|
||||||
|
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
|
||||||
|
|
||||||
|
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "")
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
|
||||||
|
}
|
||||||
|
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m)});
|
||||||
|
actionList.Add(new ActionViewModel {Name = ""});
|
||||||
|
|
||||||
|
if (ModuleState.PaneModuleIndex > 0)
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModuleState.PaneModuleIndex > 0)
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string pane in PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
if (pane != ModuleState.Pane)
|
||||||
|
{
|
||||||
|
actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> EditUrlAsync(string url, int moduleId, string import)
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
EditUrl(moduleId, import);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task ModuleAction(ActionViewModel action)
|
||||||
|
{
|
||||||
|
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
|
||||||
|
{
|
||||||
|
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||||
|
|
||||||
|
string url = NavigateUrl();
|
||||||
|
|
||||||
|
if (action.Action != null)
|
||||||
|
{
|
||||||
|
url = await action.Action(url, pagemodule);
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationManager.NavigateTo(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> MoveToPane(string url, string newPane, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
string oldPane = pagemodule.Pane;
|
||||||
|
pagemodule.Pane = newPane;
|
||||||
|
pagemodule.Order = int.MaxValue; // add to bottom of pane
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> DeleteModule(string url, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
pagemodule.IsDeleted = true;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> Settings(string url, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
url = EditUrl(pagemodule.ModuleId, "Settings");
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> MoveTop(string s, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
pagemodule.Order = 0;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> MoveBottom(string s, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
pagemodule.Order = int.MaxValue;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> MoveUp(string s, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
pagemodule.Order -= 3;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> MoveDown(string s, PageModule pagemodule)
|
||||||
|
{
|
||||||
|
pagemodule.Order += 3;
|
||||||
|
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||||
|
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActionViewModel
|
||||||
|
{
|
||||||
|
public string Name { set; get; }
|
||||||
|
|
||||||
|
public Func<string, PageModule, Task<string>> Action { set; get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -179,31 +179,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
var installation = await InstallationService.Install(config);
|
var installation = await InstallationService.Install(config);
|
||||||
//TODO: Should be moved to Database manager
|
|
||||||
if (installation.Success)
|
if (installation.Success)
|
||||||
{
|
{
|
||||||
Site site = new Site();
|
|
||||||
site.TenantId = -1; // will be populated on server
|
|
||||||
site.Name = "Default Site";
|
|
||||||
site.LogoFileId = null;
|
|
||||||
site.FaviconFileId = null;
|
|
||||||
site.DefaultThemeType = Constants.DefaultTheme;
|
|
||||||
site.DefaultLayoutType = Constants.DefaultLayout;
|
|
||||||
site.DefaultContainerType = Constants.DefaultContainer;
|
|
||||||
site.PwaIsEnabled = false;
|
|
||||||
site.PwaAppIconFileId = null;
|
|
||||||
site.PwaSplashIconFileId = null;
|
|
||||||
site.AllowRegistration = false;
|
|
||||||
site = await SiteService.AddSiteAsync(site, null);
|
|
||||||
|
|
||||||
User user = new User();
|
|
||||||
user.SiteId = site.SiteId;
|
|
||||||
user.Username = _hostUsername;
|
|
||||||
user.Password = _hostPassword;
|
|
||||||
user.Email = _hostEmail;
|
|
||||||
user.DisplayName = _hostUsername;
|
|
||||||
user = await UserService.AddUserAsync(user);
|
|
||||||
|
|
||||||
NavigationManager.NavigateTo("", true);
|
NavigationManager.NavigateTo("", true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -44,6 +44,7 @@ namespace Oqtane.Controllers
|
|||||||
_config.Reload();
|
_config.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_databaseManager.BuildDefaultSite(config.Password, config.HostEmail);
|
||||||
installation.Success = true;
|
installation.Success = true;
|
||||||
return installation;
|
return installation;
|
||||||
}
|
}
|
||||||
|
@ -130,10 +130,6 @@ namespace Oqtane.Controllers
|
|||||||
System.IO.File.Delete(file);
|
System.IO.File.Delete(file);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up module schema versions
|
|
||||||
_sql.ExecuteNonQuery(tenant, "DELETE FROM [dbo].[SchemaVersions] WHERE ScriptName LIKE '" + assemblyname + "%'");
|
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Schema Versions Removed For {AssemblyName}", assemblyname);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove module definition
|
// remove module definition
|
||||||
|
@ -315,7 +315,7 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildDefaultSite(string password, string email)
|
public void BuildDefaultSite(string password, string email)
|
||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
.breadcrumbs {
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
border-bottom: 1px solid #d6d5d5;
|
||||||
|
}
|
||||||
|
|
||||||
.top-row {
|
.top-row {
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -12,6 +16,7 @@
|
|||||||
.main .top-row {
|
.main .top-row {
|
||||||
background-color: #e6e6e6;
|
background-color: #e6e6e6;
|
||||||
border-bottom: 1px solid #d6d5d5;
|
border-bottom: 1px solid #d6d5d5;
|
||||||
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
@ -22,8 +27,8 @@
|
|||||||
background-color: rgba(0,0,0,0.4);
|
background-color: rgba(0,0,0,0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar .navbar-brand {
|
.sidebar .navbar-toggler .navbar-toggler-icon {
|
||||||
font-size: 1.1rem;
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar .oi {
|
.sidebar .oi {
|
||||||
@ -33,6 +38,10 @@
|
|||||||
top: -2px;
|
top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.app-menu .nav-item {
|
.app-menu .nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
@ -84,6 +93,13 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.breadcrumbs {
|
||||||
|
position: fixed;
|
||||||
|
left: 275px;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@ -94,7 +110,6 @@
|
|||||||
.main .top-row {
|
.main .top-row {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 9999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main > div {
|
.main > div {
|
||||||
@ -110,6 +125,10 @@
|
|||||||
/* Never collapse the sidebar for wide screens */
|
/* Never collapse the sidebar for wide screens */
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main > .container {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes sk-stretchdelay {
|
@-webkit-keyframes sk-stretchdelay {
|
||||||
@ -133,3 +152,35 @@
|
|||||||
-webkit-transform: scaleY(1.0);
|
-webkit-transform: scaleY(1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.sidebar {
|
||||||
|
margin-top: 3.5rem;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main > .top-row.px-4 {
|
||||||
|
display: flex;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-md-auto {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs {
|
||||||
|
position: fixed;
|
||||||
|
top: 150px;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main > .container {
|
||||||
|
margin-top: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ Please note that this project is governed by the **[.NET Foundation Contributor
|
|||||||
|
|
||||||
**To get started with Oqtane:**
|
**To get started with Oqtane:**
|
||||||
|
|
||||||
1. Install **[.NET Core 3.2 Preview4 SDK (v3.1.201)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**.
|
1. Install **[.NET Core 3.2 Preview5 SDK (v3.1.201)](https://dotnet.microsoft.com/download/dotnet-core/3.1)**.
|
||||||
|
|
||||||
2. Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**.
|
2. Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user