Merge pull request #4 from oqtane/master

sync
This commit is contained in:
gjwalk 2021-04-20 08:50:44 -04:00 committed by GitHub
commit 8069d838d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 1934 additions and 769 deletions

View File

@ -11,26 +11,28 @@
}
<AuthorizeView>
<NotAuthorized>
<div class="container Oqtane-Modules-Admin-Login">
<div class="form-group">
<label for="Username" class="control-label">@Localizer["Username:"] </label>
<input type="text" name="Username" class="form-control username" placeholder="Username" @bind="@_username" id="Username" />
</div>
<div class="form-group">
<label for="Password" class="control-label">@Localizer["Password:"] </label>
<input type="password" name="Password" class="form-control password" placeholder="Password" @bind="@_password" id="Password" />
</div>
<div class="form-group">
<div class="form-check form-check-inline">
<label class="form-check-label" for="Remember">@Localizer["Remember Me?"]</label>&nbsp;
<input type="checkbox" class="form-check-input" name="Remember" @bind="@_remember" id="Remember" />
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
<div class="form-group">
<label for="Username" class="control-label">@Localizer["Username:"] </label>
<input type="text" @ref="username" name="Username" class="form-control username" placeholder="Username" @bind="@_username" id="Username" required />
</div>
<div class="form-group">
<label for="Password" class="control-label">@Localizer["Password:"] </label>
<input type="password" name="Password" class="form-control password" placeholder="Password" @bind="@_password" id="Password" required />
</div>
<div class="form-group">
<div class="form-check form-check-inline">
<label class="form-check-label" for="Remember">@Localizer["Remember Me?"]</label>&nbsp;
<input type="checkbox" class="form-check-input" name="Remember" @bind="@_remember" id="Remember" />
</div>
</div>
<button type="button" class="btn btn-primary" @onclick="Login">@Localizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["Forgot Password"]</button>
</div>
<button type="button" class="btn btn-primary" @onclick="Login">@Localizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["Forgot Password"]</button>
</div>
</form>
</NotAuthorized>
</AuthorizeView>
@ -41,6 +43,10 @@
private string _username = string.Empty;
private string _password = string.Empty;
private bool _remember = false;
private bool validated = false;
private ElementReference login;
private ElementReference username;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
@ -80,52 +86,68 @@
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await username.FocusAsync();
}
}
private async Task Login()
{
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(login))
{
// server-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
// complete the login on the server so that the cookies are set correctly on SignalR
var interop = new Interop(JSRuntime);
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields);
// server-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
// complete the login on the server so that the cookies are set correctly on SignalR
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields);
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email."], MessageType.Error);
}
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Email Verification When They Initially Created."], MessageType.Error);
// client-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, true, _remember);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload"));
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email."], MessageType.Error);
}
}
}
else
{
// client-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, true, _remember);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload"));
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email."], MessageType.Error);
}
AddModuleMessage(Localizer["Please Provide Your Username And Password"], MessageType.Warning);
}
}
@ -157,4 +179,12 @@
StateHasChanged();
}
private async Task KeyPressed(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
await Login();
}
}
}

View File

@ -100,7 +100,7 @@ else
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
protected override async Task OnParametersSetAsync()
{
try
{
@ -110,9 +110,7 @@ else
if (string.IsNullOrEmpty(_moduledefinitionname))
{
_owner = ModuleState.Title;
_module = ModuleState.Title;
_description = ModuleState.Title;
AddModuleMessage(Localizer["Please Note That The Module Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
else
{
@ -162,8 +160,7 @@ else
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
module.ModuleDefinitionName = _moduledefinitionname;
await ModuleService.UpdateModuleAsync(module);
ClearModuleMessage();
NavigationManager.NavigateTo(NavigateUrl());
NavigationManager.NavigateTo(NavigateUrl(), true);
}
}
catch (Exception ex)

View File

@ -0,0 +1,158 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService
@inject ISystemService SystemService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@using System.Text.RegularExpressions
@using System.IO;
@if (_systeminfo != null && _templates != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Select Template"]&gt;</option>
@foreach (string template in _templates)
{
<option value="@template">@template</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@Localizer["Local Version"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Create Module"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
private string _owner = string.Empty;
private string _module = string.Empty;
private string _description = string.Empty;
private string _template = "-";
private string _reference = Constants.Version;
private string _location = string.Empty;
private Dictionary<string, string> _systeminfo;
private List<string> _templates;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
try
{
_systeminfo = await SystemService.GetSystemInfoAsync();
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
AddModuleMessage(Localizer["Please Note That The Module Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Module Creator");
}
}
private async Task CreateModule()
{
try
{
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
GetLocation();
AddModuleMessage(Localizer["The Source Code For Your Module Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Activate The Module.", NavigateUrl("admin/system")], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Module");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
{
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
Path.DirectorySeparatorChar + _owner + "." + _module;
}
StateHasChanged();
}
}

View File

@ -12,6 +12,8 @@
else
{
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" />
<Pager Items="@_moduleDefinitions">
<Header>
@ -25,17 +27,17 @@ else
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
<td>
@if (context.AssemblyName != "Oqtane.Client")
{
{
<ActionDialog Header="Delete Module" Message="@Localizer["Are You Sure You Wish To Delete The {0} Module?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
}
}
</td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@if (UpgradeAvailable(context.ModuleDefinitionName, context.Version))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.ModuleDefinitionName, context.Version))>@Localizer["Upgrade"]</button>
}
}
</td>
</Row>
</Pager>

View File

@ -8,7 +8,7 @@
@inject IStringLocalizer<Settings> Localizer
<TabStrip>
<TabPanel Name="Settings" Heading="Module Settings" ResourceKey="ModuleSettings">
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
@if (_containers != null)
{
<table class="table table-borderless">
@ -26,7 +26,6 @@
</td>
<td>
<select id="container" class="form-control" @bind="@_containerType">
<option value="-">&lt;@Localizer["Inherit From Page Or Site"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -47,7 +46,7 @@
</tr>
<tr>
<td>
<Label For="page" HelpText="The page that the module is on" ResourceKey="Page">Page: </Label>
<Label For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
</td>
<td>
<select id="page" class="form-control" @bind="@_pageId">
@ -76,10 +75,16 @@
</table>
}
</TabPanel>
@if (_settingsModuleType != null)
@if (_moduleSettingsType != null)
{
<TabPanel Name="ModuleSettings" Heading="@_settingstitle" ResourceKey="Module Settings">
@DynamicComponent
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
@ModuleSettingsComponent
</TabPanel>
}
@if (_containerSettingsType != null)
{
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
@ContainerSettingsComponent
</TabPanel>
}
</TabStrip>
@ -87,6 +92,10 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Module Settings";
private List<Theme> _themes;
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _title;
private string _containerType;
@ -95,50 +104,65 @@
private string _permissions = null;
private string _pageId;
private PermissionGrid _permissionGrid;
private Type _settingsModuleType;
private string _settingstitle = "Other Settings";
private object _settings;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Module Settings";
private RenderFragment DynamicComponent { get; set; }
private Type _moduleSettingsType;
private object _moduleSettings;
private string _moduleSettingsTitle = "Module Settings";
private RenderFragment ModuleSettingsComponent { get; set; }
private Type _containerSettingsType;
private object _containerSettings;
private RenderFragment ContainerSettingsComponent { get; set; }
protected override async Task OnInitializedAsync()
{
_title = ModuleState.Title;
_containers = ThemeService.GetContainerControls(await ThemeService.GetThemesAsync(), PageState.Page.ThemeType);
_themes = await ThemeService.GetThemesAsync();
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
_containerType = ModuleState.ContainerType;
if (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType) && _containerType == PageState.Page.DefaultContainerType)
{
_containerType = "-";
}
if (_containerType == PageState.Site.DefaultContainerType)
{
_containerType = "-";
}
_allPages = ModuleState.AllPages.ToString();
_permissions = ModuleState.Permissions;
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
_pageId = ModuleState.PageId.ToString();
_settingsModuleType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
if (_settingsModuleType != null)
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
{
var moduleobject = Activator.CreateInstance(_settingsModuleType) as IModuleControl;
_settingstitle = moduleobject.Title;
if (string.IsNullOrEmpty(_settingstitle))
// module settings type explicitly declared in IModule interface
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
}
else
{
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
}
if (_moduleSettingsType != null)
{
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
if (!string.IsNullOrEmpty(moduleobject.Title))
{
_settingstitle = "Other Settings";
_moduleSettingsTitle = moduleobject.Title;
}
DynamicComponent = builder =>
ModuleSettingsComponent = builder =>
{
builder.OpenComponent(0, _settingsModuleType);
builder.AddComponentReferenceCapture(1, inst => { _settings = Convert.ChangeType(inst, _settingsModuleType); });
builder.OpenComponent(0, _moduleSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
builder.CloseComponent();
};
}
}
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
{
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
if (_containerSettingsType != null)
{
ContainerSettingsComponent = builder =>
{
builder.OpenComponent(0, _containerSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
builder.CloseComponent();
};
}
}
}
private async Task SaveModule()
@ -163,16 +187,25 @@
module.Permissions = _permissionGrid.GetPermissions();
await ModuleService.UpdateModuleAsync(module);
if (_settings is ISettingsControl control)
if (_moduleSettingsType != null)
{
await control.UpdateSettings();
if (_moduleSettings is ISettingsControl moduleSettingsControl)
{
// module settings updated using explicit interface
await moduleSettingsControl.UpdateSettings();
}
else
{
// legacy support - module settings updated by convention ( ie. by calling a public method named "UpdateSettings" in settings component )
_moduleSettings?.GetType().GetMethod("UpdateSettings")?.Invoke(_moduleSettings, null);
}
}
else
if (_containerSettingsType != null && _containerSettings is ISettingsControl containerSettingsControl)
{
// Compatibility 2.0 fallback
_settings?.GetType().GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component
await containerSettingsControl.UpdateSettings();
}
NavigationManager.NavigateTo(NavigateUrl());
}

View File

@ -102,7 +102,6 @@
</td>
<td>
<select id="Theme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Inherit From Site"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
@ -110,37 +109,13 @@
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)" ResourceKey="Layout">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;@Localizer["Inherit From Site"]&gt;</option>
@foreach (var layout in _layouts)
{
if (layout.TypeName == _layouttype)
{
<option value="@(layout.TypeName)" selected>@(layout.Name)</option>
}
else
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;@Localizer["Inherit From Site"]&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -180,14 +155,21 @@
</tr>
</table>
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@Localizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList;
private string _name;
@ -200,25 +182,28 @@
private string _isnavigation = "True";
private string _url;
private string _ispersonalizable = "False";
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _themetype = string.Empty;
private string _containertype = string.Empty;
private string _icon = string.Empty;
private string _permissions = string.Empty;
private PermissionGrid _permissionGrid;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private Type _themeSettingsType;
private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; }
protected override async Task OnInitializedAsync()
{
try
{
_themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = PageState.Site.DefaultThemeType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = PageState.Site.DefaultContainerType;
_pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themes = ThemeService.GetThemeControls(_themeList);
_permissions = string.Empty;
ThemeSettings();
}
catch (Exception ex)
{
@ -267,18 +252,9 @@
try
{
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = "-";
ThemeSettings();
StateHasChanged();
}
catch (Exception ex)
@ -288,12 +264,31 @@
}
}
private void ThemeSettings()
{
_themeSettingsType = null;
var theme = _themeList.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 =>
{
builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
builder.CloseComponent();
};
}
}
}
private async Task SavePage()
{
Page page = null;
try
{
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_layouts.Count == 0 || _layouttype != "-"))
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && _containertype != "-")
{
page = new Page();
page.SiteId = PageState.Page.SiteId;
@ -360,11 +355,6 @@
{
page.ThemeType = string.Empty;
}
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
{
page.LayoutType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
@ -390,7 +380,7 @@
}
else
{
AddModuleMessage(Localizer["You Must Provide Page Name And Theme/Layout"], MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide Page Name, Theme, and Container"], MessageType.Warning);
}
}

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Admin.Pages
@using Oqtane.Interfaces
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IPageService PageService
@ -27,7 +28,10 @@
<option value="-1">&lt;@Localizer["Site Root"]&gt;</option>
@foreach (Page page in _pageList)
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
if (page.PageId != _pageId)
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
}
}
</select>
</td>
@ -106,7 +110,6 @@
</td>
<td>
<select id="Theme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Inherit From Site"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
@ -114,37 +117,13 @@
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)" ResourceKey="Layout">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;@Localizer["Inherit From Site"]&gt;</option>
@foreach (var layout in _layouts)
{
if (layout.TypeName == _layouttype)
{
<option value="@(layout.TypeName)" selected>@(layout.Name)</option>
}
else
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;@Localizer["Inherit From Site"]&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -189,14 +168,21 @@
</table>
}
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">@Localizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList;
private int _pageId;
@ -211,8 +197,7 @@
private string _isnavigation;
private string _url;
private string _ispersonalizable;
private string _themetype = "-";
private string _layouttype = "-";
private string _themetype;
private string _containertype = "-";
private string _icon;
private string _permissions = null;
@ -222,12 +207,10 @@
private DateTime _modifiedon;
private string _deletedby;
private DateTime? _deletedon;
#pragma warning disable 649
private PermissionGrid _permissionGrid;
#pragma warning restore 649
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private Type _themeSettingsType;
private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; }
protected override async Task OnInitializedAsync()
{
@ -266,21 +249,15 @@
_url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString();
_themetype = page.ThemeType;
if (_themetype == PageState.Site.DefaultThemeType)
if (string.IsNullOrEmpty(_themetype))
{
_themetype = "-";
_themetype = PageState.Site.DefaultThemeType;
}
_layouts = ThemeService.GetLayoutControls(_themeList, page.ThemeType);
_layouttype = page.LayoutType;
if (_layouttype == PageState.Site.DefaultLayoutType)
{
_layouttype = "-";
}
_containers = ThemeService.GetContainerControls(_themeList, page.ThemeType);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = page.DefaultContainerType;
if (string.IsNullOrEmpty(_containertype))
{
_containertype = "-";
_containertype = PageState.Site.DefaultContainerType;
}
_icon = page.Icon;
_permissions = page.Permissions;
@ -290,6 +267,8 @@
_modifiedon = page.ModifiedOn;
_deletedby = page.DeletedBy;
_deletedon = page.DeletedOn;
ThemeSettings();
}
}
catch (Exception ex)
@ -347,18 +326,9 @@
try
{
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = "-";
ThemeSettings();
StateHasChanged();
}
catch (Exception ex)
@ -368,12 +338,31 @@
}
}
private void ThemeSettings()
{
_themeSettingsType = null;
var theme = _themeList.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 =>
{
builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
builder.CloseComponent();
};
}
}
}
private async Task SavePage()
{
Page page = null;
try
{
if (_name != string.Empty)
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && _containertype != "-")
{
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
string currentPath = page.Path;
@ -442,11 +431,6 @@
{
page.ThemeType = string.Empty;
}
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
{
page.LayoutType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
@ -478,6 +462,11 @@
}
}
if (_themeSettingsType != null && _themeSettings is ISettingsControl themeSettingsControl)
{
await themeSettingsControl.UpdateSettings();
}
await logger.LogInformation("Page Saved {Page}", page);
if (PageState.QueryString.ContainsKey("cp"))
{
@ -490,7 +479,7 @@
}
else
{
AddModuleMessage(Localizer["You Must Provide Page Name"], MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide Page Name, Theme, and Container"], MessageType.Warning);
}
}
catch (Exception ex)

View File

@ -91,23 +91,6 @@
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the sites default layout" ResourceKey="DefaultLayout">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;@Localizer["Select Layout"]&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
@ -244,7 +227,6 @@
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _name = string.Empty;
private List<Tenant> _tenantList;
@ -256,7 +238,6 @@
private int _faviconfileid = -1;
private FileManager _faviconfilemanager;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _admincontainertype = "-";
private string _allowregistration;
@ -309,8 +290,6 @@
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_admincontainertype = site.AdminContainerType;
@ -371,15 +350,12 @@
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
@ -395,7 +371,7 @@
{
try
{
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-")
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -427,7 +403,6 @@
}
site.DefaultThemeType = _themetype;
site.DefaultLayoutType = (_layouttype == "-" ? string.Empty : _layouttype);
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
@ -491,7 +466,7 @@
}
else
{
AddModuleMessage(Localizer["You Must Provide A Site Name, Alias, And Default Theme/Layout/Container"], MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Site Name, Alias, And Default Theme/Container"], MessageType.Warning);
}
}
catch (Exception ex)

View File

@ -47,23 +47,6 @@ else
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site" ResourceKey="DefaultLayout">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;@Localizer["Select Layout"]&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
@ -219,7 +202,6 @@ else
@code {
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<SiteTemplate> _siteTemplates;
private List<Tenant> _tenants;
@ -238,7 +220,6 @@ else
private string _name = string.Empty;
private string _urls = string.Empty;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _admincontainertype = "";
private string _sitetemplatetype = "-";
@ -284,29 +265,26 @@ else
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error Loading Pane Layouts For Theme"], MessageType.Error);
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error Loading Containers For Theme"], MessageType.Error);
}
}
private async Task SaveSite()
{
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-" && _sitetemplatetype != "-")
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
{
var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync();
@ -393,7 +371,6 @@ else
config.SiteName = _name;
config.Aliases = _urls.Replace("\n", ",");
config.DefaultTheme = _themetype;
config.DefaultLayout = _layouttype;
config.DefaultContainer = _containertype;
config.DefaultAdminContainer = _admincontainertype;
config.SiteTemplate = _sitetemplatetype;
@ -421,7 +398,7 @@ else
}
else
{
AddModuleMessage(Localizer["You Must Provide A Tenant, Site Name, Alias, Default Theme/Layout/Container, And Site Template"], MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template"], MessageType.Warning);
}
}
}

View File

@ -40,23 +40,6 @@
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site" ResourceKey="DefaultLayout">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;@Localizer["Select Layout"]&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
@ -127,14 +110,12 @@
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private Alias _alias;
private string _name = string.Empty;
private List<Alias> _aliasList;
private string _urls = string.Empty;
private string _themetype;
private string _layouttype;
private string _containertype = "-";
private string _admincontainertype = "-";
private string _createdby;
@ -170,8 +151,6 @@
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_admincontainertype = site.AdminContainerType;
@ -208,15 +187,12 @@
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
@ -232,7 +208,7 @@
{
try
{
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-")
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -252,7 +228,6 @@
site.Name = _name;
site.LogoFileId = null;
site.DefaultThemeType = _themetype;
site.DefaultLayoutType = _layouttype ?? string.Empty;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.IsDeleted = (_isdeleted == null || Boolean.Parse(_isdeleted));

View File

@ -0,0 +1,151 @@
@namespace Oqtane.Modules.Admin.Themes
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IModuleService ModuleService
@inject IPageModuleService PageModuleService
@inject ISystemService SystemService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@using System.Text.RegularExpressions
@using System.IO;
@if (_systeminfo != null && _templates != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_theme" />
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Select Template"]&gt;</option>
@foreach (string template in _templates)
{
<option value="@template">@template</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@Localizer["Local Version"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Create Theme"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
private string _owner = string.Empty;
private string _theme = string.Empty;
private string _template = "-";
private string _reference = Constants.Version;
private string _location = string.Empty;
private Dictionary<string, string> _systeminfo;
private List<string> _templates;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
try
{
_systeminfo = await SystemService.GetSystemInfoAsync();
_templates = await ThemeService.GetThemeTemplatesAsync();
AddModuleMessage(Localizer["Please Note That The Theme Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Theme Creator");
}
}
private async Task CreateTheme()
{
try
{
if (IsValid(_owner) && IsValid(_theme) && _owner != _theme && _template != "-")
{
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference };
theme = await ThemeService.CreateThemeAsync(theme);
GetLocation();
AddModuleMessage(Localizer["The Source Code For Your Theme Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Activate The Module.", NavigateUrl("admin/system")], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Theme Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Theme");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
{
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
Path.DirectorySeparatorChar + _owner + "." + _theme;
}
StateHasChanged();
}
}

View File

@ -13,6 +13,8 @@
else
{
<ActionLink Action="Add" Text="Install Theme" />
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" />
<Pager Items="@_themes">
<Header>
@ -26,17 +28,17 @@ else
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" ResourceKey="ViewTheme" /></td>
<td>
@if (context.AssemblyName != "Oqtane.Client")
{
{
<ActionDialog Header="Delete Theme" Message="@Localizer["Are You Sure You Wish To Delete The {0} Theme?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
}
}
</td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@if (UpgradeAvailable(context.ThemeName, context.Version))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>@Localizer["Upgrade"]</button>
}
}
</td>
<td></td>
</Row>

View File

@ -6,21 +6,24 @@
<div class="col">
<TabStrip>
<TabPanel Name="Rich" Heading="Rich Text Editor">
@if (_filemanagervisible)
@if (AllowFileManagement)
{
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
<div class="row justify-content-center" style="margin-bottom: 20px;">
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["Synchronize Content"]</button>&nbsp;&nbsp;
<button type="button" class="btn btn-primary" @onclick="InsertImage">@Localizer["Insert Image"]</button>
@if (_filemanagervisible)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
</div>
<div class="row justify-content-center" style="margin-bottom: 20px;">
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["Synchronize Content"]</button>&nbsp;&nbsp;
<button type="button" class="btn btn-primary" @onclick="InsertImage">@Localizer["Insert Image"]</button>
@if (_filemanagervisible)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
}
</div>
}
<div class="row">
<div class="col">
<div @ref="@_toolBar">
@ -107,6 +110,9 @@
[Parameter]
public string DebugLevel { get; set; } = "info";
[Parameter]
public bool AllowFileManagement { get; set; } = true;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" },

View File

@ -30,16 +30,12 @@ else
[Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to specify SecurityAccessLevel
protected override void OnInitialized()
{
base.OnInitialized();
Parent.AddTabPanel((TabPanel)this);
}
protected override void OnParametersSet()
{
base.OnParametersSet();
Parent.AddTabPanel((TabPanel)this);
if (string.IsNullOrEmpty(Heading))
{
Name = Localize(nameof(Name), Name);

View File

@ -9,7 +9,7 @@
{
@if (IsAuthorized(tabPanel))
{
<li class="nav-item">
<li class="nav-item" @key="tabPanel.Name">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@ -35,7 +35,7 @@
</CascadingValue>
@code {
private List<TabPanel> _tabPanels = new List<TabPanel>();
private List<TabPanel> _tabPanels;
[Parameter]
public RenderFragment ChildContent { get; set; } // contains the TabPanels
@ -51,14 +51,22 @@
}
}
protected override void OnParametersSet()
{
_tabPanels = new List<TabPanel>();
}
internal void AddTabPanel(TabPanel tabPanel)
{
_tabPanels.Add(tabPanel);
if (string.IsNullOrEmpty(ActiveTab))
if (!_tabPanels.Exists(item => item.Name == tabPanel.Name))
{
ActiveTab = tabPanel.Name;
_tabPanels.Add(tabPanel);
if (string.IsNullOrEmpty(ActiveTab))
{
ActiveTab = tabPanel.Name;
}
StateHasChanged();
}
StateHasChanged();
}
private bool IsAuthorized(TabPanel tabPanel)

View File

@ -4,12 +4,13 @@
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject IHtmlTextService HtmlTextService
@inject ISettingService SettingService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
@if (_content != null)
{
<RichTextEditor Content="@_content" @ref="@RichTextEditorHtml"></RichTextEditor>
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
<button type="button" class="btn btn-success" @onclick="SaveContent">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@if (!string.IsNullOrEmpty(_content))
@ -26,13 +27,14 @@
public override string Title => "Edit Html/Text";
public override List<Resource> Resources => new List<Resource>()
{
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" }
};
private RichTextEditor RichTextEditorHtml;
private bool _allowfilemanagement;
private string _content = null;
private string _createdby;
private DateTime _createdon;
@ -43,6 +45,8 @@
{
try
{
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null)
{

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
namespace Oqtane.Modules.HtmlText
{
@ -10,7 +10,8 @@ namespace Oqtane.Modules.HtmlText
Description = "Renders HTML or Text Content",
Version = "1.0.0",
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
ReleaseVersions = "1.0.0"
ReleaseVersions = "1.0.0",
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"
};
}
}

View File

@ -0,0 +1,49 @@
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject ISettingService SettingService
@implements Oqtane.Interfaces.ISettingsControl
@inject IStringLocalizer<Settings> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
</td>
<td>
<select id="files" class="form-control" @bind="@_allowfilemanagement">
<option value="true">@Localizer["Yes"]</option>
<option value="false">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
@code {
private string _allowfilemanagement;
protected override void OnInitialized()
{
try
{
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
public async Task UpdateSettings()
{
try
{
var settings = ModuleState.Settings;
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations>
<Version>2.0.1</Version>
<Version>2.0.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -14,18 +14,11 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Modules\Admin\ModuleCreator\Templates\**" />
<Content Remove="Modules\Admin\ModuleCreator\Templates\**" />
<EmbeddedResource Remove="Modules\Admin\ModuleCreator\Templates\**" />
<None Remove="Modules\Admin\ModuleCreator\Templates\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />

View File

@ -129,19 +129,19 @@ namespace Oqtane.Client
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name)))
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.FullName)))
{
using (var memoryStream = new MemoryStream())
{
entry.Open().CopyTo(memoryStream);
byte[] file = memoryStream.ToArray();
switch (Path.GetExtension(entry.Name))
switch (Path.GetExtension(entry.FullName))
{
case ".dll":
dlls.Add(entry.Name, file);
dlls.Add(entry.FullName, file);
break;
case ".pdb":
pdbs.Add(entry.Name, file);
pdbs.Add(entry.FullName, file);
break;
}
}

View File

@ -35,13 +35,7 @@ namespace Oqtane.Services
public async Task<Folder> GetFolderAsync(int siteId, [NotNull] string folderPath)
{
if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar)))
{
folderPath = Utilities.PathCombine(folderPath, System.IO.Path.DirectorySeparatorChar.ToString());
}
var path = WebUtility.UrlEncode(folderPath);
return await GetJsonAsync<Folder>($"{ApiUrl}/{siteId}/{path}");
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -12,5 +12,7 @@ namespace Oqtane.Services
List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName);
Task InstallThemesAsync();
Task DeleteThemeAsync(string themeName);
Task<Theme> CreateThemeAsync(Theme theme);
Task<List<string>> GetThemeTemplatesAsync();
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
@ -29,10 +29,10 @@ namespace Oqtane.Services
return themes.SelectMany(item => item.Themes).ToList();
}
//[Obsolete("This method is deprecated.", false)]
public List<ThemeControl> GetLayoutControls(List<Theme> themes, string themeName)
{
return themes.Where(item => Utilities.GetTypeName(themeName).StartsWith(Utilities.GetTypeName(item.ThemeName)))
.SelectMany(item => item.Layouts).ToList();
return null;
}
public List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName)
@ -50,5 +50,16 @@ namespace Oqtane.Services
{
await DeleteAsync($"{ApiUrl}/{themeName}");
}
public async Task<Theme> CreateThemeAsync(Theme theme)
{
return await PostJsonAsync($"{ApiUrl}", theme);
}
public async Task<List<string>> GetThemeTemplatesAsync()
{
List<string> templates = await GetJsonAsync<List<string>>($"{ApiUrl}/templates");
return templates;
}
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Themes.BlazorTheme
@namespace Oqtane.Themes.BlazorTheme
@inherits ThemeBase
<div class="breadcrumbs">
@ -17,16 +17,13 @@
</div>
<div class="container">
<div class="row px-4">
<Pane Name="Content" />
</div>
<div class="row px-4">
<Pane Name="Admin" />
<Pane Name="@PaneNames.Admin" />
</div>
</div>
</div>
@code {
public override string Panes => "Content";
public override string Panes => PaneNames.Admin;
public override List<Resource> Resources => new List<Resource>()
{

View File

@ -0,0 +1,45 @@
@namespace Oqtane.Themes.Controls
@inherits ModuleActionsBase
@attribute [OqtaneIgnore]
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions) && PageState.Action == Constants.DefaultAction)
{
<div class="app-moduleactions">
<a class="nav-link dropdown-toggle" data-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);">
@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>&nbsp;@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>&nbsp;Move To &gt;
</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>&nbsp;@action.Name
</a>
</li>
}
</ul>
</li>
}
</ul>
</div>
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -76,7 +76,7 @@ namespace Oqtane.Themes.Controls
{
if (pane != ModuleState.Pane)
{
actionList.Add(new ActionViewModel {Icon = Icons.AccountLogin, Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
actionList.Add(new ActionViewModel {Icon = Icons.AccountLogin, Name = pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
}
}
}

View File

@ -1,12 +0,0 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@if (!string.IsNullOrWhiteSpace(Value))
{
<span class="@Value" aria-hidden="true"></span>
}
@code {
[Parameter()]
public string Value { get; set; }
}

View File

@ -1,34 +0,0 @@
@namespace Oqtane.Themes.Controls
@inherits ModuleActionsBase
@attribute [OqtaneIgnore]
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
{
<div class="app-moduleactions">
<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);">
@foreach (var action in Actions)
{
if (string.IsNullOrEmpty(action.Name))
{
<div class="dropdown-divider"></div>
}
else
{
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
@if (string.IsNullOrEmpty(action.Icon))
{
@((MarkupString) "&nbsp;&nbsp;");
}
else
{
<span class="@action.Icon" aria-hidden="true"></span>
}
&nbsp;
@action.Name
</a>
}
}
</div>
</div>
}

View File

@ -599,7 +599,7 @@
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 : PageState.Page.Panes.FirstOrDefault();
_pane = PageState.Page.Panes.Contains(pane) ? pane : PaneNames.Admin;
}
private async Task UpdateSettingsAsync()

View File

@ -3,17 +3,19 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Providers;
using Oqtane.Security;
using Oqtane.Services;
using Oqtane.Shared;
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;}
[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()
{
@ -29,13 +31,14 @@ namespace Oqtane.Themes.Controls
{
await UserService.LogoutUserAsync(PageState.User);
PageState.User = null;
bool authorizedtoviewpage = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, PageState.Page.Permissions);
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) };
var fields = new { __RequestVerificationToken = antiforgerytoken, returnurl = !authorizedtoviewpage ? PageState.Alias.Path : PageState.Alias.Path + "/" + PageState.Page.Path };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/logout/", fields);
}
else
@ -43,7 +46,7 @@ namespace Oqtane.Themes.Controls
// client-side Blazor
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload"));
NavigationManager.NavigateTo(NavigateUrl(!authorizedtoviewpage ? PageState.Alias.Path : PageState.Page.Path, "reload"));
}
}
}

View File

@ -1,4 +1,4 @@
namespace Oqtane.Themes
namespace Oqtane.Themes
{
public interface ILayoutControl
{

View File

@ -1,5 +1,8 @@
namespace Oqtane.Themes
using System;
namespace Oqtane.Themes
{
[Obsolete("This class is deprecated", false)]
public abstract class LayoutBase : ThemeBase, ILayoutControl
{

View File

@ -0,0 +1,47 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ContainerBase
@inject ISettingService SettingService
<div class="@_classes">
@if (_title)
{
<div class="row px-4">
<div class="d-flex flex-nowrap">
<ModuleActions /><h2><ModuleTitle /></h2>
</div>
<hr class="app-rule" />
</div>
}
else
{
<ModuleActions />
}
<div class="row px-4">
<div class="container">
<ModuleInstance />
</div>
</div>
</div>
@code {
public override string Name => "Customizable Container";
private bool _title = true;
private string _classes = "container";
protected override void OnParametersSet()
{
try
{
_title = bool.Parse(SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Title", "true"));
_classes += " " + SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Background", "");
_classes += " " + SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Text", "");
_classes += " " + SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Border", "");
_classes = _classes.Trim();
}
catch
{
// error loading container settings
}
}
}

View File

@ -0,0 +1,113 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ModuleBase
@implements Oqtane.Interfaces.ISettingsControl
@inject ISettingService SettingService
@attribute [OqtaneIgnore]
<table class="table table-borderless">
<tr>
<td>
<Label For="title" ResourceKey="Title" HelpText="Specify If The Module Title Should Be Displayed">Display Title?</Label>
</td>
<td>
<select id="title" class="form-control" @bind="@_title">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="background\" ResourceKey="Background" HelpText="Optionally Specify A Background Color For The Container">Background Color:</Label>
</td>
<td>
<select id="background" class="form-control" @bind="@_background">
<option value="">None</option>
<option value="bg-primary">Primary</option>
<option value="bg-secondary">Secondary</option>
<option value="bg-success">Success</option>
<option value="bg-danger">Danger</option>
<option value="bg-warning">Warning</option>
<option value="bg-info">Info</option>
<option value="bg-light">Light</option>
<option value="bg-dark">Dark</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="text" ResourceKey="Text" HelpText="Optionally Specify A Text Color For The Container">Text Color:</Label>
</td>
<td>
<select id="text" class="form-control" @bind="@_text">
<option value="">None</option>
<option value="text-primary">Primary</option>
<option value="text-secondary">Secondary</option>
<option value="text-success">Success</option>
<option value="text-danger">Danger</option>
<option value="text-warning">Warning</option>
<option value="text-info">Info</option>
<option value="text-light">Light</option>
<option value="text-dark">Dark</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="border" ResourceKey="Border" HelpText="Optionally Specify A Border For The Container">Border Color:</Label>
</td>
<td>
<select id="border" class="form-control" @bind="@_border">
<option value="">None</option>
<option value="border">Default</option>
<option value="border border-primary">Primary</option>
<option value="border border-secondary">Secondary</option>
<option value="border border-success">Success</option>
<option value="border border-danger">Danger</option>
<option value="border border-warning">Warning</option>
<option value="border border-info">Info</option>
<option value="border border-light">Light</option>
<option value="border border-dark">Dark</option>
</select>
</td>
</tr>
</table>
@code {
private string _title = "true";
private string _background = "";
private string _text = "";
private string _border = "";
protected override void OnInitialized()
{
try
{
_title = SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Title", "true");
_background = SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Background", "");
_text = SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Text", "");
_border = SettingService.GetSetting(ModuleState.Settings, GetType().Namespace + ":Border", "");
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
public async Task UpdateSettings()
{
try
{
var settings = ModuleState.Settings;
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Title", _title);
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Background", _background);
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Text", _text);
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Border", _border);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -1,32 +0,0 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ThemeBase
<main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
<Logo /><Menu Orientation="Horizontal" />
<div class="controls ml-md-auto">
<div class="controls-group"><UserProfile /> <Login /> <ControlPanel /></div>
</div>
</nav>
<div class="content container">
<PaneLayout />
<div class="row px-4">
<Pane Name="Admin" />
</div>
</div>
</main>
@code {
public override string Name => "Default";
public override string Panes => string.Empty;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.5.0/cyborg/bootstrap.min.css", Integrity = "sha384-GKugkVcT8wqoh3M8z1lqHbU+g6j498/ZT/zuXbepz7Dc09/otQZxTimkEMTkRWHP", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -1,20 +0,0 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits LayoutBase
<div class="row px-4">
<Pane Name="Top" />
</div>
<div class="row px-4">
<div class="col-sm"><Pane Name="Left" /></div>
<div class="col-sm"><Pane Name="Content" /></div>
<div class="col-sm"><Pane Name="Right" /></div>
</div>
<div class="row px-4">
<Pane Name="Bottom" />
</div>
@code {
public override string Name => "Multiple Panes";
public override string Panes => "Top,Left,Content,Right,Bottom";
}

View File

@ -1,13 +0,0 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ContainerBase
<div class="container">
@if (PageState.EditMode)
{
<ModuleActions />
}
<ModuleInstance />
</div>
@code {
public override string Name => "No Header";
}

View File

@ -1,12 +0,0 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits LayoutBase
<div class="row px-4">
<Pane Name="Content" />
</div>
@code {
public override string Name => "Single Pane";
public override string Panes => "Content";
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
namespace Oqtane.Themes.OqtaneTheme
{
@ -7,7 +7,9 @@ namespace Oqtane.Themes.OqtaneTheme
public Theme Theme => new Theme
{
Name = "Oqtane Theme",
Version = "1.0.0"
Version = "1.0.0",
ThemeSettingsType = "Oqtane.Themes.OqtaneTheme.ThemeSettings, Oqtane.Client",
ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client"
};
}
}

View File

@ -0,0 +1,131 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ThemeBase
@inject ISettingService SettingService
<main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
<Logo /><Menu Orientation="Horizontal" />
<div class="controls ml-md-auto">
<div class="controls-group"><UserProfile /> <Login /> <ControlPanel /></div>
</div>
</nav>
<div class="content">
<div class="container">
<div class="row">
<div class="col-md-12">
<Pane Name="@PaneNames.Admin" />
</div>
</div>
</div>
<Pane Name="Top Full Width" />
<div class="container">
<div class="row">
<div class="col-md-12">
<Pane Name="Top 100%" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<Pane Name="Left 50%" />
</div>
<div class="col-md-6">
<Pane Name="Right 50%" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<Pane Name="Left 33%" />
</div>
<div class="col-md-4">
<Pane Name="Center 33%" />
</div>
<div class="col-md-4">
<Pane Name="Right 33%" />
</div>
</div>
<div class="row">
<div class="col-md-3">
<Pane Name="Left Outer 25%" />
</div>
<div class="col-md-3">
<Pane Name="Left Inner 25%" />
</div>
<div class="col-md-3">
<Pane Name="Right Inner 25%" />
</div>
<div class="col-md-3">
<Pane Name="Right Outer 25%" />
</div>
</div>
<div class="row">
<div class="col-md-3">
<Pane Name="Left 25%" />
</div>
<div class="col-md-6">
<Pane Name="Center 50%" />
</div>
<div class="col-md-3">
<Pane Name="Right 25%" />
</div>
</div>
<div class="row">
<div class="col-md-8">
<Pane Name="Left Sidebar 66%" />
</div>
<div class="col-md-4">
<Pane Name="Right Sidebar 33%" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<Pane Name="Left Sidebar 33%" />
</div>
<div class="col-md-8">
<Pane Name="Right Sidebar 66%" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<Pane Name="Bottom 100%" />
</div>
</div>
</div>
<Pane Name="Bottom Full Width" />
@if (_footer)
{
<div class="bg-primary footer">
<Pane Name="Footer" />
</div>
}
</div>
</main>
@code {
public override string Name => "Default Theme";
public override string Panes => PaneNames.Admin + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.5.0/cyborg/bootstrap.min.css", Integrity = "sha384-GKugkVcT8wqoh3M8z1lqHbU+g6j498/ZT/zuXbepz7Dc09/otQZxTimkEMTkRWHP", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
private bool _footer = false;
protected override void OnParametersSet()
{
try
{
_footer = bool.Parse(SettingService.GetSetting(PageState.Page.Settings, GetType().Namespace + ":Footer", "false"));
}
catch
{
// error loading theme settings
}
}
}

View File

@ -0,0 +1,49 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ModuleBase
@implements Oqtane.Interfaces.ISettingsControl
@inject ISettingService SettingService
@attribute [OqtaneIgnore]
<table class="table table-borderless">
<tr>
<td>
<Label For="title" ResourceKey="Title" HelpText="Specify If The Page Footer Should Be Displayed">Display Footer?</Label>
</td>
<td>
<select id="title" class="form-control" @bind="@_footer">
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</td>
</tr>
</table>
@code {
private string _footer = "false";
protected override void OnInitialized()
{
try
{
_footer = SettingService.GetSetting(PageState.Page.Settings, GetType().Namespace + ":Footer", "false");
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
public async Task UpdateSettings()
{
try
{
var settings = PageState.Page.Settings;
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Footer", _footer);
await SettingService.UpdatePageSettingsAsync(settings, PageState.Page.PageId);
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -1,29 +1,46 @@
@namespace Oqtane.UI
<CascadingValue Value="@_moduleState" IsFixed="true">
@DynamicComponent
<CascadingValue Value="@ModuleState" IsFixed="true">
@if (_useadminborder)
{
<div class="app-pane-admin-border">
@DynamicComponent
</div>
}
else
{
@DynamicComponent
}
</CascadingValue>
@code {
private Module _moduleState;
private bool _useadminborder = false;
[CascadingParameter]
protected PageState PageState { get; set; }
[Parameter]
public Module Module { get; set; }
public Module ModuleState { get; set; }
RenderFragment DynamicComponent { get; set; }
protected override void OnParametersSet()
{
_moduleState = Module; // passed in from Pane component
string container = _moduleState.ContainerType;
if (PageState.ModuleId != -1 && _moduleState.UseAdminContainer)
string container = ModuleState.ContainerType;
if (PageState.ModuleId != -1 && ModuleState.UseAdminContainer)
{
container = (!string.IsNullOrEmpty(PageState.Site.AdminContainerType)) ? PageState.Site.AdminContainerType : Constants.DefaultAdminContainer;
}
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
{
_useadminborder = true;
}
else
{
_useadminborder = false;
}
DynamicComponent = builder =>
{
Type containerType = Type.GetType(container);

View File

@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Threading.Tasks;
@ -232,5 +233,19 @@ namespace Oqtane.UI
return Task.CompletedTask;
}
}
public ValueTask<bool> FormValid(ElementReference form)
{
try
{
return _jsRuntime.InvokeAsync<bool>(
"Oqtane.Interop.formValid",
form);
}
catch
{
return new ValueTask<bool>(Task.FromResult(false));
}
}
}
}

View File

@ -6,7 +6,7 @@
@if (_useadminborder)
{
<div class="@_paneadminborder">
<div class="app-pane-admin-border">
@((MarkupString)_panetitle)
@DynamicComponent
</div>
@ -18,7 +18,6 @@ else
@code {
private bool _useadminborder = false;
private string _paneadminborder = "app-pane-admin-border";
private string _panetitle = "";
[CascadingParameter]
@ -31,7 +30,7 @@ else
protected override void OnParametersSet()
{
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && Name != PaneNames.Admin)
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
{
_useadminborder = true;
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
@ -125,7 +124,7 @@ else
private void CreateComponent(RenderTreeBuilder builder, Module module)
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.AddAttribute(1, "ModuleState", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
}

View File

@ -1,25 +1,5 @@
@namespace Oqtane.UI
@DynamicComponent
@namespace Oqtane.UI
@code {
[CascadingParameter]
protected PageState PageState { get; set; }
RenderFragment DynamicComponent { get; set; }
protected override void OnParametersSet()
{
DynamicComponent = builder =>
{
var layoutType = Type.GetType(PageState.Page.LayoutType);
if (layoutType == null)
{
// fallback
layoutType = Type.GetType(Constants.DefaultLayout);
}
builder.OpenComponent(0, layoutType);
builder.CloseComponent();
};
}
// panelayouts are deprecated - this component is included for backward compatibility
}

View File

@ -384,21 +384,15 @@
page.ThemeType = site.DefaultThemeType;
}
if (string.IsNullOrEmpty(page.LayoutType))
{
page.LayoutType = site.DefaultLayoutType;
}
page.Panes = new List<string>();
page.Resources = new List<Resource>();
string panes = "";
string panes = PaneNames.Admin;
Type themetype = Type.GetType(page.ThemeType);
if (themetype == null)
{
// fallback
page.ThemeType = Constants.DefaultTheme;
page.LayoutType = Constants.DefaultLayout;
themetype = Type.GetType(Constants.DefaultTheme);
}
if (themetype != null)
@ -406,24 +400,13 @@
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
{
panes = themeobject.Panes;
if (!string.IsNullOrEmpty(themeobject.Panes))
{
panes = themeobject.Panes;
}
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
}
}
if (!string.IsNullOrEmpty(page.LayoutType))
{
Type layouttype = Type.GetType(page.LayoutType);
if (layouttype != null)
{
var layoutobject = Activator.CreateInstance(layouttype) as IThemeControl;
if (layoutobject != null)
{
panes = layoutobject.Panes;
}
}
}
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
}
catch

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Client</id>
<version>2.0.1</version>
<version>2.0.2</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,7 +13,7 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Framework</id>
<version>2.0.1</version>
<version>2.0.2</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,7 +13,7 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane framework</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Server</id>
<version>2.0.1</version>
<version>2.0.2</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,7 +13,7 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Shared</id>
<version>2.0.1</version>
<version>2.0.2</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,7 +13,7 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.1.Install.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.2.Install.zip" -Force

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.1.Upgrade.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.2.Upgrade.zip" -Force

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
@ -69,6 +69,10 @@ namespace Oqtane.Controllers
public Folder GetByPath(int siteId, string path)
{
var folderPath = WebUtility.UrlDecode(path);
if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar)))
{
folderPath = Utilities.PathCombine(folderPath, System.IO.Path.DirectorySeparatorChar.ToString());
}
Folder folder = _folders.GetFolder(siteId, folderPath);
if (folder != null)
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))

View File

@ -12,6 +12,7 @@ using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Themes;
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Generic;
namespace Oqtane.Controllers
{
@ -180,6 +181,7 @@ namespace Oqtane.Controllers
}
}
}
return memoryStream.ToArray();
}
});

View File

@ -175,9 +175,9 @@ namespace Oqtane.Controllers
// GET: api/<controller>/templates
[HttpGet("templates")]
[Authorize(Roles = RoleNames.Host)]
public List<string> Get()
public List<string> GetTemplates()
{
var templates = new List<String>();
var templates = new List<string>();
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", Path.DirectorySeparatorChar.ToString());
foreach (string directory in Directory.GetDirectories(templatePath))
{
@ -186,7 +186,7 @@ namespace Oqtane.Controllers
return templates;
}
// POST api/<controller>?moduleid=x
// POST api/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Host)]
public ModuleDefinition Post([FromBody] ModuleDefinition moduleDefinition)

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -19,16 +19,18 @@ namespace Oqtane.Controllers
private readonly IPageRepository _pages;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, ISettingRepository settings, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
{
_pages = pages;
_modules = modules;
_pageModules = pageModules;
_settings = settings;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
@ -39,11 +41,15 @@ namespace Oqtane.Controllers
[HttpGet]
public IEnumerable<Page> Get(string siteid)
{
List<Setting> settings = _settings.GetSettings(EntityNames.Page).ToList();
List<Page> pages = new List<Page>();
foreach (Page page in _pages.GetPages(int.Parse(siteid)))
{
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
pages.Add(page);
}
}
@ -65,6 +71,8 @@ namespace Oqtane.Controllers
}
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}
else
@ -84,6 +92,8 @@ namespace Oqtane.Controllers
{
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}
else
@ -164,7 +174,6 @@ namespace Oqtane.Controllers
page.IsNavigation = false;
page.Url = "";
page.ThemeType = parent.ThemeType;
page.LayoutType = parent.LayoutType;
page.DefaultContainerType = parent.DefaultContainerType;
page.Icon = parent.Icon;
page.Permissions = new List<Permission> {

View File

@ -103,5 +103,91 @@ namespace Oqtane.Controllers
}
}
// GET: api/<controller>/templates
[HttpGet("templates")]
[Authorize(Roles = RoleNames.Host)]
public List<string> GetTemplates()
{
var templates = new List<string>();
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", Path.DirectorySeparatorChar.ToString());
foreach (string directory in Directory.GetDirectories(templatePath))
{
templates.Add(directory.Replace(templatePath, ""));
}
return templates;
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Host)]
public Theme Post([FromBody] Theme theme)
{
if (ModelState.IsValid)
{
string rootPath;
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", theme.Template, Path.DirectorySeparatorChar.ToString());
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + "." + theme.Name, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + "." + theme.Name + ", " + theme.Owner + "." + theme.Name + ".Client.Oqtane";
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, theme);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Theme Created {Theme}", theme);
}
return theme;
}
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, Theme theme)
{
// process folder
string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
folderPath = folderPath.Replace("[Owner]", theme.Owner);
folderPath = folderPath.Replace("[Theme]", theme.Name);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
FileInfo[] files = current.GetFiles("*.*");
if (files != null)
{
foreach (FileInfo file in files)
{
// process file
string filePath = Path.Combine(folderPath, file.Name);
filePath = filePath.Replace("[Owner]", theme.Owner);
filePath = filePath.Replace("[Theme]", theme.Name);
string text = System.IO.File.ReadAllText(file.FullName);
text = text.Replace("[Owner]", theme.Owner);
text = text.Replace("[Theme]", theme.Name);
text = text.Replace("[RootPath]", rootPath);
text = text.Replace("[RootFolder]", rootFolder);
text = text.Replace("[Folder]", folderPath);
text = text.Replace("[File]", Path.GetFileName(filePath));
if (theme.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
text = text.Replace("[ClientReference]", "<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Client.dll</HintPath></Reference>");
text = text.Replace("[SharedReference]", "<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Shared.dll</HintPath></Reference>");
}
else
{
text = text.Replace("[FrameworkVersion]", theme.Version);
text = text.Replace("[ClientReference]", "<PackageReference Include=\"Oqtane.Client\" Version=\"" + theme.Version + "\" />");
text = text.Replace("[SharedReference]", "<PackageReference Include=\"Oqtane.Shared\" Version=\"" + theme.Version + "\" />");
}
System.IO.File.WriteAllText(filePath, text);
}
DirectoryInfo[] folders = current.GetDirectories();
foreach (DirectoryInfo folder in folders.Reverse())
{
ProcessTemplatesRecursively(folder, rootPath, rootFolder, templatePath, theme);
}
}
}
}
}

View File

@ -90,7 +90,6 @@ namespace Oqtane.Infrastructure
install.HostName = UserNames.Host;
install.SiteTemplate = GetInstallationConfig(SettingKeys.SiteTemplateKey, Constants.DefaultSiteTemplate);
install.DefaultTheme = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme);
install.DefaultLayout = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout);
install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer);
install.SiteName = Constants.DefaultSite;
install.IsNewTenant = true;
@ -122,10 +121,6 @@ namespace Oqtane.Infrastructure
if (string.IsNullOrEmpty(install.DefaultTheme))
{
install.DefaultTheme = GetInstallationConfig(SettingKeys.DefaultThemeKey, Constants.DefaultTheme);
if (string.IsNullOrEmpty(install.DefaultLayout))
{
install.DefaultLayout = GetInstallationConfig(SettingKeys.DefaultLayoutKey, Constants.DefaultLayout);
}
}
if (string.IsNullOrEmpty(install.DefaultContainer))
{
@ -438,7 +433,6 @@ namespace Oqtane.Infrastructure
Name = install.SiteName,
LogoFileId = null,
DefaultThemeType = install.DefaultTheme,
DefaultLayoutType = install.DefaultLayout,
DefaultContainerType = install.DefaultContainer,
SiteTemplateType = install.SiteTemplate
};

View File

@ -1,4 +1,4 @@
namespace Oqtane.Infrastructure
namespace Oqtane.Infrastructure
{
public interface ILocalizationManager
{

View File

@ -1,9 +1,7 @@
namespace Oqtane.Infrastructure
namespace Oqtane.Infrastructure
{
public class LocalizationOptions
{
public string DefaultCulture { get; set; }
public string[] SupportedCultures { get; set; }
}
}

View File

@ -1,4 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.Options;
using Oqtane.Shared;
@ -22,8 +24,20 @@ namespace Oqtane.Infrastructure
: _localizationOptions.DefaultCulture;
public string[] GetSupportedCultures()
=> _localizationOptions.SupportedCultures.IsNullOrEmpty()
? SupportedCultures
: _localizationOptions.SupportedCultures;
{
List<string> cultures = new List<string>();
foreach(var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Oqtane.Client.resources.dll", SearchOption.AllDirectories))
{
cultures.Add(Path.GetFileName(Path.GetDirectoryName(file)));
}
if (cultures.Count == 0)
{
return SupportedCultures;
}
else
{
return cultures.ToArray();
}
}
}
}

View File

@ -48,7 +48,7 @@ namespace Oqtane.SiteTemplates
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions() ,
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = "Content",
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -70,7 +70,7 @@ namespace Oqtane.SiteTemplates
"<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>" +
"<p>THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>"
},
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content",
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -94,7 +94,7 @@ namespace Oqtane.SiteTemplates
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = "Content",
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -118,7 +118,7 @@ namespace Oqtane.SiteTemplates
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = "Content",
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
@ -25,80 +24,101 @@ namespace Oqtane.Infrastructure
public void Upgrade(Tenant tenant, string version)
{
// core framework upgrade logic - note that you can check if current tenant is Master if you only want to execute the logic once
switch (version)
// core framework upgrade logic - executed for every tenant
using (var scope = _serviceScopeFactory.CreateScope())
{
case "0.9.0":
// this code is commented out on purpose - it provides an example of how to programmatically add a page to all existing sites on upgrade
var pageTemplates = new List<PageTemplate>();
//pageTemplates.Add(new PageTemplate
//{
// Name = "Test",
// Parent = "",
// Path = "test",
// Icon = Icons.Badge,
// IsNavigation = true,
// IsPersonalizable = false,
// EditMode = false,
// PagePermissions = new List<Permission>
// {
// new Permission(PermissionNames.View, RoleNames.Admin, true),
// new Permission(PermissionNames.View, RoleNames.Everyone, true),
// new Permission(PermissionNames.Edit, RoleNames.Admin, true)
// }.EncodePermissions(),
// PageTemplateModules = new List<PageTemplateModule>
// {
// new PageTemplateModule
// {
// ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "Test", Pane = "Content",
// ModulePermissions = new List<Permission>
// {
// new Permission(PermissionNames.View, RoleNames.Admin, true),
// new Permission(PermissionNames.View, RoleNames.Everyone, true),
// new Permission(PermissionNames.Edit, RoleNames.Admin, true)
// }.EncodePermissions(),
// Content = ""
// }
// }
//});
CreateSitePages(tenant, pageTemplates);
break;
case "2.0.2":
if (tenant.Name == TenantNames.Master)
{
// remove Internal module template files as they are no longer supported
var internalTemplatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", "Internal", Path.DirectorySeparatorChar.ToString());
if (Directory.Exists(internalTemplatePath))
{
Directory.Delete(internalTemplatePath, true);
}
}
break;
// set SiteState based on tenant
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
siteState.Alias = new Alias { TenantId = tenant.TenantId };
switch (version)
{
case "1.0.0":
Upgrade_1_0_0(tenant, scope);
break;
case "2.0.2":
Upgrade_2_0_2(tenant, scope);
break;
}
}
}
private void CreateSitePages(Tenant tenant, List<PageTemplate> pageTemplates)
private void Upgrade_1_0_0(Tenant tenant, IServiceScope scope)
{
var pageTemplates = new List<PageTemplate>();
// **Note: this code is commented out on purpose - it provides an example of how to programmatically add a page to all existing sites on upgrade
//pageTemplates.Add(new PageTemplate
//{
// Name = "Test",
// Parent = "",
// Path = "test",
// Icon = Icons.Badge,
// IsNavigation = true,
// IsPersonalizable = false,
// EditMode = false,
// PagePermissions = new List<Permission>
// {
// new Permission(PermissionNames.View, RoleNames.Admin, true),
// new Permission(PermissionNames.View, RoleNames.Everyone, true),
// new Permission(PermissionNames.Edit, RoleNames.Admin, true)
// }.EncodePermissions(),
// PageTemplateModules = new List<PageTemplateModule>
// {
// new PageTemplateModule
// {
// ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "Test", Pane = "Content",
// ModulePermissions = new List<Permission>
// {
// new Permission(PermissionNames.View, RoleNames.Admin, true),
// new Permission(PermissionNames.View, RoleNames.Everyone, true),
// new Permission(PermissionNames.Edit, RoleNames.Admin, true)
// }.EncodePermissions(),
// Content = ""
// }
// }
//});
CreateSitePages(scope, pageTemplates);
}
private void Upgrade_2_0_2(Tenant tenant, IServiceScope scope)
{
if (tenant.Name == TenantNames.Master)
{
// remove Internal module template files as they are no longer supported
var internalTemplatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", "Internal", Path.DirectorySeparatorChar.ToString());
if (Directory.Exists(internalTemplatePath))
{
try
{
Directory.Delete(internalTemplatePath, true);
}
catch
{
// error deleting directory
}
}
}
// initialize SiteGuid
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
foreach (Site site in sites.GetSites().ToList())
{
site.SiteGuid = System.Guid.NewGuid().ToString();
sites.UpdateSite(site);
}
}
private void CreateSitePages(IServiceScope scope, List<PageTemplate> pageTemplates)
{
if (pageTemplates.Count != 0)
{
var processed = new List<Site>();
foreach (Alias alias in _aliases.GetAliases().Where(item => item.TenantId == tenant.TenantId))
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
foreach (Site site in sites.GetSites().ToList())
{
if (!processed.Exists(item => item.SiteId == alias.SiteId))
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
siteState.Alias = alias;
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
var site = sites.GetSite(alias.SiteId);
if (site != null)
{
sites.CreatePages(site, pageTemplates);
}
processed.Add(site);
}
}
sites.CreatePages(site, pageTemplates);
}
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Configurations>Debug;Release</Configurations>
<Version>2.0.1</Version>
<Version>2.0.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -12,7 +12,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
</PropertyGroup>
@ -20,6 +20,9 @@
<Compile Remove="wwwroot\Modules\Templates\**" />
<Content Remove="wwwroot\Modules\Templates\**" />
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
<Compile Remove="wwwroot\Themes\Templates\**" />
<Content Remove="wwwroot\Themes\Templates\**" />
<EmbeddedResource Remove="wwwroot\Themes\Templates\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Scripts\Master.00.00.00.00.sql" />
@ -36,6 +39,9 @@
<EmbeddedResource Include="Scripts\Tenant.02.00.01.01.sql" />
<EmbeddedResource Include="Scripts\Tenant.02.00.01.02.sql" />
<EmbeddedResource Include="Scripts\Tenant.02.00.01.03.sql" />
<EmbeddedResource Include="Scripts\Tenant.02.00.02.01.sql" />
<EmbeddedResource Include="Scripts\Tenant.02.00.02.02.sql" />
<EmbeddedResource Include="Scripts\Tenant.02.00.02.03.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
</ItemGroup>
@ -60,9 +66,11 @@
<UpgradeFiles Include="$(ProjectDir)bin\Release\net5.0\Oqtane.Upgrade.dll" />
<UpgradeFiles Include="$(ProjectDir)bin\Release\net5.0\Oqtane.Upgrade.pdb" />
<UpgradeFiles Include="$(ProjectDir)bin\Release\net5.0\Oqtane.Upgrade.runtimeconfig.json" />
<TemplateFiles Include="$(ProjectDir)wwwroot\Modules\Templates\**\*.*" />
<ModuleTemplateFiles Include="$(ProjectDir)wwwroot\Modules\Templates\**\*.*" />
<ThemeTemplateFiles Include="$(ProjectDir)wwwroot\Themes\Templates\**\*.*" />
</ItemGroup>
<Target Name="AddPayloadsFolder" AfterTargets="Publish">
<Copy SourceFiles="@(UpgradeFiles)" DestinationFiles="@(UpgradeFiles->'$(PublishDir)%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
<Copy SourceFiles="@(TemplateFiles)" DestinationFiles="@(TemplateFiles->'$(PublishDir)wwwroot\Modules\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
<Copy SourceFiles="@(ModuleTemplateFiles)" DestinationFiles="@(ModuleTemplateFiles->'$(PublishDir)wwwroot\Modules\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
<Copy SourceFiles="@(ThemeTemplateFiles)" DestinationFiles="@(ThemeTemplateFiles->'$(PublishDir)wwwroot\Themes\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
</Target></Project>

View File

@ -15,6 +15,7 @@
<!-- stub the PWA manifest but defer the assignment of href -->
<link id="app-manifest" rel="manifest" />
<link rel="stylesheet" href="css/app.css" />
<script src="js/app.js"></script>
<script src="js/loadjs.min.js"></script>
@Html.Raw(@Model.HeadResources)
</head>

View File

@ -68,7 +68,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "User Login", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Login.Index).ToModuleDefinitionName(), Title = "User Login", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -97,7 +97,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Register.Index).ToModuleDefinitionName(), Title = "User Registration", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Register.Index).ToModuleDefinitionName(), Title = "User Registration", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -127,7 +127,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Reset.Index).ToModuleDefinitionName(), Title = "Password Reset", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Reset.Index).ToModuleDefinitionName(), Title = "Password Reset", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -156,7 +156,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UserProfile.Index).ToModuleDefinitionName(), Title = "User Profile", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UserProfile.Index).ToModuleDefinitionName(), Title = "User Profile", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -181,7 +181,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Dashboard.Index).ToModuleDefinitionName(), Title = "Admin Dashboard", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Dashboard.Index).ToModuleDefinitionName(), Title = "Admin Dashboard", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -208,7 +208,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Site.Index).ToModuleDefinitionName(), Title = "Site Settings", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Site.Index).ToModuleDefinitionName(), Title = "Site Settings", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -235,7 +235,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Pages.Index).ToModuleDefinitionName(), Title = "Page Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Pages.Index).ToModuleDefinitionName(), Title = "Page Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -262,7 +262,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Users.Index).ToModuleDefinitionName(), Title = "User Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Users.Index).ToModuleDefinitionName(), Title = "User Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -289,7 +289,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Profiles.Index).ToModuleDefinitionName(), Title = "Profile Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Profiles.Index).ToModuleDefinitionName(), Title = "Profile Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -316,7 +316,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Roles.Index).ToModuleDefinitionName(), Title = "Role Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Roles.Index).ToModuleDefinitionName(), Title = "Role Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -343,7 +343,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Files.Index).ToModuleDefinitionName(), Title = "File Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Files.Index).ToModuleDefinitionName(), Title = "File Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -370,7 +370,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.RecycleBin.Index).ToModuleDefinitionName(), Title = "Recycle Bin", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.RecycleBin.Index).ToModuleDefinitionName(), Title = "Recycle Bin", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
@ -399,7 +399,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Logs.Index).ToModuleDefinitionName(), Title = "Event Log", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Logs.Index).ToModuleDefinitionName(), Title = "Event Log", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -421,7 +421,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sites.Index).ToModuleDefinitionName(), Title = "Site Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sites.Index).ToModuleDefinitionName(), Title = "Site Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -443,7 +443,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.ModuleDefinitions.Index).ToModuleDefinitionName(), Title = "Module Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.ModuleDefinitions.Index).ToModuleDefinitionName(), Title = "Module Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -465,7 +465,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Themes.Index).ToModuleDefinitionName(), Title = "Theme Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Themes.Index).ToModuleDefinitionName(), Title = "Theme Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -494,7 +494,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Languages.Index).ToModuleDefinitionName(), Title = "Language Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Languages.Index).ToModuleDefinitionName(), Title = "Language Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -518,7 +518,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Jobs.Index).ToModuleDefinitionName(), Title = "Scheduled Jobs", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Jobs.Index).ToModuleDefinitionName(), Title = "Scheduled Jobs", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -540,7 +540,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sql.Index).ToModuleDefinitionName(), Title = "Sql Management", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Sql.Index).ToModuleDefinitionName(), Title = "Sql Management", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -562,7 +562,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.SystemInfo.Index).ToModuleDefinitionName(), Title = "System Info", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.SystemInfo.Index).ToModuleDefinitionName(), Title = "System Info", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -584,7 +584,7 @@ namespace Oqtane.Repository
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Upgrade.Index).ToModuleDefinitionName(), Title = "System Update", Pane = "Content",
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Upgrade.Index).ToModuleDefinitionName(), Title = "System Update", Pane = PaneNames.Admin,
ModulePermissions = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Host, true),
@ -605,7 +605,7 @@ namespace Oqtane.Repository
public Site AddSite(Site site)
{
site.SiteGuid = System.Guid.NewGuid().ToString();
_db.Site.Add(site);
_db.SaveChanges();
CreateSite(site);
@ -745,7 +745,6 @@ namespace Oqtane.Repository
Url = "",
IsNavigation = pagetemplate.IsNavigation,
ThemeType = "",
LayoutType = "",
DefaultContainerType = "",
Icon = pagetemplate.Icon,
Permissions = pagetemplate.PagePermissions,

View File

@ -63,7 +63,7 @@ namespace Oqtane.Repository
{
// Check if type should be ignored
if (themeControlType.IsOqtaneIgnore() ||
themeControlType.GetInterfaces().Contains(typeof(ILayoutControl)) ||
themeControlType.GetInterfaces().Contains(typeof(ILayoutControl)) || // deprecated
themeControlType.GetInterfaces().Contains(typeof(IContainerControl))) continue;
// create namespace root typename
@ -98,7 +98,6 @@ namespace Oqtane.Repository
// set internal properties
theme.ThemeName = qualifiedThemeType;
theme.Themes = new List<ThemeControl>();
theme.Layouts = new List<ThemeControl>();
theme.Containers = new List<ThemeControl>();
theme.AssemblyName = assembly.FullName.Split(",")[0];
themes.Add(theme);
@ -113,27 +112,10 @@ namespace Oqtane.Repository
TypeName = themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name,
Name = theme.Name + " - " + ((string.IsNullOrEmpty(themecontrolobject.Name)) ? Utilities.GetTypeNameLastSegment(themeControlType.FullName, 0) : themecontrolobject.Name),
Thumbnail = themecontrolobject.Thumbnail,
Panes = themecontrolobject.Panes
Panes = (!string.IsNullOrEmpty(themecontrolobject.Panes)) ? themecontrolobject.Panes : PaneNames.Admin
}
);
// layouts
Type[] layouttypes = themeTypes
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray();
foreach (Type layouttype in layouttypes)
{
var layoutobject = Activator.CreateInstance(layouttype) as IThemeControl;
theme.Layouts.Add(
new ThemeControl
{
TypeName = layouttype.FullName + ", " + themeControlType.Assembly.GetName().Name,
Name = (string.IsNullOrEmpty(layoutobject.Name)) ? Utilities.GetTypeNameLastSegment(layouttype.FullName, 0) : layoutobject.Name,
Thumbnail = layoutobject.Thumbnail,
Panes = layoutobject.Panes
}
);
}
// containers
Type[] containertypes = themeTypes
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();

View File

@ -0,0 +1,10 @@
/*
Version 2.0.2 Tenant migration script
*/
ALTER TABLE [dbo].[Site] ADD
[SiteGuid] [char](36) NULL
GO

View File

@ -0,0 +1,20 @@
/*
Version 2.0.2 Tenant migration script
*/
UPDATE [dbo].[Site] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client';
GO
UPDATE [dbo].[Site] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.NoTitle, Oqtane.Client';
GO
UPDATE [dbo].[Page] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client';
GO
UPDATE [dbo].[Page] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.NoTitle, Oqtane.Client';
GO
UPDATE [dbo].[PageModule] SET ContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client' WHERE ContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client';
GO
UPDATE [dbo].[PageModule] SET ContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client' WHERE ContainerType = 'Oqtane.Themes.OqtaneTheme.NoTitle, Oqtane.Client';
GO

View File

@ -0,0 +1,31 @@
/*
Version 2.0.2 Tenant migration script
*/
ALTER TABLE [dbo].[Setting] ALTER COLUMN [SettingName] [nvarchar](200) NOT NULL
GO
ALTER TABLE [dbo].[Site]
DROP COLUMN DefaultLayoutType
GO
ALTER TABLE [dbo].[Page]
DROP COLUMN LayoutType
GO
UPDATE [dbo].[Site] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client';
GO
UPDATE [dbo].[Site] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client';
GO
UPDATE [dbo].[Page] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client';
GO
UPDATE [dbo].[Page] SET DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client' WHERE DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client';
GO
UPDATE [dbo].[PageModule] SET ContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client' WHERE ContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client';
GO
UPDATE [dbo].[PageModule] SET ContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client' WHERE ContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client';
GO

View File

@ -41,7 +41,6 @@ namespace Oqtane
Configuration = builder.Build();
_supportedCultures = localizationManager.GetSupportedCultures();
_runtime = (Configuration.GetSection("Runtime").Value == "WebAssembly") ? Runtime.WebAssembly : Runtime.Server;
//add possibility to switch off swagger on production.

View File

@ -74,22 +74,29 @@
{
try
{
if (PageState.Action == "Add")
if (!string.IsNullOrEmpty(_name))
{
[Module] [Module] = new [Module]();
[Module].ModuleId = ModuleState.ModuleId;
[Module].Name = _name;
[Module] = await [Module]Service.Add[Module]Async([Module]);
await logger.LogInformation("[Module] Added {[Module]}", [Module]);
if (PageState.Action == "Add")
{
[Module] [Module] = new [Module]();
[Module].ModuleId = ModuleState.ModuleId;
[Module].Name = _name;
[Module] = await [Module]Service.Add[Module]Async([Module]);
await logger.LogInformation("[Module] Added {[Module]}", [Module]);
}
else
{
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);
}
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);
AddModuleMessage("The Name Is Required", MessageType.Warning);
}
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{

View File

@ -36,40 +36,6 @@ else
}
}
<!-- The content below is for informational purposes only and can be safely removed -->
<hr />
[Module] Module Created Successfully. Use Edit Mode To Add A [Module]. You Can Access The Files At The Following Locations:<br /><br />
[RootPath]Client\<br />
- [Owner].[Module].Client.csproj - client project<br />
- _Imports.razor - global imports for module components<br />
- Edit.razor - component for adding or editing content<br />
- Index.razor - main component for your module **the content you are reading is in this file**<br />
- ModuleInfo.cs - implements IModule interface to provide configuration settings for your module<br />
- Settings.razor - component for managing module settings<br />
- Services\I[Module]Service.cs - interface for defining service API methods<br />
- Services\[Module]Service.cs - implements service API interface methods<br /><br />
[RootPath]Package\<br />
- [Owner].[Module].nuspec - nuget manifest for packaging module<br />
- [Owner].[Module].Package.csproj - packaging project<br />
- debug.cmd - copies assemblies to Oqtane bin folder when in Debug mode<br />
- release.cmd - creates nuget package and deploys to Oqtane wwwroot/modules folder when in Release mode<br /><br />
[RootPath]Server\<br />
- [Owner].[Module].Server.csproj - server project<br />
- Controllers\[Module]Controller.cs - API methods implemented using a REST pattern<br />
- Manager\[Module]Manager.cs - implements optional module interfaces for features such as import/export of content<br />
- Repository\I[Module]Repository.cs - interface for defining repository methods<br />
- Repository\[Module]Respository.cs - implements repository interface methods for data access using EF Core<br />
- Repository\[Module]Context.cs - provides a DB Context for data access<br />
- Scripts\[Owner].[Module].1.0.0.sql - database schema definition script<br />
- Scripts\[Owner].[Module].Uninstall.sql - database uninstall script<br />
- wwwroot\Module.css - module style sheet<br /><br />
[RootPath]Shared\<br />
- [Owner].[Module].csproj - shared project<br />
- Models\[Module].cs - model definition<br /><br />
<!-- The content above is for informational purposes only and can be safely removed -->
@code {
public override List<Resource> Resources => new List<Resource>()
{

View File

@ -1,4 +1,4 @@
/* Oqtane Styles */
/* Oqtane Styles */
body {
padding-top: 7rem;
@ -50,6 +50,13 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
color:#ffffff;
}
.footer {
padding-top: 15px;
min-height: 40px;
text-align: center;
color: #ffffff;
}
@media (max-width: 767px) {
.app-menu {

View File

@ -0,0 +1,4 @@
using System.Resources;
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("[Owner].[Theme].Client")]

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Themes.OqtaneTheme
@namespace [Owner].[Theme]
@inherits ContainerBase
<div class="container">
<div class="row px-4">
<div class="d-flex flex-nowrap">
@ -15,5 +16,5 @@
</div>
@code {
public override string Name => "Standard Header";
public override string Name => "Container1";
}

View File

@ -0,0 +1,15 @@
using Oqtane.Models;
using Oqtane.Themes;
namespace [Owner].[Theme]
{
public class ThemeInfo : ITheme
{
public Theme Theme => new Theme
{
Name = "[Theme]",
Version = "1.0.0"
};
}
}

View File

@ -0,0 +1,109 @@
@namespace [Owner].[Theme]
@inherits ThemeBase
<main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
<Logo /><Menu Orientation="Horizontal" />
<div class="controls ml-md-auto">
<div class="controls-group"><UserProfile /> <Login /> <ControlPanel /></div>
</div>
</nav>
<div class="content">
<div class="container">
<div class="row">
<div class="col-md-12">
<Pane Name="@PaneNames.Admin" />
</div>
</div>
</div>
<Pane Name="Top Full Width" />
<div class="container">
<div class="row">
<div class="col-md-12">
<Pane Name="Top 100%" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<Pane Name="Left 50%" />
</div>
<div class="col-md-6">
<Pane Name="Right 50%" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<Pane Name="Left 33%" />
</div>
<div class="col-md-4">
<Pane Name="Center 33%" />
</div>
<div class="col-md-4">
<Pane Name="Right 33%" />
</div>
</div>
<div class="row">
<div class="col-md-3">
<Pane Name="Left Outer 25%" />
</div>
<div class="col-md-3">
<Pane Name="Left Inner 25%" />
</div>
<div class="col-md-3">
<Pane Name="Right Inner 25%" />
</div>
<div class="col-md-3">
<Pane Name="Right Outer 25%" />
</div>
</div>
<div class="row">
<div class="col-md-3">
<Pane Name="Left 25%" />
</div>
<div class="col-md-6">
<Pane Name="Center 50%" />
</div>
<div class="col-md-3">
<Pane Name="Right 25%" />
</div>
</div>
<div class="row">
<div class="col-md-8">
<Pane Name="Left Sidebar 66%" />
</div>
<div class="col-md-4">
<Pane Name="Right Sidebar 33%" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<Pane Name="Left Sidebar 33%" />
</div>
<div class="col-md-8">
<Pane Name="Right Sidebar 66%" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<Pane Name="Bottom 100%" />
</div>
</div>
</div>
<Pane Name="Bottom Full Width" />
</div>
</main>
@code {
public override string Name => "Theme1";
public override string Panes => PaneNames.Admin + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css", Integrity = "sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RazorLangVersion>3.0</RazorLangVersion>
<Version>1.0.0</Version>
<Authors>[Owner]</Authors>
<Company>[Owner]</Company>
<Description>[Description]</Description>
<Product>[Owner].[Theme]</Product>
<Copyright>[Owner]</Copyright>
<AssemblyName>[Owner].[Theme].Client.Oqtane</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
[ClientReference]
[SharedReference]
</ItemGroup>
<PropertyGroup>
<!-- there may be other elements here -->
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,21 @@
@using System
@using System.Linq
@using System.Collections.Generic
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@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

View File

@ -0,0 +1,83 @@
/* Oqtane Styles */
body {
padding-top: 7rem;
}
.controls {
z-index: 2000;
padding-top: 15px;
padding-bottom: 15px;
margin-right: 10px;
}
.app-menu .nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.app-menu .nav-item a {
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
.app-menu .nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.app-menu .nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.app-menu .nav-link .oi {
width: 1.5rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
margin-left: auto;
}
div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu {
color:#000000;
}
@media (max-width: 767px) {
.app-menu {
width: 100%
}
.navbar {
position: fixed;
top: 60px;
width: 100%;
}
.controls {
height: 60px;
top: 15px;
position: fixed;
top: 0px;
width: 100%;
background-color: rgb(0, 0, 0);
}
.controls-group {
float: right;
margin-right: 25px;
}
.content {
position: relative;
top: 60px;
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Client\[Owner].[Theme].Client.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="IF $(ConfigurationName) == Debug (debug.cmd)" />
<Exec Command="IF $(ConfigurationName) == Release (release.cmd)" />
</Target>
</Project>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>[Owner].[Theme]</id>
<version>1.0.0</version>
<authors>[Owner]</authors>
<owners>[Owner]</owners>
<title>[Theme]</title>
<description>[Theme]</description>
<copyright>[Owner]</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane module</tags>
<releaseNotes></releaseNotes>
<summary></summary>
<dependencies>
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
</dependencies>
</metadata>
<files>
<file src="..\Client\bin\Release\net5.0\[Owner].[Theme].Client.Oqtane.dll" target="lib\net5.0" />
<file src="..\Client\bin\Release\net5.0\[Owner].[Theme].Client.Oqtane.pdb" target="lib\net5.0" />
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
</files>
</package>

View File

@ -0,0 +1,3 @@
XCOPY "..\Client\bin\Debug\net5.0\[Owner].[Theme].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
XCOPY "..\Client\bin\Debug\net5.0\[Owner].[Theme].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net5.0\" /Y
XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Client\wwwroot\" /Y /S /I

View File

@ -0,0 +1,2 @@
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].[Theme].nuspec
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Themes\" /Y

View File

@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28621.142
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "[Owner].[Theme].Client", "Client\[Owner].[Theme].Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "[Owner].[Theme].Package", "Package\[Owner].[Theme].Package.csproj", "{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Wasm|Any CPU = Wasm|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.Build.0 = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Wasm|Any CPU.ActiveCfg = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Wasm|Any CPU.Build.0 = Release|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Release|Any CPU.Build.0 = Release|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
{C5CE512D-CBB7-4545-AF0F-9B6591A0C3A7}.Wasm|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D016F15-46FE-4726-8DFD-2E4FD4DC7668}
EndGlobalSection
EndGlobal

Some files were not shown because too many files have changed in this diff Show More