Compare commits

..

80 Commits

Author SHA1 Message Date
aac7d6b97a Merge pull request #664 from sbwalker/master
create 1.0.2 packages
2020-07-23 15:32:17 -04:00
bcc33af52b create 1.0.2 packages 2020-07-23 15:31:28 -04:00
24fd42636a Merge pull request #663 from sbwalker/master
preparing for 1.0.2 release
2020-07-23 15:08:27 -04:00
8d539d058c preparing for 1.0.2 release 2020-07-23 15:07:18 -04:00
abda377f6f Merge pull request #662 from sbwalker/master
fix regression bug caused by #648  - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows
2020-07-23 14:59:25 -04:00
51bf822392 fix regression bug caused by #648 - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows 2020-07-23 14:58:33 -04:00
d3f135a9c7 Merge pull request #47 from oqtane/master
sync
2020-07-23 14:41:29 -04:00
07ba99cc41 Merge pull request #661 from sbwalker/master
increase wait time for browser redirects during app restarts
2020-07-23 14:40:46 -04:00
336550c571 increase wait time for browser redirects during app restarts 2020-07-23 14:39:53 -04:00
679cc04178 Merge pull request #660 from sbwalker/master
optimize NotificationJob so that it only processes the sites for each tenant once.
2020-07-23 14:15:24 -04:00
75fe4e7c89 optimize NotificationJob so that it only processes the sites for each tenant once. 2020-07-23 14:14:29 -04:00
410f8c74e5 Merge pull request #649 from JoergH66/feature/LoadDependDlls_InstallManager
Fixes 2 external module installation problems
2020-07-23 11:39:35 -04:00
05f67d6a2a Merge pull request #659 from sbwalker/master
fixed scheduler so that it does not set NextExecution until after the job has finished executing
2020-07-23 11:39:19 -04:00
3a6cde0e24 fixed scheduler so that it does not set NextExecution until after the job has finished executing 2020-07-23 11:38:20 -04:00
fe1de2b243 Merge pull request #658 from sbwalker/master
Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered.
2020-07-22 16:10:23 -04:00
62a6b5f28a Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered. 2020-07-22 16:09:39 -04:00
d648fa0f02 Merge pull request #657 from ADefWebserver/master
Make a Deploy to Azure Button #168
2020-07-21 11:07:36 -04:00
e706e8cf1f Update README.md
Points the button to the Oqtane repository
2020-07-18 09:55:27 -07:00
9eb8a7e65c Update azuredeploy.json 2020-07-18 09:40:40 -07:00
11c610edf0 Update .deployment 2020-07-18 09:18:02 -07:00
a65cdbd7ad Rename azure.deployment to .deployment 2020-07-18 08:12:18 -07:00
97c56ba142 Create azure.deployment 2020-07-18 08:02:03 -07:00
9fe72a1c98 Update azuredeploy.json 2020-07-17 18:45:57 -07:00
7b40725534 Update README.md 2020-07-17 18:15:08 -07:00
5e1671afe3 Create azuredeploy.json 2020-07-17 18:10:22 -07:00
50d74cbcee Merge pull request #656 from sbwalker/master
modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template
2020-07-17 15:11:32 -04:00
bc73e5e3d0 modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template 2020-07-17 15:09:05 -04:00
b02bdee8cb Merge pull request #46 from oqtane/master
sync
2020-07-17 10:10:23 -04:00
9db4985b14 Merge pull request #655 from alexhendel/fix-path-handling
Fix directory separator for path operations
2020-07-16 10:27:34 -04:00
6f281c256b Merge pull request #654 from mikecasas/upstream-local
Should be moduleid as the entity id is added in the CreateAuthPolicyU…
2020-07-16 10:27:19 -04:00
807252c9e5 Fix directory separator for path operations 2020-07-15 16:09:19 +02:00
23e7f66188 Delete module id as it is getting added in CreateAuthPolicyUrl method. 2020-07-14 11:47:59 -04:00
57c500f4bc Merge remote-tracking branch 'github-mike/upstream-local' into upstream-local 2020-07-14 11:42:55 -04:00
fe302aa9e4 Should be moduleid as the entity id is added in the CreateAuthPolicyUrl method. 2020-07-12 18:59:20 -04:00
f14f927df7 Update README.md 2020-07-09 10:19:20 -04:00
c3f74a5217 Update README.md 2020-07-09 10:18:06 -04:00
457d1bb563 Update README.md 2020-07-09 10:17:34 -04:00
25918056cb Update README.md 2020-07-09 10:14:53 -04:00
86517dd793 Update README.md 2020-07-09 10:13:07 -04:00
00ce083a2c Update README.md 2020-07-09 10:11:28 -04:00
bce262cd8e Merge pull request #652 from sbwalker/master
remove line feeds from content during import
2020-07-09 08:46:50 -04:00
b5db62ef6a remove line feeds from content during import 2020-07-09 08:45:23 -04:00
3703d87d50 Merge pull request #651 from sbwalker/master
Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator
2020-07-08 19:57:13 -04:00
f515def414 Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator 2020-07-08 19:56:02 -04:00
4bdf20822f check whether the file is in use, dependent runtime-dlls will distribute 2020-07-08 14:06:41 +02:00
49f4e64cb4 Merge pull request #45 from oqtane/master
sync
2020-07-07 08:44:51 -04:00
e615263706 Merge pull request #647 from chlupac/Notifications
Notification changes UNDO
2020-07-07 08:43:52 -04:00
2a7e256116 Merge pull request #648 from alexhendel/fix-linux-theme-installation
Fix directory separator in InstallationManager
2020-07-07 08:43:37 -04:00
a083405b48 Fix directory seperator in InstallationManager 2020-07-07 09:56:24 +02:00
3ea280c82a Motification changes UNDO 2020-07-06 15:27:35 +02:00
192433f02d Merge pull request #44 from oqtane/master
sync
2020-07-05 11:32:20 -04:00
56a2e9dcea Merge pull request #644 from chlupac/ActionIcons
Icons in module actions menu
2020-07-03 15:16:47 -04:00
921cced1c8 Merge pull request #643 from chlupac/Notifications
Notification job optimalization
2020-07-03 15:16:14 -04:00
b17f679f38 Notification job optimalization 2020-07-03 10:19:12 +02:00
8e43fcab21 Icons in module actions menu 2020-07-03 10:15:45 +02:00
73c5092e46 Merge pull request #43 from oqtane/master
sync
2020-07-02 08:56:55 -04:00
56537e4785 Merge pull request #642 from svreic/bugfix/page-path-validation
Page path validation
2020-07-02 08:13:39 -04:00
3bd7d7196d Merge pull request #640 from PoisnFang/routing
Url parameter helper enhancements
2020-07-02 08:11:14 -04:00
7b5a192b82 Added double page path validation 2020-07-02 09:47:42 +02:00
d4be058d07 Can get parameters without template
clear urlParameters dictionary if template fails. Removed UrlParametersTemplate property and UrlParamerters auto dictionary
2020-07-01 15:15:39 -07:00
6c20fea46a Merge pull request #641 from chlupac/NotifyRepo
Notification Repository Breaking change fix
2020-07-01 14:22:26 -04:00
2e7cfefb2e Notification Repository Breaking change fix 2020-07-01 14:23:55 +02:00
038894cf64 Enhancement to url parameters helper in modulebase 2020-07-01 01:35:06 -07:00
954e30d89f Save url parameter action segments 2020-06-30 16:01:16 -07:00
93d9c4534d Merge pull request #42 from oqtane/master
sync
2020-06-30 16:56:08 -04:00
468ca8c6a9 Merge pull request #639 from PoisnFang/routing
Hot fix for homepage routing
2020-06-30 16:54:54 -04:00
e7a4c08dea Now also properly routes in admin modules 2020-06-30 13:51:48 -07:00
69d639ee42 Hot fix for homepage routing 2020-06-30 13:42:35 -07:00
a780569a6f Merge pull request #41 from oqtane/master
sync
2020-06-30 16:16:11 -04:00
568c283efd Merge pull request #638 from PoisnFang/routing
Module Router Enhancement
2020-06-30 16:15:27 -04:00
fccdd07a08 Replaced token identifiers for { } 2020-06-30 12:59:19 -07:00
5e816ea912 Removed anchor property and hash is only set if there is anchor 2020-06-30 12:49:56 -07:00
cb2d529689 added in GetUrlParameters route to Module Index action 2020-06-30 04:16:08 -07:00
c5037e7084 Url parameters working on any page, plus queries and anchors 2020-06-30 03:41:35 -07:00
fdc39d57fb Module Router Enhancement
Allows for PageVariables through the URL
2020-06-27 11:49:24 -07:00
4960e2c668 Update README.md 2020-06-26 08:45:13 -04:00
66cc3a1392 Merge pull request #637 from sbwalker/master
improvements for custom authorization policy usage
2020-06-25 10:24:54 -04:00
6e7c8e7b05 improvements for custom authorization policy usage 2020-06-25 10:23:27 -04:00
727b943fa3 Merge pull request #636 from sbwalker/master
added ModuleControlBase
2020-06-25 09:32:56 -04:00
a4a0334ec0 added ModuleControlBase 2020-06-25 09:31:21 -04:00
91 changed files with 944 additions and 409 deletions

2
.deployment Normal file
View File

@ -0,0 +1,2 @@
[config]
project = Oqtane.Server/Oqtane.Server.csproj

View File

@ -63,10 +63,18 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="retention-log" HelpText="What items do you want in the retention log">Retention Log (Items): </Label> <Label For="retention" HelpText="Number of log entries to retain for this job">Retention Log (Items): </Label>
</td> </td>
<td> <td>
<input id="retention-log" class="form-control" @bind="@_retentionHistory" /> <input id="retention" class="form-control" @bind="@_retentionHistory" />
</td>
</tr>
<tr>
<td>
<Label For="next" HelpText="Next execution for this job.">Next Execution: </Label>
</td>
<td>
<input id="next" class="form-control" @bind="@_nextExecution" />
</td> </td>
</tr> </tr>
</table> </table>
@ -83,6 +91,7 @@
private string _startDate = string.Empty; private string _startDate = string.Empty;
private string _endDate = string.Empty; private string _endDate = string.Empty;
private string _retentionHistory = string.Empty; private string _retentionHistory = string.Empty;
private string _nextExecution = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -102,6 +111,7 @@
_startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty; _startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty;
_endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty; _endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty;
_retentionHistory = job.RetentionHistory.ToString(); _retentionHistory = job.RetentionHistory.ToString();
_nextExecution = job.NextExecution.ToString();
} }
} }
catch (Exception ex) catch (Exception ex)
@ -140,6 +150,15 @@
job.EndDate = DateTime.Parse(_endDate); job.EndDate = DateTime.Parse(_endDate);
} }
if (_nextExecution == string.Empty)
{
job.NextExecution = null;
}
else
{
job.NextExecution = DateTime.Parse(_nextExecution);
}
job.RetentionHistory = int.Parse(_retentionHistory); job.RetentionHistory = int.Parse(_retentionHistory);
try try

View File

@ -22,7 +22,7 @@ else
<td>@context.FinishDate</td> <td>@context.FinishDate</td>
</Row> </Row>
<Detail> <Detail>
<td colspan="4">@context.Notes</td> <td colspan="4">@((MarkupString)context.Notes)</td>
</Detail> </Detail>
</Pager> </Pager>
} }

View File

@ -79,7 +79,7 @@
{ {
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync(); await ModuleDefinitionService.InstallModuleDefinitionsAsync();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -75,12 +75,20 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<Label For="license" HelpText="The license of the module">License: </Label> <Label For="license" HelpText="The module license terms">License: </Label>
</td> </td>
<td> <td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea> <textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td> </td>
</tr> </tr>
<tr>
<td>
<Label For="runtimes" HelpText="The Blazor runtimes which this module supports">Runtimes: </Label>
</td>
<td>
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</td>
</tr>
</table> </table>
</Section> </Section>
</TabPanel> </TabPanel>
@ -110,6 +118,7 @@
private string _url = ""; private string _url = "";
private string _contact = ""; private string _contact = "";
private string _license = ""; private string _license = "";
private string _runtimes = "";
private string _permissions; private string _permissions;
private string _createdby; private string _createdby;
private DateTime _createdon; private DateTime _createdon;
@ -139,6 +148,7 @@
_url = moduleDefinition.Url; _url = moduleDefinition.Url;
_contact = moduleDefinition.Contact; _contact = moduleDefinition.Contact;
_license = moduleDefinition.License; _license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.Permissions; _permissions = moduleDefinition.Permissions;
_createdby = moduleDefinition.CreatedBy; _createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn; _createdon = moduleDefinition.CreatedOn;

View File

@ -86,7 +86,7 @@ else
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version); await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync(); await ModuleDefinitionService.InstallModuleDefinitionsAsync();
} }
catch (Exception ex) catch (Exception ex)
@ -102,7 +102,7 @@ else
{ {
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId); await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -31,9 +31,15 @@
{ {
try try
{ {
await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content); bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
StateHasChanged(); if (success)
NavigationManager.NavigateTo(NavigateUrl()); {
AddModuleMessage("Content Imported Successfully", MessageType.Success);
}
else
{
AddModuleMessage("A Problem Was Encountered Importing Content. Please Ensure The Content Is Formatted Correctly For The Module.", MessageType.Warning);
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -162,17 +162,6 @@
<input id="Icon" class="form-control" @bind="@_icon" /> <input id="Icon" class="form-control" @bind="@_icon" />
</td> </td>
</tr> </tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr> <tr>
<td> <td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label> <Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
@ -217,7 +206,6 @@
private string _isnavigation = "True"; private string _isnavigation = "True";
private string _url; private string _url;
private string _ispersonalizable = "False"; private string _ispersonalizable = "False";
private string _mode = "view";
private string _themetype = "-"; private string _themetype = "-";
private string _layouttype = "-"; private string _layouttype = "-";
private string _containertype = "-"; private string _containertype = "-";
@ -346,6 +334,12 @@
} }
} }
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{
AddModuleMessage($"A page with path {_path} already exists for the selected parent page. The page path needs to be unique for the selected parent.", MessageType.Warning);
return;
}
Page child; Page child;
switch (_insert) switch (_insert)
{ {
@ -367,7 +361,6 @@
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation)); page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
page.Url = _url; page.Url = _url;
page.EditMode = (_mode == "edit" ? true : false);
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{ {
@ -407,4 +400,8 @@
} }
} }
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
}
} }

View File

@ -173,17 +173,6 @@
<input id="Icon" class="form-control" @bind="@_icon" /> <input id="Icon" class="form-control" @bind="@_icon" />
</td> </td>
</tr> </tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr> <tr>
<td> <td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label> <Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
@ -235,7 +224,6 @@
private string _isnavigation; private string _isnavigation;
private string _url; private string _url;
private string _ispersonalizable; private string _ispersonalizable;
private string _mode;
private string _themetype = "-"; private string _themetype = "-";
private string _layouttype = "-"; private string _layouttype = "-";
private string _containertype = "-"; private string _containertype = "-";
@ -290,7 +278,6 @@
_isnavigation = page.IsNavigation.ToString(); _isnavigation = page.IsNavigation.ToString();
_url = page.Url; _url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString(); _ispersonalizable = page.IsPersonalizable.ToString();
_mode = (page.EditMode) ? "edit" : "view";
_themetype = page.ThemeType; _themetype = page.ThemeType;
if (_themetype == PageState.Site.DefaultThemeType) if (_themetype == PageState.Site.DefaultThemeType)
{ {
@ -333,7 +320,7 @@
_children = new List<Page>(); _children = new List<Page>();
if (_parentid == "-1") if (_parentid == "-1")
{ {
foreach(Page p in PageState.Pages.Where(item => item.ParentId == null)) foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{ {
@ -433,6 +420,13 @@
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path); page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
} }
} }
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
{
AddModuleMessage($"A page with path {_path} already exists for the selected parent page. The page path needs to be unique for the selected parent.", MessageType.Warning);
return;
}
if (_insert != "=") if (_insert != "=")
{ {
Page child; Page child;
@ -456,7 +450,6 @@
} }
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation)); page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.Url = _url; page.Url = _url;
page.EditMode = (_mode == "edit");
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType) if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{ {
@ -512,4 +505,9 @@
AddModuleMessage("Error Saving Page", MessageType.Error); AddModuleMessage("Error Saving Page", MessageType.Error);
} }
} }
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
}
} }

View File

@ -7,7 +7,7 @@
@inject IThemeService ThemeService @inject IThemeService ThemeService
@inject ISettingService SettingService @inject ISettingService SettingService
@if (_themes != null) @if (_initialized)
{ {
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
@ -211,6 +211,7 @@
} }
@code { @code {
private bool _initialized = false;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>(); private List<ThemeControl> _layouts = new List<ThemeControl>();
@ -318,6 +319,8 @@
_deletedby = site.DeletedBy; _deletedby = site.DeletedBy;
_deletedon = site.DeletedOn; _deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString(); _isdeleted = site.IsDeleted.ToString();
_initialized = true;
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -6,7 +6,7 @@
@inject IAliasService AliasService @inject IAliasService AliasService
@inject IThemeService ThemeService @inject IThemeService ThemeService
@if (_themes != null) @if (_initialized)
{ {
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
@ -106,6 +106,7 @@
} }
@code { @code {
private bool _initialized = false;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>(); private List<ThemeControl> _layouts = new List<ThemeControl>();
@ -163,6 +164,8 @@
_deletedby = site.DeletedBy; _deletedby = site.DeletedBy;
_deletedon = site.DeletedOn; _deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString(); _isdeleted = site.IsDeleted.ToString();
_initialized = true;
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@ -79,7 +79,7 @@
{ {
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync(); await ThemeService.InstallThemesAsync();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -87,7 +87,7 @@ else
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version); await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync(); await ThemeService.InstallThemesAsync();
} }
catch (Exception ex) catch (Exception ex)
@ -103,7 +103,7 @@ else
{ {
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.DeleteThemeAsync(Theme.ThemeName); await ThemeService.DeleteThemeAsync(Theme.ThemeName);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -71,7 +71,7 @@
{ {
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade(); await InstallationService.Upgrade();
} }
catch (Exception ex) catch (Exception ex)
@ -88,7 +88,7 @@
await PackageService.DownloadPackageAsync(packageid, version, "Framework"); await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator(); ShowProgressIndicator();
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3); await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade(); await InstallationService.Upgrade();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@if (_visible) @if (_visible)
{ {
<div class="app-admin-modal"> <div class="app-admin-modal">
@ -40,7 +40,7 @@
@code { @code {
private bool _visible = false; private bool _visible = false;
private bool _editmode = true; private bool _editmode = false;
private bool _authorized = false; private bool _authorized = false;
private string _iconSpan = string.Empty; private string _iconSpan = string.Empty;
@ -66,7 +66,7 @@
public bool Disabled { get; set; } // optional public bool Disabled { get; set; } // optional
[Parameter] [Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false
[Parameter] [Parameter]
public Action OnClick { get; set; } // required if an Action is specified - executes a method in the calling component public Action OnClick { get; set; } // required if an Action is specified - executes a method in the calling component
@ -84,6 +84,7 @@
{ {
Class = "btn btn-success"; Class = "btn btn-success";
} }
if (!string.IsNullOrEmpty(EditMode)) if (!string.IsNullOrEmpty(EditMode))
{ {
_editmode = bool.Parse(EditMode); _editmode = bool.Parse(EditMode);

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@inject IUserService UserService @inject IUserService UserService
@if (_authorized) @if (_authorized)
@ -21,7 +20,7 @@
private string _parameters = string.Empty; private string _parameters = string.Empty;
private string _classname = "btn btn-primary"; private string _classname = "btn btn-primary";
private string _style = string.Empty; private string _style = string.Empty;
private bool _editmode = true; private bool _editmode = false;
private bool _authorized = false; private bool _authorized = false;
private string _iconSpan = string.Empty; private string _iconSpan = string.Empty;
@ -47,7 +46,7 @@
public bool Disabled { get; set; } // optional public bool Disabled { get; set; } // optional
[Parameter] [Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false.
[Parameter] [Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
@ -90,7 +89,7 @@
if (!string.IsNullOrEmpty(IconName)) if (!string.IsNullOrEmpty(IconName))
{ {
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>{(IconOnly?"":"&nbsp")}"; _iconSpan = $"<span class=\"oi oi-{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
} }

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@if (_text != string.Empty) @if (_text != string.Empty)
{ {

View File

@ -1,7 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@inject IFolderService FolderService @inject IFolderService FolderService
@inject IFileService FileService @inject IFileService FileService
@ -56,11 +54,11 @@
<div> <div>
@if (UploadMultiple) @if (UploadMultiple)
{ {
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/> <input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
} }
else else
{ {
<input type="file" id="@_fileinputid" name="file" accept="@_filter"/> <input type="file" id="@_fileinputid" name="file" accept="@_filter" />
} }
<span id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress> <span id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress>
<span class="float-right"> <span class="float-right">
@ -134,7 +132,7 @@
if (!string.IsNullOrEmpty(Folder)) if (!string.IsNullOrEmpty(Folder))
{ {
_folders = new List<Folder> {new Folder {FolderId = -1, Name = Folder}}; _folders = new List<Folder> { new Folder { FolderId = -1, Name = Folder } };
FolderId = -1; FolderId = -1;
} }
else else
@ -163,7 +161,7 @@
await GetFiles(); await GetFiles();
// create unique id for component // create unique id for component
_guid = Guid.NewGuid().ToString("N"); _guid = Guid.NewGuid().ToString("N");
_fileinputid = _guid + "FileInput"; _fileinputid = _guid + "FileInput";
_progressinfoid = _guid + "ProgressInfo"; _progressinfoid = _guid + "ProgressInfo";
@ -211,7 +209,7 @@
_message = string.Empty; _message = string.Empty;
try try
{ {
FolderId = int.Parse((string) e.Value); FolderId = int.Parse((string)e.Value);
await GetFiles(); await GetFiles();
FileId = -1; FileId = -1;
_image = string.Empty; _image = string.Empty;
@ -227,7 +225,7 @@
private async Task FileChanged(ChangeEventArgs e) private async Task FileChanged(ChangeEventArgs e)
{ {
_message = string.Empty; _message = string.Empty;
FileId = int.Parse((string) e.Value); FileId = int.Parse((string)e.Value);
await SetImage(); await SetImage();
StateHasChanged(); StateHasChanged();
@ -244,8 +242,8 @@
var maxwidth = 200; var maxwidth = 200;
var maxheight = 200; var maxheight = 200;
var ratioX = (double) maxwidth / (double) file.ImageWidth; var ratioX = (double)maxwidth / (double)file.ImageWidth;
var ratioY = (double) maxheight / (double) file.ImageHeight; var ratioY = (double)maxheight / (double)file.ImageHeight;
var ratio = ratioX < ratioY ? ratioX : ratioY; var ratio = ratioX < ratioY ? ratioX : ratioY;
_image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name + _image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@if (!string.IsNullOrEmpty(HelpText)) @if (!string.IsNullOrEmpty(HelpText))
{ {

View File

@ -1,10 +1,16 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore] @inject NavigationManager NavigationManager
@if (!string.IsNullOrEmpty(_message)) @if (!string.IsNullOrEmpty(_message))
{ {
<div class="@_classname" role="alert">@_message</div> <div class="@_classname" role="alert">
@_message
@if (Type == MessageType.Error && UserSecurity.IsAuthorized(PageState.User, Constants.HostRole))
{
@((MarkupString)"&nbsp;&nbsp;")<NavLink href="@NavigateUrl("admin/log")">View Details</NavLink>
}
</div>
<br /> <br />
} }

View File

@ -1,41 +1,39 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@typeparam TableItem @typeparam TableItem
<p> <p>
@if(Format == "Table") @if (Format == "Table")
{ {
<table class="@Class"> <table class="@Class">
<thead> <thead>
<tr>@Header</tr> <tr>@Header</tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
}
}
</tbody>
</table>
}
@if (Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList) @foreach (var item in ItemList)
{ {
<tr>@Row(item)</tr> <div class="row">@Row(item)</div>
@if (Detail != null) @if (Detail != null)
{ {
<tr>@Detail(item)</tr> <div class="row">@Detail(item)</div>
} }
} }
</tbody> </div>
</table>
}
@if(Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<div class="row">@Detail(item)</div>
}
}
</div>
} }
<div class="mx-auto text-center"> <div class="mx-auto text-center">
@if (_page > _maxPages) @if (_page > _maxPages)

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@inject IRoleService RoleService @inject IRoleService RoleService
@inject IUserService UserService @inject IUserService UserService
@ -39,7 +38,7 @@
<th scope="col">User</th> <th scope="col">User</th>
@foreach (PermissionString permission in _permissions) @foreach (PermissionString permission in _permissions)
{ {
<th style="text-align: center; width: 1px;">@permission.PermissionName</th> <th style="text-align: center; width: 1px;">@permission.PermissionName</th>
} }
</tr> </tr>
</thead> </thead>
@ -48,7 +47,7 @@
{ {
string userid = "[" + user.UserId.ToString() + "]"; string userid = "[" + user.UserId.ToString() + "]";
<tr> <tr>
<td >@user.DisplayName</td> <td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions) @foreach (PermissionString permission in _permissions)
{ {
var p = permission; var p = permission;

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
<div class="row" style="margin-bottom: 50px;"> <div class="row" style="margin-bottom: 50px;">
<div class="col"> <div class="col">
@ -108,7 +107,7 @@
public string DebugLevel { get; set; } = "info"; public string DebugLevel { get; set; } = "info";
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" } new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
<div class="d-flex"> <div class="d-flex">
<div> <div>

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
@if (Name == Parent.ActiveTab) @if (Name == Parent.ActiveTab)
{ {

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleControlBase
@attribute [OqtaneIgnore]
<CascadingValue Value="this"> <CascadingValue Value="this">
<div class="container-fluid"> <div class="container-fluid">

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
<img src="@_src" title="@_title" @onclick="SetValue" /> <img src="@_src" title="@_title" @onclick="SetValue" />

View File

@ -7,7 +7,7 @@
@if (PageState.EditMode) @if (PageState.EditMode)
{ {
<br /><ActionLink Action="Edit" /><br /><br /> <br /><ActionLink Action="Edit" EditMode="true" /><br /><br />
} }
@code { @code {

View File

@ -21,23 +21,23 @@ namespace Oqtane.Modules.HtmlText.Services
public async Task<HtmlTextInfo> GetHtmlTextAsync(int moduleId) public async Task<HtmlTextInfo> GetHtmlTextAsync(int moduleId)
{ {
var htmltext = await GetJsonAsync<List<HtmlTextInfo>>($"{ApiUrl}/{moduleId}?entityid={moduleId}"); var htmltext = await GetJsonAsync<List<HtmlTextInfo>>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", moduleId));
return htmltext.FirstOrDefault(); return htmltext.FirstOrDefault();
} }
public async Task AddHtmlTextAsync(HtmlTextInfo htmlText) public async Task AddHtmlTextAsync(HtmlTextInfo htmlText)
{ {
await PostJsonAsync($"{ApiUrl}?entityid={htmlText.ModuleId}", htmlText); await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", htmlText.ModuleId), htmlText);
} }
public async Task UpdateHtmlTextAsync(HtmlTextInfo htmlText) public async Task UpdateHtmlTextAsync(HtmlTextInfo htmlText)
{ {
await PutJsonAsync($"{ApiUrl}/{htmlText.HtmlTextId}?entityid={htmlText.ModuleId}", htmlText); await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", htmlText.ModuleId), htmlText);
} }
public async Task DeleteHtmlTextAsync(int moduleId) public async Task DeleteHtmlTextAsync(int moduleId)
{ {
await DeleteAsync($"{ApiUrl}/{moduleId}?entityid={moduleId}"); await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", moduleId));
} }
} }
} }

View File

@ -116,6 +116,54 @@ namespace Oqtane.Modules
return Utilities.ContentUrl(PageState.Alias, fileid); return Utilities.ContentUrl(PageState.Alias, fileid);
} }
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
{
var urlParameters = new Dictionary<string, string>();
string[] templateSegments;
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
var parameterId = 0;
if (string.IsNullOrEmpty(parametersTemplate))
{
for (int i = 0; i < parameters.Length; i++)
{
urlParameters.TryAdd("parameter" + i, parameters[i]);
}
}
else
{
templateSegments = parametersTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (parameters.Length == templateSegments.Length)
{
for (int i = 0; i < parameters.Length; i++)
{
if (parameters.Length > i)
{
if (templateSegments[i] == parameters[i])
{
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
parameterId++;
}
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
{
var key = templateSegments[i].Replace("{", "");
key = key.Replace("}", "");
urlParameters.TryAdd(key, parameters[i]);
}
else
{
i = parameters.Length;
urlParameters.Clear();
}
}
}
}
}
return urlParameters;
}
// user feedback methods // user feedback methods
public void AddModuleMessage(string message, MessageType type) public void AddModuleMessage(string message, MessageType type)
{ {
@ -154,12 +202,15 @@ namespace Oqtane.Modules
case "add": case "add":
logFunction = LogFunction.Create; logFunction = LogFunction.Create;
break; break;
case "edit": case "edit":
logFunction = LogFunction.Update; logFunction = LogFunction.Update;
break; break;
case "delete": case "delete":
logFunction = LogFunction.Delete; logFunction = LogFunction.Delete;
break; break;
default: default:
logFunction = LogFunction.Read; logFunction = LogFunction.Read;
break; break;

View File

@ -0,0 +1,10 @@
using Oqtane.Shared;
namespace Oqtane.Modules
{
[OqtaneIgnore]
public abstract class ModuleControlBase : ModuleBase
{
}
}

View File

@ -6,7 +6,7 @@
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<RazorLangVersion>3.0</RazorLangVersion> <RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -15,7 +15,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@ -170,6 +170,19 @@ namespace Oqtane.Services
// can be used to override the default alias // can be used to override the default alias
public Alias Alias { get; set; } public Alias Alias { get; set; }
// add entityid parameter to url for custom authorization policy
public string CreateAuthorizationPolicyUrl(string url, int entityId)
{
if (url.Contains("?"))
{
return url + "&entityid=" + entityId.ToString();
}
else
{
return url + "?entityid=" + entityId.ToString();
}
}
[Obsolete("This method is obsolete. Use CreateApiUrl(Alias alias, string serviceName) instead.", false)] [Obsolete("This method is obsolete. Use CreateApiUrl(Alias alias, string serviceName) instead.", false)]
public string CreateApiUrl(Alias alias, string absoluteUri, string serviceName) public string CreateApiUrl(Alias alias, string absoluteUri, string serviceName)
{ {

View File

@ -127,7 +127,10 @@
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{ {
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option> if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
} }
} }
</select> </select>
@ -196,26 +199,17 @@
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null)) @if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
{ {
@if (PageState.Page.EditMode) if (PageState.EditMode)
{ {
<button type="button" class="btn @ButtonClass active" aria-pressed="true" autocomplete="off"> <button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span> <span class="oi oi-pencil"></span>
</button> </button>
} }
else else
{ {
if (PageState.EditMode) <button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
{ <span class="oi oi-pencil"></span>
<button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))"> </button>
<span class="oi oi-pencil"></span>
</button>
}
else
{
<button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
} }
} }

View File

@ -2,7 +2,7 @@
@inherits ModuleActionsBase @inherits ModuleActionsBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) @if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
{ {
<div class="app-moduleactions"> <div class="app-moduleactions">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a> <a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
@ -15,7 +15,18 @@
} }
else else
{ {
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">@action.Name</a> <a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
@if (string.IsNullOrEmpty(action.Icon))
{
@((MarkupString) "&nbsp;&nbsp;");
}
else
{
<span class="oi oi-@action.Icon" aria-hidden="true"></span>
}
&nbsp;
@action.Name
</a>
} }
} }
</div> </div>

View File

@ -19,7 +19,7 @@ namespace Oqtane.Themes.Controls
[Inject] public IPageModuleService PageModuleService { get; set; } [Inject] public IPageModuleService PageModuleService { get; set; }
[Inject] public IModuleService ModuleService { get; set; } [Inject] public IModuleService ModuleService { get; set; }
protected List<ActionViewModel> Actions; public List<ActionViewModel> Actions;
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
@ -31,51 +31,52 @@ namespace Oqtane.Themes.Controls
var actionList = new List<ActionViewModel>(); var actionList = new List<ActionViewModel>();
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions)) if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
{ {
actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)}); actionList.Add(new ActionViewModel {Icon = Icons.Cog, Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
if (UserSecurity.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(Constants.AllUsersRole)) if (UserSecurity.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(Constants.AllUsersRole))
{ {
actionList.Add(new ActionViewModel { Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) }); actionList.Add(new ActionViewModel {Icon=Icons.CircleX, Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
} }
else else
{ {
actionList.Add(new ActionViewModel { Name = "Publish Module", Action = async (s, m) => await Publish(s, m) }); actionList.Add(new ActionViewModel {Icon=Icons.CircleCheck, Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
} }
actionList.Add(new ActionViewModel { Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) }); actionList.Add(new ActionViewModel {Icon=Icons.Trash, Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "") if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "")
{ {
actionList.Add(new ActionViewModel { Name = "" }); actionList.Add(new ActionViewModel { Name = "" });
actionList.Add(new ActionViewModel {Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")}); actionList.Add(new ActionViewModel {Icon=Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
actionList.Add(new ActionViewModel {Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")}); actionList.Add(new ActionViewModel {Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
} }
actionList.Add(new ActionViewModel {Name = ""}); actionList.Add(new ActionViewModel {Name = ""});
if (ModuleState.PaneModuleIndex > 0) if (ModuleState.PaneModuleIndex > 0)
{ {
actionList.Add(new ActionViewModel {Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)}); actionList.Add(new ActionViewModel {Icon = Icons.DataTransferUpload ,Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
} }
if (ModuleState.PaneModuleIndex > 0) if (ModuleState.PaneModuleIndex > 0)
{ {
actionList.Add(new ActionViewModel {Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)}); actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickTop, Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
} }
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
{ {
actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)}); actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickBottom, Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
} }
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1)) if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
{ {
actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)}); actionList.Add(new ActionViewModel {Icon = Icons.DataTransferDownload, Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
} }
foreach (string pane in PageState.Page.Panes) foreach (string pane in PageState.Page.Panes)
{ {
if (pane != ModuleState.Pane) if (pane != ModuleState.Pane)
{ {
actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)}); actionList.Add(new ActionViewModel {Icon = Icons.AccountLogin, Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
} }
} }
} }
@ -202,8 +203,8 @@ namespace Oqtane.Themes.Controls
public class ActionViewModel public class ActionViewModel
{ {
public string Icon { get; set; }
public string Name { set; get; } public string Name { set; get; }
public Func<string, PageModule, Task<string>> Action { set; get; } public Func<string, PageModule, Task<string>> Action { set; get; }
} }
} }

View File

@ -19,7 +19,7 @@
{ {
_moduleState = Module; // passed in from Pane component _moduleState = Module; // passed in from Pane component
string container = _moduleState.ContainerType; string container = _moduleState.ContainerType;
if (PageState.ModuleId != -1 && PageState.Action != "" && _moduleState.UseAdminContainer) if (PageState.ModuleId != -1 && _moduleState.UseAdminContainer)
{ {
container = Constants.DefaultAdminContainer; container = Constants.DefaultAdminContainer;
} }

View File

@ -14,6 +14,7 @@ namespace Oqtane.UI
public List<Module> Modules { get; set; } public List<Module> Modules { get; set; }
public Uri Uri { get; set; } public Uri Uri { get; set; }
public Dictionary<string, string> QueryString { get; set; } public Dictionary<string, string> QueryString { get; set; }
public string UrlParameters { get; set; }
public int ModuleId { get; set; } public int ModuleId { get; set; }
public string Action { get; set; } public string Action { get; set; }
public bool EditMode { get; set; } public bool EditMode { get; set; }

View File

@ -1,4 +1,5 @@
@namespace Oqtane.UI @using Microsoft.AspNetCore.Components.Rendering
@namespace Oqtane.UI
@inject IUserService UserService @inject IUserService UserService
@inject IModuleService ModuleService @inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService @inject IModuleDefinitionService ModuleDefinitionService
@ -25,7 +26,7 @@
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) && Name != Constants.AdminPane) if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) && Name != Constants.AdminPane)
{ {
_paneadminborder = "app-pane-admin-border"; _paneadminborder = "app-pane-admin-border";
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>"; _panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
@ -38,12 +39,12 @@
DynamicComponent = builder => DynamicComponent = builder =>
{ {
if (PageState.ModuleId != -1 && PageState.Action != "") if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
{ {
if (Name.ToLower() == Constants.AdminPane.ToLower()) if (Name.ToLower() == Constants.AdminPane.ToLower())
{ {
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId); Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null) if (module != null && !module.IsDeleted)
{ {
var typename = module.ModuleType; var typename = module.ModuleType;
// check for core module actions component // check for core module actions component
@ -88,10 +89,7 @@
{ {
module.Title = module.ControlTitle; module.Title = module.ControlTitle;
} }
CreateComponent(builder, module);
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
} }
} }
else else
@ -106,14 +104,12 @@
if (PageState.ModuleId != -1) if (PageState.ModuleId != -1)
{ {
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId); Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null && module.Pane.ToLower() == Name.ToLower()) if (module != null && module.Pane.ToLower() == Name.ToLower() && !module.IsDeleted)
{ {
// check if user is authorized to view module // check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{ {
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent)); CreateComponent(builder, module);
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
} }
} }
} }
@ -124,14 +120,19 @@
// check if user is authorized to view module // check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{ {
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent)); CreateComponent(builder, module);
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
} }
} }
} }
} }
}; };
} }
private void CreateComponent(RenderTreeBuilder builder, Module module)
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
}
} }

View File

@ -76,7 +76,8 @@
User user = null; User user = null;
List<Module> modules; List<Module> modules;
var moduleid = -1; var moduleid = -1;
var action = ""; var action = string.Empty;
var urlparameters = string.Empty;
var editmode = false; var editmode = false;
var reload = Reload.None; var reload = Reload.None;
var lastsyncdate = DateTime.UtcNow; var lastsyncdate = DateTime.UtcNow;
@ -179,21 +180,60 @@
// extract admin route elements from path // extract admin route elements from path
var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
int result; int result;
// check if path has moduleid and action specification ie. pagename/moduleid/action/
if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result)) int modIdPos = 0;
int actionPos = 0;
int urlParametersPos = 0;
for (int i = 0; i < segments.Length; i++)
{ {
action = segments[segments.Length - 1];
moduleid = result; if (segments[i] == Constants.UrlParametersDelimiter)
path = path.Replace(moduleid.ToString() + "/" + action + "/", "");
}
else
{
// check if path has moduleid specification ie. pagename/moduleid/
if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result))
{ {
moduleid = result; urlParametersPos = i + 1;
path = path.Replace(moduleid.ToString() + "/", "");
} }
if (i >= urlParametersPos && urlParametersPos != 0)
{
urlparameters += "/" + segments[i];
}
if (segments[i] == Constants.ModuleDelimiter)
{
modIdPos = i + 1;
actionPos = modIdPos + 1;
if (actionPos > segments.Length - 1)
{
action = Constants.DefaultAction;
}
else
{
action = segments[actionPos];
}
}
}
// check if path has moduleid and action specification ie. pagename/moduleid/action/
if (modIdPos > 0)
{
int.TryParse(segments[modIdPos], out result);
moduleid = result;
if (actionPos > segments.Length - 1)
{
path = path.Replace(segments[modIdPos - 1] + "/" + segments[modIdPos] + "/", "");
}
else
{
path = path.Replace(segments[modIdPos - 1] + "/" + segments[modIdPos] + "/" + segments[actionPos] + "/", "");
}
}
if (urlParametersPos > 0)
{
path = path.Replace(segments[urlParametersPos - 1] + urlparameters + "/", "");
} }
// remove trailing slash so it can be used as a key for Pages // remove trailing slash so it can be used as a key for Pages
@ -220,17 +260,14 @@
{ {
page = pages.Where(item => item.Path == path).FirstOrDefault(); page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = Reload.Page; reload = Reload.Page;
if (page != null) editmode = false;
{
editmode = page.EditMode;
}
} }
if (page != null) if (page != null)
{ {
if (PageState == null) if (PageState == null)
{ {
editmode = page.EditMode; editmode = false;
} }
// check if user is authorized to view page // check if user is authorized to view page
@ -263,6 +300,7 @@
Modules = modules, Modules = modules,
Uri = new Uri(_absoluteUri, UriKind.Absolute), Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring, QueryString = querystring,
UrlParameters = urlparameters,
ModuleId = moduleid, ModuleId = moduleid,
Action = action, Action = action,
EditMode = editmode, EditMode = editmode,
@ -360,6 +398,13 @@
string panes = ""; string panes = "";
Type themetype = Type.GetType(page.ThemeType); 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) if (themetype != null)
{ {
var themeobject = Activator.CreateInstance(themetype) as IThemeControl; var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
@ -401,7 +446,7 @@
if (module.PageId == page.PageId || module.ModuleId == moduleid) if (module.PageId == page.PageId || module.ModuleId == moduleid)
{ {
var typename = string.Empty; var typename = string.Empty;
if (module.ModuleDefinition != null) if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(GetRuntime().ToString())))
{ {
typename = module.ModuleDefinition.ControlTypeTemplate; typename = module.ModuleDefinition.ControlTypeTemplate;
} }

View File

@ -54,11 +54,6 @@
DynamicComponent = builder => DynamicComponent = builder =>
{ {
var themeType = Type.GetType(PageState.Page.ThemeType); var themeType = Type.GetType(PageState.Page.ThemeType);
if (themeType == null)
{
// fallback
themeType = Type.GetType(Constants.DefaultTheme);
}
builder.OpenComponent(0, themeType); builder.OpenComponent(0, themeType);
builder.CloseComponent(); builder.CloseComponent();
}; };

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
DEL "*.nupkg" DEL "*.nupkg"
dotnet clean -c Release ..\Oqtane.sln dotnet clean -c Release ..\Oqtane.sln
dotnet build -c Release ..\Oqtane.sln dotnet build -c Release ..\Oqtane.sln
dotnet pack -o .\ -c Release ..\Oqtane.sln
nuget.exe pack Oqtane.Framework.nuspec nuget.exe pack Oqtane.Framework.nuspec
nuget.exe pack Oqtane.Client.nuspec
nuget.exe pack Oqtane.Server.nuspec
nuget.exe pack Oqtane.Shared.nuspec

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -483,7 +483,7 @@ namespace Oqtane.Controllers
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries); string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
foreach (string folder in folders) foreach (string folder in folders)
{ {
path = Utilities.PathCombine(path, folder, "\\"); path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Oqtane.Models; using Oqtane.Models;
@ -112,7 +113,7 @@ namespace Oqtane.Controllers
Folder parent = _folders.GetFolder(folder.ParentId.Value); Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name); folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
} }
folder.Path = Utilities.PathCombine(folder.Path, "\\"); folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
folder = _folders.AddFolder(folder); folder = _folders.AddFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
} }
@ -147,7 +148,7 @@ namespace Oqtane.Controllers
Folder parent = _folders.GetFolder(folder.ParentId.Value); Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name); folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
} }
folder.Path = Utilities.PathCombine(folder.Path, "\\"); folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
folder = _folders.UpdateFolder(folder); folder = _folders.UpdateFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
} }

View File

@ -122,7 +122,7 @@ namespace Oqtane.Controllers
var pages = _pages.GetPages(module.SiteId).ToList(); var pages = _pages.GetPages(module.SiteId).ToList();
foreach (Page page in pages) foreach (Page page in pages)
{ {
if (page.PageId != pageModule.PageId && !page.EditMode) if (page.PageId != pageModule.PageId && !page.Path.StartsWith("admin/"))
{ {
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType }); _pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
} }

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
public class ModuleControllerBase : Controller
{
protected readonly ILogManager _logger;
protected int _entityId = -1; // passed as a querystring parameter for policy authorization and used for validation
public ModuleControllerBase(ILogManager logger, IHttpContextAccessor accessor)
{
_logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
}
}
}

View File

@ -170,17 +170,17 @@ namespace Oqtane.Controllers
{ {
string rootPath; string rootPath;
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath); DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template,"\\"); string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template,Path.DirectorySeparatorChar.ToString());
if (moduleDefinition.Template == "internal") if (moduleDefinition.Template == "internal")
{ {
rootPath = Utilities.PathCombine(rootFolder.FullName,"\\"); rootPath = Utilities.PathCombine(rootFolder.FullName,Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client"; moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
} }
else else
{ {
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\"); rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s",Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane"; moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane";
} }

View File

@ -125,7 +125,7 @@ namespace Oqtane.Controllers
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId); _syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
if (!page.EditMode) if (!page.Path.StartsWith("admin/"))
{ {
var modules = _modules.GetModules(page.SiteId).Where(item => item.AllPages).ToList(); var modules = _modules.GetModules(page.SiteId).Where(item => item.AllPages).ToList();
foreach (Module module in modules) foreach (Module module in modules)
@ -163,7 +163,6 @@ namespace Oqtane.Controllers
page.Order = 0; page.Order = 0;
page.IsNavigation = false; page.IsNavigation = false;
page.Url = ""; page.Url = "";
page.EditMode = false;
page.ThemeType = parent.ThemeType; page.ThemeType = parent.ThemeType;
page.LayoutType = parent.LayoutType; page.LayoutType = parent.LayoutType;
page.DefaultContainerType = parent.DefaultContainerType; page.DefaultContainerType = parent.DefaultContainerType;

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using Oqtane.Shared; using Oqtane.Shared;
using System; using System;
using System.IO;
using System.Net; using System.Net;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
@ -174,7 +175,7 @@ namespace Oqtane.Controllers
} }
// add folder for user // add folder for user
Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users","\\")); Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users",Path.DirectorySeparatorChar.ToString()));
if (folder != null) if (folder != null)
{ {
_folders.AddFolder(new Folder _folders.AddFolder(new Folder
@ -182,7 +183,7 @@ namespace Oqtane.Controllers
SiteId = folder.SiteId, SiteId = folder.SiteId,
ParentId = folder.FolderId, ParentId = folder.FolderId,
Name = "My Folder", Name = "My Folder",
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"), Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),Path.DirectorySeparatorChar.ToString()),
Order = 1, Order = 1,
IsSystem = true, IsSystem = true,
Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" +

View File

@ -461,7 +461,7 @@ namespace Oqtane.Infrastructure
userroles.AddUserRole(userRole); userroles.AddUserRole(userRole);
// add user folder // add user folder
var folder = folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", "\\")); var folder = folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", Path.DirectorySeparatorChar.ToString()));
if (folder != null) if (folder != null)
{ {
folders.AddFolder(new Folder folders.AddFolder(new Folder
@ -469,7 +469,7 @@ namespace Oqtane.Infrastructure
SiteId = folder.SiteId, SiteId = folder.SiteId,
ParentId = folder.FolderId, ParentId = folder.FolderId,
Name = "My Folder", Name = "My Folder",
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), "\\"), Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
Order = 1, Order = 1,
IsSystem = true, IsSystem = true,
Permissions = new List<Permission> Permissions = new List<Permission>

View File

@ -1,13 +1,13 @@
using System.Reflection; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Microsoft.Extensions.Hosting; using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using System.Xml; using System.Xml;
using Oqtane.Shared; using Microsoft.AspNetCore.Hosting;
using System;
using System.Diagnostics;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Oqtane.Shared;
namespace Oqtane.Infrastructure namespace Oqtane.Infrastructure
{ {
@ -88,7 +88,7 @@ namespace Oqtane.Infrastructure
// deploy to appropriate locations // deploy to appropriate locations
foreach (ZipArchiveEntry entry in archive.Entries) foreach (ZipArchiveEntry entry in archive.Entries)
{ {
string foldername = Path.GetDirectoryName(entry.FullName).Split('\\')[0]; string foldername = Path.GetDirectoryName(entry.FullName).Split(Path.DirectorySeparatorChar)[0];
string filename = Path.GetFileName(entry.FullName); string filename = Path.GetFileName(entry.FullName);
switch (foldername) switch (foldername)
@ -98,7 +98,12 @@ namespace Oqtane.Infrastructure
ExtractFile(entry, filename); ExtractFile(entry, filename);
break; break;
case "wwwroot": case "wwwroot":
filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("wwwroot/", "").Split('/'))); filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace($"wwwroot/", "").Split('/')));
ExtractFile(entry, filename);
break;
case "runtimes":
var destSubFolder = Path.GetDirectoryName(entry.FullName);
filename = Path.Combine(binFolder, destSubFolder, filename);
ExtractFile(entry, filename); ExtractFile(entry, filename);
break; break;
} }
@ -121,7 +126,25 @@ namespace Oqtane.Infrastructure
{ {
Directory.CreateDirectory(Path.GetDirectoryName(filename)); Directory.CreateDirectory(Path.GetDirectoryName(filename));
} }
entry.ExtractToFile(filename, true); if (!FileInUse(filename) == false)
{
entry.ExtractToFile(filename, true);
}
}
private static bool FileInUse(string path)
{
try
{
using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
{
var flag = fs.CanWrite;
}
return false;
}
catch
{
return true;
}
} }
public void UpgradeFramework() public void UpgradeFramework()
@ -131,7 +154,7 @@ namespace Oqtane.Infrastructure
{ {
// get package with highest version and clean up any others // get package with highest version and clean up any others
string packagename = ""; string packagename = "";
foreach(string package in Directory.GetFiles(folder, "Oqtane.Framework.*.nupkg")) foreach (string package in Directory.GetFiles(folder, "Oqtane.Framework.*.nupkg"))
{ {
if (packagename != "") if (packagename != "")
{ {

View File

@ -27,6 +27,8 @@ namespace Oqtane.Infrastructure
protected async Task ExecuteAsync(CancellationToken stoppingToken) protected async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
await Task.Yield(); // required so that this method does not block startup
try try
{ {
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
@ -41,21 +43,26 @@ namespace Oqtane.Infrastructure
Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault(); Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault();
if (job != null && job.IsEnabled && !job.IsExecuting) if (job != null && job.IsEnabled && !job.IsExecuting)
{ {
// set next execution date // get next execution date
DateTime NextExecution;
if (job.NextExecution == null) if (job.NextExecution == null)
{ {
if (job.StartDate != null) if (job.StartDate != null)
{ {
job.NextExecution = job.StartDate; NextExecution = job.StartDate.Value;
} }
else else
{ {
job.NextExecution = DateTime.UtcNow; NextExecution = DateTime.UtcNow;
} }
} }
else
{
NextExecution = job.NextExecution.Value;
}
// determine if the job should be run // determine if the job should be run
if (job.NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow)) if (NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow))
{ {
IJobLogRepository jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>(); IJobLogRepository jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
@ -89,7 +96,7 @@ namespace Oqtane.Infrastructure
jobLogs.UpdateJobLog(log); jobLogs.UpdateJobLog(log);
// update the job // update the job
job.NextExecution = CalculateNextExecution(job.NextExecution.Value, job.Frequency, job.Interval); job.NextExecution = CalculateNextExecution(NextExecution, job.Frequency, job.Interval);
job.IsExecuting = false; job.IsExecuting = false;
jobs.UpdateJob(job); jobs.UpdateJob(job);

View File

@ -20,11 +20,15 @@ namespace Oqtane.Infrastructure
{ {
string log = ""; string log = "";
// iterate through aliases in this installation // iterate through tenants in this installation
List<int> tenants = new List<int>();
var aliasRepository = provider.GetRequiredService<IAliasRepository>(); var aliasRepository = provider.GetRequiredService<IAliasRepository>();
List<Alias> aliases = aliasRepository.GetAliases().ToList(); List<Alias> aliases = aliasRepository.GetAliases().ToList();
foreach (Alias alias in aliases) foreach (Alias alias in aliases)
{ {
if (tenants.Contains(alias.TenantId)) continue;
tenants.Add(alias.TenantId);
// use the SiteState to set the Alias explicitly so the tenant can be resolved // use the SiteState to set the Alias explicitly so the tenant can be resolved
var siteState = provider.GetRequiredService<SiteState>(); var siteState = provider.GetRequiredService<SiteState>();
siteState.Alias = alias; siteState.Alias = alias;
@ -34,11 +38,11 @@ namespace Oqtane.Infrastructure
var settingRepository = provider.GetRequiredService<ISettingRepository>(); var settingRepository = provider.GetRequiredService<ISettingRepository>();
var notificationRepository = provider.GetRequiredService<INotificationRepository>(); var notificationRepository = provider.GetRequiredService<INotificationRepository>();
// iterate through sites // iterate through sites for this tenant
List<Site> sites = siteRepository.GetSites().ToList(); List<Site> sites = siteRepository.GetSites().ToList();
foreach (Site site in sites) foreach (Site site in sites)
{ {
log += "Processing Notifications For Site: " + site.Name + "\n\n"; log += "Processing Notifications For Site: " + site.Name + "<br />";
// get site settings // get site settings
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList(); List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
@ -101,14 +105,14 @@ namespace Oqtane.Infrastructure
catch (Exception ex) catch (Exception ex)
{ {
// error // error
log += ex.Message + "\n\n"; log += ex.Message + "<br />";
} }
} }
log += "Notifications Delivered: " + sent + "\n\n"; log += "Notifications Delivered: " + sent + "<br />";
} }
else else
{ {
log += "SMTP Not Configured" + "\n\n"; log += "SMTP Not Configured" + "<br />";
} }
} }
} }
@ -116,7 +120,6 @@ namespace Oqtane.Infrastructure
return log; return log;
} }
private Dictionary<string, string> GetSettings(List<Setting> settings) private Dictionary<string, string> GetSettings(List<Setting> settings)
{ {
Dictionary<string, string> dictionary = new Dictionary<string, string>(); Dictionary<string, string> dictionary = new Dictionary<string, string>();

View File

@ -42,7 +42,6 @@ namespace Oqtane.SiteTemplates
Icon = "home", Icon = "home",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> { PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true),
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -89,7 +88,6 @@ namespace Oqtane.SiteTemplates
Icon = "lock-locked", Icon = "lock-locked",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> { PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.RegisteredRole, true), new Permission(PermissionNames.View, Constants.RegisteredRole, true),
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -114,7 +112,6 @@ namespace Oqtane.SiteTemplates
Icon = "target", Icon = "target",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = true, IsPersonalizable = true,
EditMode = false,
PagePermissions = new List<Permission> { PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true),
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -134,7 +131,7 @@ namespace Oqtane.SiteTemplates
if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo-white.png"))) if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo-white.png")))
{ {
string folderpath = Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString(),"\\"); string folderpath = Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(), "Sites", site.SiteId.ToString(), Path.DirectorySeparatorChar.ToString());
System.IO.Directory.CreateDirectory(folderpath); System.IO.Directory.CreateDirectory(folderpath);
if (!System.IO.File.Exists(Path.Combine(folderpath, "logo-white.png"))) if (!System.IO.File.Exists(Path.Combine(folderpath, "logo-white.png")))
{ {

View File

@ -30,7 +30,6 @@ namespace Oqtane.SiteTemplates
Icon = "home", Icon = "home",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> { PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.AllUsersRole, true), new Permission(PermissionNames.View, Constants.AllUsersRole, true),
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),

View File

@ -8,24 +8,18 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Controllers;
namespace Oqtane.Modules.HtmlText.Controllers namespace Oqtane.Modules.HtmlText.Controllers
{ {
[Route("{alias}/api/[controller]")] [Route("{alias}/api/[controller]")]
public class HtmlTextController : Controller public class HtmlTextController : ModuleControllerBase
{ {
private readonly IHtmlTextRepository _htmlText; private readonly IHtmlTextRepository _htmlText;
private readonly ILogManager _logger;
private int _entityId = -1; // passed as a querystring parameter for authorization and used for validation
public HtmlTextController(IHtmlTextRepository htmlText, ILogManager logger, IHttpContextAccessor httpContextAccessor) public HtmlTextController(IHtmlTextRepository htmlText, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
{ {
_htmlText = htmlText; _htmlText = htmlText;
_logger = logger;
if (httpContextAccessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(httpContextAccessor.HttpContext.Request.Query["entityid"]);
}
} }
// GET api/<controller>/5 // GET api/<controller>/5

View File

@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>
@ -22,6 +22,9 @@
<Content Remove="wwwroot\Modules\Templates\**" /> <Content Remove="wwwroot\Modules\Templates\**" />
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" /> <EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="Scripts\Tenant.01.00.02.01.sql" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Scripts\Master.00.00.00.00.sql" /> <EmbeddedResource Include="Scripts\Master.00.00.00.00.sql" />
<EmbeddedResource Include="Scripts\Master.00.09.00.00.sql" /> <EmbeddedResource Include="Scripts\Master.00.09.00.00.sql" />
@ -32,6 +35,7 @@
<EmbeddedResource Include="Scripts\Tenant.00.09.02.00.sql" /> <EmbeddedResource Include="Scripts\Tenant.00.09.02.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.01.00.01.00.sql" /> <EmbeddedResource Include="Scripts\Tenant.01.00.01.00.sql" />
<EmbeddedResource Include="Scripts\Tenant.01.00.01.01.sql" /> <EmbeddedResource Include="Scripts\Tenant.01.00.01.01.sql" />
<EmbeddedResource Include="Scripts\Tenant.01.00.02.01.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" /> <EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.1.0.0.sql" />
<EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" /> <EmbeddedResource Include="Modules\HtmlText\Scripts\HtmlText.Uninstall.sql" />
</ItemGroup> </ItemGroup>

View File

@ -123,7 +123,7 @@ namespace Oqtane.Repository
ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); ModuleDefinition moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault();
if (moduledefinition != null) if (moduledefinition != null)
{ {
ModuleContent modulecontent = JsonSerializer.Deserialize<ModuleContent>(content); ModuleContent modulecontent = JsonSerializer.Deserialize<ModuleContent>(content.Replace("\n", ""));
if (modulecontent.ModuleDefinitionName == moduledefinition.ModuleDefinitionName) if (modulecontent.ModuleDefinitionName == moduledefinition.ModuleDefinitionName)
{ {
if (moduledefinition.ServerManagerType != "") if (moduledefinition.ServerManagerType != "")
@ -147,9 +147,10 @@ namespace Oqtane.Repository
} }
} }
} }
catch catch (Exception ex)
{ {
// error occurred during import // error occurred during import
string error = ex.Message;
} }
return success; return success;

View File

@ -21,7 +21,7 @@ namespace Oqtane.Repository
return _db.Notification return _db.Notification
.Where(item => item.SiteId == siteId) .Where(item => item.SiteId == siteId)
.Where(item => item.IsDelivered == false) .Where(item => item.IsDelivered == false)
.Where(item => item.SendOn < System.DateTime.UtcNow) .Where(item => item.SendOn == null || item.SendOn < System.DateTime.UtcNow)
.ToList(); .ToList();
} }

View File

@ -58,7 +58,6 @@ namespace Oqtane.Repository
Icon = Icons.LockLocked, Icon = Icons.LockLocked,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -88,7 +87,6 @@ namespace Oqtane.Repository
Icon = Icons.Person, Icon = Icons.Person,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -119,7 +117,6 @@ namespace Oqtane.Repository
Icon = Icons.Person, Icon = Icons.Person,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -149,7 +146,6 @@ namespace Oqtane.Repository
Icon = Icons.Person, Icon = Icons.Person,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -175,7 +171,7 @@ namespace Oqtane.Repository
// admin pages // admin pages
pageTemplates.Add(new PageTemplate pageTemplates.Add(new PageTemplate
{ {
Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false, EditMode = true, Name = "Admin", Parent = "", Path = "admin", Icon = "", IsNavigation = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -203,7 +199,6 @@ namespace Oqtane.Repository
Icon = Icons.Home, Icon = Icons.Home,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -231,7 +226,6 @@ namespace Oqtane.Repository
Icon = Icons.Layers, Icon = Icons.Layers,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -259,7 +253,6 @@ namespace Oqtane.Repository
Icon = Icons.People, Icon = Icons.People,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -287,7 +280,6 @@ namespace Oqtane.Repository
Icon = Icons.Person, Icon = Icons.Person,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -315,7 +307,6 @@ namespace Oqtane.Repository
Icon = Icons.LockLocked, Icon = Icons.LockLocked,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -343,7 +334,6 @@ namespace Oqtane.Repository
Icon = Icons.File, Icon = Icons.File,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -371,7 +361,6 @@ namespace Oqtane.Repository
Icon = Icons.Trash, Icon = Icons.Trash,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.AdminRole, true), new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -401,7 +390,6 @@ namespace Oqtane.Repository
Icon = Icons.MagnifyingGlass, Icon = Icons.MagnifyingGlass,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -428,7 +416,6 @@ namespace Oqtane.Repository
Icon = Icons.List, Icon = Icons.List,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -450,7 +437,7 @@ namespace Oqtane.Repository
}); });
pageTemplates.Add(new PageTemplate pageTemplates.Add(new PageTemplate
{ {
Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = Icons.Globe, IsNavigation = false, IsPersonalizable = false, EditMode = true, Name = "Site Management", Parent = "Admin", Path = "admin/sites", Icon = Icons.Globe, IsNavigation = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -472,7 +459,7 @@ namespace Oqtane.Repository
}); });
pageTemplates.Add(new PageTemplate pageTemplates.Add(new PageTemplate
{ {
Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = Icons.Browser, IsNavigation = false, IsPersonalizable = false, EditMode = true, Name = "Module Management", Parent = "Admin", Path = "admin/modules", Icon = Icons.Browser, IsNavigation = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -494,7 +481,7 @@ namespace Oqtane.Repository
}); });
pageTemplates.Add(new PageTemplate pageTemplates.Add(new PageTemplate
{ {
Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = Icons.Brush, IsNavigation = false, IsPersonalizable = false, EditMode = true, Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = Icons.Brush, IsNavigation = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -516,7 +503,7 @@ namespace Oqtane.Repository
}); });
pageTemplates.Add(new PageTemplate pageTemplates.Add(new PageTemplate
{ {
Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = Icons.Timer, IsNavigation = false, IsPersonalizable = false, EditMode = true, Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = Icons.Timer, IsNavigation = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -544,7 +531,6 @@ namespace Oqtane.Repository
Icon = "spreadsheet", Icon = "spreadsheet",
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -572,7 +558,6 @@ namespace Oqtane.Repository
Icon = "medical-cross", Icon = "medical-cross",
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
EditMode = true,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -594,7 +579,7 @@ namespace Oqtane.Repository
}); });
pageTemplates.Add(new PageTemplate pageTemplates.Add(new PageTemplate
{ {
Name = "System Update", Parent = "Admin", Path = "admin/update", Icon = Icons.Aperture, IsNavigation = false, IsPersonalizable = false, EditMode = true, Name = "System Update", Parent = "Admin", Path = "admin/update", Icon = Icons.Aperture, IsNavigation = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PagePermissions = new List<Permission>
{ {
new Permission(PermissionNames.View, Constants.HostRole, true), new Permission(PermissionNames.View, Constants.HostRole, true),
@ -692,7 +677,7 @@ namespace Oqtane.Repository
}); });
_folderRepository.AddFolder(new Folder _folderRepository.AddFolder(new Folder
{ {
SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = Utilities.PathCombine("Users","\\"), Order = 1, IsSystem = true, SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Path = Utilities.PathCombine("Users",Path.DirectorySeparatorChar.ToString()), Order = 1, IsSystem = true,
Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]" Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]"
}); });
@ -754,7 +739,6 @@ namespace Oqtane.Repository
Order = 1, Order = 1,
Url = "", Url = "",
IsNavigation = pagetemplate.IsNavigation, IsNavigation = pagetemplate.IsNavigation,
EditMode = pagetemplate.EditMode,
ThemeType = "", ThemeType = "",
LayoutType = "", LayoutType = "",
DefaultContainerType = "", DefaultContainerType = "",

View File

@ -0,0 +1,9 @@
/*
Version 1.0.2.1 migration script
*/
ALTER TABLE [dbo].[Page]
DROP COLUMN EditMode
GO

View File

@ -72,7 +72,7 @@ namespace Oqtane
}); });
} }
// register authorization services // register custom authorization policies
services.AddAuthorizationCore(options => services.AddAuthorizationCore(options =>
{ {
options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View))); options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View)));

View File

@ -50,7 +50,7 @@
if (PageState.Action == "Edit") if (PageState.Action == "Edit")
{ {
_id = Int32.Parse(PageState.QueryString["id"]); _id = Int32.Parse(PageState.QueryString["id"]);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id); [Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
if ([Module] != null) if ([Module] != null)
{ {
_name = [Module].Name; _name = [Module].Name;
@ -82,7 +82,7 @@
} }
else else
{ {
[Module] [Module] = await [Module]Service.Get[Module]Async(_id); [Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name; [Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]); await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]); await logger.LogInformation("[Module] Updated {[Module]}", [Module]);

View File

@ -95,7 +95,7 @@ else
{ {
try try
{ {
await [Module]Service.Delete[Module]Async([Module].[Module]Id); await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]); await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
StateHasChanged(); StateHasChanged();

View File

@ -8,12 +8,12 @@ namespace [Owner].[Module]s.Services
{ {
Task<List<[Module]>> Get[Module]sAsync(int ModuleId); Task<List<[Module]>> Get[Module]sAsync(int ModuleId);
Task<[Module]> Get[Module]Async(int [Module]Id); Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId);
Task<[Module]> Add[Module]Async([Module] [Module]); Task<[Module]> Add[Module]Async([Module] [Module]);
Task<[Module]> Update[Module]Async([Module] [Module]); Task<[Module]> Update[Module]Async([Module] [Module]);
Task Delete[Module]Async(int [Module]Id); Task Delete[Module]Async(int [Module]Id, int ModuleId);
} }
} }

View File

@ -18,32 +18,44 @@ namespace [Owner].[Module]s.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]"); private string Apiurl => CreateApiUrl(_siteState.Alias, "[Module]");
public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId) public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId)
{ {
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>($"{Apiurl}?moduleid={ModuleId}"); List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>(CreateAuthPolicyUrl($"{Apiurl}?moduleid={ModuleId}", ModuleId));
return [Module]s.OrderBy(item => item.Name).ToList(); return [Module]s.OrderBy(item => item.Name).ToList();
} }
public async Task<[Module]> Get[Module]Async(int [Module]Id) public async Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
{ {
return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}"); return await GetJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
} }
public async Task<[Module]> Add[Module]Async([Module] [Module]) public async Task<[Module]> Add[Module]Async([Module] [Module])
{ {
return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]); return await PostJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}", [Module].ModuleId), [Module]);
} }
public async Task<[Module]> Update[Module]Async([Module] [Module]) public async Task<[Module]> Update[Module]Async([Module] [Module])
{ {
return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]); return await PutJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module].[Module]Id}", [Module].ModuleId), [Module]);
} }
public async Task Delete[Module]Async(int [Module]Id) public async Task Delete[Module]Async(int [Module]Id, int ModuleId)
{ {
await DeleteAsync($"{Apiurl}/{[Module]Id}"); await DeleteAsync(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
}
private string CreateAuthPolicyUrl(string Url, int ModuleId)
{
if (Url.Contains("?"))
{
return Url + "&entityid=" + ModuleId.ToString();
}
else
{
return Url + "?entityid=" + ModuleId.ToString();
}
} }
} }
} }

View File

@ -25,8 +25,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Oqtane.Client" Version="1.0.1" /> <PackageReference Include="Oqtane.Client" Version="1.0.2" />
<PackageReference Include="Oqtane.Shared" Version="1.0.1" /> <PackageReference Include="Oqtane.Shared" Version="1.0.2" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>

View File

@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
@ -9,21 +10,27 @@ using [Owner].[Module]s.Repository;
namespace [Owner].[Module]s.Controllers namespace [Owner].[Module]s.Controllers
{ {
[Route("{site}/api/[controller]")] [Route("{alias}/api/[controller]")]
public class [Module]Controller : Controller public class [Module]Controller : Controller
{ {
private readonly I[Module]Repository _[Module]s; private readonly I[Module]Repository _[Module]s;
private readonly ILogManager _logger; private readonly ILogManager _logger;
protected int _entityId = -1;
public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger) public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger, IHttpContextAccessor accessor)
{ {
_[Module]s = [Module]s; _[Module]s = [Module]s;
_logger = logger; _logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
} }
// GET: api/<controller>?moduleid=x // GET: api/<controller>?moduleid=x
[HttpGet] [HttpGet]
[Authorize(Roles = Constants.RegisteredRole)] [Authorize(Policy = "ViewModule")]
public IEnumerable<[Module]> Get(string moduleid) public IEnumerable<[Module]> Get(string moduleid)
{ {
return _[Module]s.Get[Module]s(int.Parse(moduleid)); return _[Module]s.Get[Module]s(int.Parse(moduleid));
@ -31,18 +38,23 @@ namespace [Owner].[Module]s.Controllers
// GET api/<controller>/5 // GET api/<controller>/5
[HttpGet("{id}")] [HttpGet("{id}")]
[Authorize(Roles = Constants.RegisteredRole)] [Authorize(Policy = "ViewModule")]
public [Module] Get(int id) public [Module] Get(int id)
{ {
return _[Module]s.Get[Module](id); [Module] [Module] = _[Module]s.Get[Module](id);
if ([Module] != null && [Module].ModuleId != _entityId)
{
[Module] = null;
}
return [Module];
} }
// POST api/<controller> // POST api/<controller>
[HttpPost] [HttpPost]
[Authorize(Roles = Constants.AdminRole)] [Authorize(Policy = "EditModule")]
public [Module] Post([FromBody] [Module] [Module]) public [Module] Post([FromBody] [Module] [Module])
{ {
if (ModelState.IsValid) if (ModelState.IsValid && [Module].ModuleId == _entityId)
{ {
[Module] = _[Module]s.Add[Module]([Module]); [Module] = _[Module]s.Add[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]); _logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
@ -52,10 +64,10 @@ namespace [Owner].[Module]s.Controllers
// PUT api/<controller>/5 // PUT api/<controller>/5
[HttpPut("{id}")] [HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)] [Authorize(Policy = "EditModule")]
public [Module] Put(int id, [FromBody] [Module] [Module]) public [Module] Put(int id, [FromBody] [Module] [Module])
{ {
if (ModelState.IsValid) if (ModelState.IsValid && [Module].ModuleId == _entityId)
{ {
[Module] = _[Module]s.Update[Module]([Module]); [Module] = _[Module]s.Update[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]); _logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
@ -65,11 +77,15 @@ namespace [Owner].[Module]s.Controllers
// DELETE api/<controller>/5 // DELETE api/<controller>/5
[HttpDelete("{id}")] [HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)] [Authorize(Policy = "EditModule")]
public void Delete(int id) public void Delete(int id)
{ {
_[Module]s.Delete[Module](id); [Module] [Module] = _[Module]s.Get[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id); if ([Module] != null && [Module].ModuleId == _entityId)
{
_[Module]s.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
}
} }
} }
} }

View File

@ -31,7 +31,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Oqtane.Server" Version="1.0.1" /> <PackageReference Include="Oqtane.Server" Version="1.0.2" />
<PackageReference Include="Oqtane.Shared" Version="1.0.1" /> <PackageReference Include="Oqtane.Shared" Version="1.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1 +0,0 @@
This is the location where static resources such as images, style sheets, or scripts for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module namespace.

View File

@ -17,7 +17,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Oqtane.Shared" Version="1.0.1" /> <PackageReference Include="Oqtane.Shared" Version="1.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -28,8 +28,14 @@
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Actions => "Add,Edit"; public override string Actions => "Add,Edit";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
int _id; int _id;
string _name; string _name;
string _createdby; string _createdby;
@ -44,7 +50,7 @@
if (PageState.Action == "Edit") if (PageState.Action == "Edit")
{ {
_id = Int32.Parse(PageState.QueryString["id"]); _id = Int32.Parse(PageState.QueryString["id"]);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id); [Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
if ([Module] != null) if ([Module] != null)
{ {
_name = [Module].Name; _name = [Module].Name;
@ -76,7 +82,7 @@
} }
else else
{ {
[Module] [Module] = await [Module]Service.Get[Module]Async(_id); [Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name; [Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]); await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]); await logger.LogInformation("[Module] Updated {[Module]}", [Module]);

View File

@ -61,6 +61,11 @@ else
<!-- The content above is for informational purposes only and can be safely removed --> <!-- The content above is for informational purposes only and can be safely removed -->
@code { @code {
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
List<[Module]> _[Module]s; List<[Module]> _[Module]s;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@ -80,7 +85,7 @@ else
{ {
try try
{ {
await [Module]Service.Delete[Module]Async([Module].[Module]Id); await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]); await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId); _[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
StateHasChanged(); StateHasChanged();

View File

@ -8,12 +8,12 @@ namespace [Owner].[Module]s.Services
{ {
Task<List<[Module]>> Get[Module]sAsync(int ModuleId); Task<List<[Module]>> Get[Module]sAsync(int ModuleId);
Task<[Module]> Get[Module]Async(int [Module]Id); Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId);
Task<[Module]> Add[Module]Async([Module] [Module]); Task<[Module]> Add[Module]Async([Module] [Module]);
Task<[Module]> Update[Module]Async([Module] [Module]); Task<[Module]> Update[Module]Async([Module] [Module]);
Task Delete[Module]Async(int [Module]Id); Task Delete[Module]Async(int [Module]Id, int ModuleId);
} }
} }

View File

@ -18,32 +18,44 @@ namespace [Owner].[Module]s.Services
_siteState = siteState; _siteState = siteState;
} }
private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]"); private string Apiurl => CreateApiUrl(_siteState.Alias, "[Module]");
public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId) public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId)
{ {
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>($"{Apiurl}?moduleid={ModuleId}"); List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>(CreateAuthPolicyUrl($"{Apiurl}?moduleid={ModuleId}", ModuleId));
return [Module]s.OrderBy(item => item.Name).ToList(); return [Module]s.OrderBy(item => item.Name).ToList();
} }
public async Task<[Module]> Get[Module]Async(int [Module]Id) public async Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
{ {
return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}"); return await GetJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
} }
public async Task<[Module]> Add[Module]Async([Module] [Module]) public async Task<[Module]> Add[Module]Async([Module] [Module])
{ {
return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]); return await PostJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}?entityid={[Module].ModuleId}", [Module].ModuleId), [Module]);
} }
public async Task<[Module]> Update[Module]Async([Module] [Module]) public async Task<[Module]> Update[Module]Async([Module] [Module])
{ {
return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]); return await PutJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module].[Module]Id}", [Module].ModuleId), [Module]);
} }
public async Task Delete[Module]Async(int [Module]Id) public async Task Delete[Module]Async(int [Module]Id, int ModuleId)
{ {
await DeleteAsync($"{Apiurl}/{[Module]Id}"); await DeleteAsync(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
}
private string CreateAuthPolicyUrl(string Url, int ModuleId)
{
if (Url.Contains("?"))
{
return Url + "&entityid=" + ModuleId.ToString();
}
else
{
return Url + "?entityid=" + ModuleId.ToString();
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
@ -9,21 +10,27 @@ using [Owner].[Module]s.Repository;
namespace [Owner].[Module]s.Controllers namespace [Owner].[Module]s.Controllers
{ {
[Route("{site}/api/[controller]")] [Route("{alias}/api/[controller]")]
public class [Module]Controller : Controller public class [Module]Controller : Controller
{ {
private readonly I[Module]Repository _[Module]s; private readonly I[Module]Repository _[Module]s;
private readonly ILogManager _logger; private readonly ILogManager _logger;
protected int _entityId = -1;
public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger) public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger, IHttpContextAccessor accessor)
{ {
_[Module]s = [Module]s; _[Module]s = [Module]s;
_logger = logger; _logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
} }
// GET: api/<controller>?moduleid=x // GET: api/<controller>?moduleid=x
[HttpGet] [HttpGet]
[Authorize(Roles = Constants.RegisteredRole)] [Authorize(Policy = "ViewModule")]
public IEnumerable<[Module]> Get(string moduleid) public IEnumerable<[Module]> Get(string moduleid)
{ {
return _[Module]s.Get[Module]s(int.Parse(moduleid)); return _[Module]s.Get[Module]s(int.Parse(moduleid));
@ -31,18 +38,23 @@ namespace [Owner].[Module]s.Controllers
// GET api/<controller>/5 // GET api/<controller>/5
[HttpGet("{id}")] [HttpGet("{id}")]
[Authorize(Roles = Constants.RegisteredRole)] [Authorize(Policy = "ViewModule")]
public [Module] Get(int id) public [Module] Get(int id)
{ {
return _[Module]s.Get[Module](id); [Module] [Module] = _[Module]s.Get[Module](id);
if ([Module] != null && [Module].ModuleId != _entityId)
{
[Module] = null;
}
return [Module];
} }
// POST api/<controller> // POST api/<controller>
[HttpPost] [HttpPost]
[Authorize(Roles = Constants.AdminRole)] [Authorize(Policy = "EditModule")]
public [Module] Post([FromBody] [Module] [Module]) public [Module] Post([FromBody] [Module] [Module])
{ {
if (ModelState.IsValid) if (ModelState.IsValid && [Module].ModuleId == _entityId)
{ {
[Module] = _[Module]s.Add[Module]([Module]); [Module] = _[Module]s.Add[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]); _logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
@ -52,10 +64,10 @@ namespace [Owner].[Module]s.Controllers
// PUT api/<controller>/5 // PUT api/<controller>/5
[HttpPut("{id}")] [HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)] [Authorize(Policy = "EditModule")]
public [Module] Put(int id, [FromBody] [Module] [Module]) public [Module] Put(int id, [FromBody] [Module] [Module])
{ {
if (ModelState.IsValid) if (ModelState.IsValid && [Module].ModuleId == _entityId)
{ {
[Module] = _[Module]s.Update[Module]([Module]); [Module] = _[Module]s.Update[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]); _logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
@ -65,11 +77,15 @@ namespace [Owner].[Module]s.Controllers
// DELETE api/<controller>/5 // DELETE api/<controller>/5
[HttpDelete("{id}")] [HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)] [Authorize(Policy = "EditModule")]
public void Delete(int id) public void Delete(int id)
{ {
_[Module]s.Delete[Module](id); [Module] [Module] = _[Module]s.Get[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id); if ([Module] != null && [Module].ModuleId == _entityId)
{
_[Module]s.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
}
} }
} }
} }

View File

@ -1 +0,0 @@
This is the location where static resources such as images, style sheets, or scripts for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module namespace.

View File

@ -20,6 +20,7 @@ namespace Oqtane.Models
ServerManagerType = ""; ServerManagerType = "";
ControlTypeRoutes = ""; ControlTypeRoutes = "";
ReleaseVersions = ""; ReleaseVersions = "";
Runtimes = "";
Template = ""; Template = "";
} }
@ -45,6 +46,8 @@ namespace Oqtane.Models
[NotMapped] [NotMapped]
public string License { get; set; } public string License { get; set; }
[NotMapped] [NotMapped]
public string Runtimes { get; set; }
[NotMapped]
public string Dependencies { get; set; } public string Dependencies { get; set; }
[NotMapped] [NotMapped]
public string PermissionNames { get; set; } public string PermissionNames { get; set; }

View File

@ -19,7 +19,6 @@ namespace Oqtane.Models
public string DefaultContainerType { get; set; } public string DefaultContainerType { get; set; }
public string Icon { get; set; } public string Icon { get; set; }
public bool IsNavigation { get; set; } public bool IsNavigation { get; set; }
public bool EditMode { get; set; }
public int? UserId { get; set; } public int? UserId { get; set; }
public bool IsPersonalizable { get; set; } public bool IsPersonalizable { get; set; }
@ -41,5 +40,9 @@ namespace Oqtane.Models
public int Level { get; set; } public int Level { get; set; }
[NotMapped] [NotMapped]
public bool HasChildren { get; set; } public bool HasChildren { get; set; }
[Obsolete("This property is obsolete", false)]
[NotMapped]
public bool EditMode { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
namespace Oqtane.Models namespace Oqtane.Models
{ {
@ -16,9 +17,11 @@ namespace Oqtane.Models
public string Icon { get; set; } public string Icon { get; set; }
public bool IsNavigation { get; set; } public bool IsNavigation { get; set; }
public bool IsPersonalizable { get; set; } public bool IsPersonalizable { get; set; }
public bool EditMode { get; set; }
public string PagePermissions { get; set; } public string PagePermissions { get; set; }
public List<PageTemplateModule> PageTemplateModules { get; set; } public List<PageTemplateModule> PageTemplateModules { get; set; }
[Obsolete("This property is obsolete", false)]
public bool EditMode { get; set; }
} }
public class PageTemplateModule public class PageTemplateModule

View File

@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@ -5,8 +5,8 @@ namespace Oqtane.Shared
public class Constants public class Constants
{ {
public const string PackageId = "Oqtane.Framework"; public const string PackageId = "Oqtane.Framework";
public const string Version = "1.0.1"; public const string Version = "1.0.2";
public const string ReleaseVersions = "0.9.0,0.9.1,0.9.2,1.0.0,1.0.1"; public const string ReleaseVersions = "0.9.0,0.9.1,0.9.2,1.0.0,1.0.1,1.0.2";
public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client"; public const string PageComponent = "Oqtane.UI.ThemeBuilder, Oqtane.Client";
public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client"; public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client";
@ -19,6 +19,8 @@ namespace Oqtane.Shared
public const string ActionToken = "{Action}"; public const string ActionToken = "{Action}";
public const string DefaultAction = "Index"; public const string DefaultAction = "Index";
public const string AdminPane = "Admin"; public const string AdminPane = "Admin";
public const string ModuleDelimiter = "*";
public const string UrlParametersDelimiter = "!";
// Default Module Actions are reserved and should not be used by modules // Default Module Actions are reserved and should not be used by modules
public static readonly string[] DefaultModuleActions = new[] { "Settings", "Import", "Export" }; public static readonly string[] DefaultModuleActions = new[] { "Settings", "Import", "Export" };
@ -46,6 +48,7 @@ namespace Oqtane.Shared
public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png"; public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png";
public const string UploadableFiles = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; public const string UploadableFiles = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg";
public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$"; public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$";
public static readonly char[] InvalidFileNameChars = public static readonly char[] InvalidFileNameChars =
{ {
'\"', '<', '>', '|', '\0', (Char) 1, (Char) 2, (Char) 3, (Char) 4, (Char) 5, (Char) 6, (Char) 7, (Char) 8, '\"', '<', '>', '|', '\0', (Char) 1, (Char) 2, (Char) 3, (Char) 4, (Char) 5, (Char) 6, (Char) 7, (Char) 8,

View File

@ -20,8 +20,53 @@ namespace Oqtane.Shared
return $"{type.Namespace}, {assemblyName}"; return $"{type.Namespace}, {assemblyName}";
} }
public static (string UrlParameters, string Querystring, string Anchor) ParseParameters(string parameters)
{
// /urlparameters /urlparameters?Id=1 /urlparameters#5 /urlparameters?Id=1#5 /urlparameters?reload#5
// Id=1 Id=1#5 reload#5 reload
// #5
var urlparameters = string.Empty;
var querystring = string.Empty;
var anchor = string.Empty;
if (parameters.Contains('#'))
{
anchor = parameters.Split('#').Last();
parameters = parameters.Replace("#" + anchor, "");
}
if (parameters.Contains('?'))
{
urlparameters = parameters.Split('?').First();
querystring = parameters.Replace(urlparameters + "?", "");
}
else if (parameters.Contains('/'))
{
urlparameters = parameters;
}
else
{
querystring = parameters;
}
return (urlparameters, querystring, anchor);
}
public static string NavigateUrl(string alias, string path, string parameters) public static string NavigateUrl(string alias, string path, string parameters)
{ {
string urlparameters;
string querystring;
string anchor;
(urlparameters, querystring, anchor) = ParseParameters(parameters);
if (!string.IsNullOrEmpty(urlparameters))
{
if (urlparameters.StartsWith("/")) urlparameters = urlparameters.Remove(0, 1);
path += $"/{Constants.UrlParametersDelimiter}/{urlparameters}";
}
var uriBuilder = new UriBuilder var uriBuilder = new UriBuilder
{ {
Path = !string.IsNullOrEmpty(alias) Path = !string.IsNullOrEmpty(alias)
@ -29,17 +74,18 @@ namespace Oqtane.Shared
? $"{alias}/{path}" ? $"{alias}/{path}"
: $"{alias}" : $"{alias}"
: $"{path}", : $"{path}",
Query = parameters Query = querystring,
}; };
anchor = string.IsNullOrEmpty(anchor) ? "" : "#" + anchor;
return uriBuilder.Uri.PathAndQuery; var navigateUrl = uriBuilder.Uri.PathAndQuery + anchor;
return navigateUrl;
} }
public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) public static string EditUrl(string alias, string path, int moduleid, string action, string parameters)
{ {
if (moduleid != -1) if (moduleid != -1)
{ {
path += $"/{moduleid}"; path += $"/{Constants.ModuleDelimiter}/{moduleid}";
if (!string.IsNullOrEmpty(action)) if (!string.IsNullOrEmpty(action))
{ {
path += $"/{action}"; path += $"/{action}";
@ -136,6 +182,7 @@ namespace Oqtane.Shared
stringBuilder.Append(RemapInternationalCharToAscii(c)); stringBuilder.Append(RemapInternationalCharToAscii(c));
prevdash = false; prevdash = false;
break; break;
case UnicodeCategory.SpaceSeparator: case UnicodeCategory.SpaceSeparator:
case UnicodeCategory.ConnectorPunctuation: case UnicodeCategory.ConnectorPunctuation:
case UnicodeCategory.DashPunctuation: case UnicodeCategory.DashPunctuation:
@ -250,7 +297,7 @@ namespace Oqtane.Shared
public static string PathCombine(params string[] segments) public static string PathCombine(params string[] segments)
{ {
var separators = new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}; var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
for (int i = 1; i < segments.Length; i++) for (int i = 1; i < segments.Length; i++)
{ {
@ -284,7 +331,6 @@ namespace Oqtane.Shared
!Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0])); !Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0]));
} }
public static bool TryGetQueryValue( public static bool TryGetQueryValue(
this Uri uri, this Uri uri,
string key, string key,
@ -304,7 +350,7 @@ namespace Oqtane.Shared
{ {
value = defaultValue; value = defaultValue;
string s; string s;
return uri.TryGetQueryValue(key, out s, (string) null) && int.TryParse(s, out value); return uri.TryGetQueryValue(key, out s, (string)null) && int.TryParse(s, out value);
} }
public static Dictionary<string, string> ParseQueryString(string query) public static Dictionary<string, string> ParseQueryString(string query)
@ -314,7 +360,7 @@ namespace Oqtane.Shared
{ {
query = query.Substring(1); query = query.Substring(1);
string str = query; string str = query;
char[] separator = new char[1] {'&'}; char[] separator = new char[1] { '&' };
foreach (string key in str.Split(separator, StringSplitOptions.RemoveEmptyEntries)) foreach (string key in str.Split(separator, StringSplitOptions.RemoveEmptyEntries))
{ {
if (key != "") if (key != "")

View File

@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@ -1,23 +1,35 @@
# Oqtane Framework # Oqtane Framework
Oqtane is a Modular Application Framework for Blazor Oqtane is a Modular Application Framework for Blazor
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json)
![Oqtane](https://github.com/oqtane/framework/blob/master/oqtane.png?raw=true "Oqtane") ![Oqtane](https://github.com/oqtane/framework/blob/master/oqtane.png?raw=true "Oqtane")
Oqtane uses Blazor, an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor apps are composed of reusable web UI components implemented using C#, HTML, and CSS. Both client and server code is written in C#, allowing you to share code and libraries. Oqtane uses Blazor, an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor apps are composed of reusable web UI components implemented using C#, HTML, and CSS. Both client and server code is written in C#, allowing you to share code and libraries.
Oqtane is being developed based on some fundamental principles which are outlined in the [Oqtane Philosophy](https://www.oqtane.org/Resources/Blog/PostId/538/oqtane-philosophy).
Please note that this project is owned by the .NET Foundation and is governed by the **[.NET Foundation Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)** Please note that this project is owned by the .NET Foundation and is governed by the **[.NET Foundation Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)**
**To get started with Oqtane:** # Getting Started
1.&nbsp;Install **[.NET Core 3.1 SDK (v3.1.300)](https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.300-windows-x64-installer)**. **Using the latest code:**
2.&nbsp;Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload enabled. Oqtane works with all editions of Visual Studio from Community to Enterprise. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**. - Install **[.NET Core 3.1 SDK (v3.1.300)](https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.300-windows-x64-installer)**.
3.&nbsp;Download or Clone the Oqtane source code to your local system. Open the **Oqtane.sln** solution file and Build the solution. - Install the Preview edition of [Visual Studio 2019](https://visualstudio.microsoft.com/vs/preview/) (version 16.6 or higher) with the **ASP.NET and web development** workload enabled. Oqtane works with all editions of Visual Studio from Community to Enterprise. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**.
NOTE: If you have already installed a previous version of Oqtane and you wish to do a clean database install, simply reset the DefaultConnection value in the Oqtane.Server\appsettings.json file to "". This will trigger a re-install when you run the application which will execute the database installation scripts. - Download or Clone the Oqtane source code to your local system. Open the **Oqtane.sln** solution file and Build the solution.
NOTE: If you want to submit pull requests make sure you install the [Github Extension For Visual Studio](https://visualstudio.github.com/). It is recommended you ignore any local changes you have made to the appsettings.json file before you submit a pull request. To automate this activity, open a command prompt and navigate to the /Oqtane.Server/ folder and enter the command "git update-index --skip-worktree appsettings.json" **Using an official release:**
- A detailed set of instructions for installing Oqtane on IIS is located here: [Installing Oqtane on IIS](https://www.oqtane.org/Resources/Blog/PostId/542/installing-oqtane-on-iis)
**Additional Instructions**
- If you have already installed a previous version of Oqtane and you wish to do a clean database install, simply reset the DefaultConnection value in the Oqtane.Server\appsettings.json file to "". This will trigger a re-install when you run the application which will execute the database installation scripts.
- If you want to submit pull requests make sure you install the [Github Extension For Visual Studio](https://visualstudio.github.com/). It is recommended you ignore any local changes you have made to the appsettings.json file before you submit a pull request. To automate this activity, open a command prompt and navigate to the /Oqtane.Server/ folder and enter the command "git update-index --skip-worktree appsettings.json"
# Roadmap # Roadmap
This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist. This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist.
@ -53,6 +65,8 @@ Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalke
# Release Announcements # Release Announcements
[Oqtane 1.0.1](https://www.oqtane.org/Resources/Blog/PostId/541/oqtane-builds-momentum-with-101-release)
[Oqtane 1.0](https://www.oqtane.org/Resources/Blog/PostId/540/announcing-oqtane-10-a-modular-application-framework-for-blazor) [Oqtane 1.0](https://www.oqtane.org/Resources/Blog/PostId/540/announcing-oqtane-10-a-modular-application-framework-for-blazor)
[Oqtane POC](https://www.oqtane.org/Resources/Blog/PostId/520/announcing-oqtane-a-modular-application-framework-for-blazor) [Oqtane POC](https://www.oqtane.org/Resources/Blog/PostId/520/announcing-oqtane-a-modular-application-framework-for-blazor)
@ -87,7 +101,7 @@ Control panel for adding, editing, and deleting pages as well as adding new modu
![Manage Page](https://github.com/oqtane/framework/blob/master/screenshots/screenshot5.png?raw=true "Manage Page") ![Manage Page](https://github.com/oqtane/framework/blob/master/screenshots/screenshot5.png?raw=true "Manage Page")
Admin dashboard for accessing the variuous administrative features of the framework: Admin dashboard for accessing the various administrative features of the framework:
![Admin Dashboard](https://github.com/oqtane/framework/blob/master/screenshots/screenshot6.png?raw=true "Admin Dashboard") ![Admin Dashboard](https://github.com/oqtane/framework/blob/master/screenshots/screenshot6.png?raw=true "Admin Dashboard")

197
azuredeploy.json Normal file
View File

@ -0,0 +1,197 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"parameters": {
"sqlServerName": {
"type": "string",
"metadata": {
"description": "The name of the sql server. It has to be unique."
}
},
"databaseName": {
"type": "string",
"metadata": {
"description": "The name of the sql databaseName. It has to be unique."
}
},
"sqlAdministratorLogin": {
"type": "string",
"metadata": {
"description": "The admin user of the SQL Server"
}
},
"sqlAdministratorLoginPassword": {
"type": "securestring",
"metadata": {
"description": "The password of the admin user of the SQL Server"
}
},
"BlazorWebsiteName": {
"type": "string",
"metadata": {
"description": "The name of the website. It has to be unique."
}
},
"BlazorSKU": {
"type": "string",
"allowedValues": [
"Free",
"Shared",
"Basic",
"Standard"
],
"defaultValue": "Standard"
},
"BlazorWorkerSize": {
"type": "string",
"allowedValues": [
"0",
"1",
"2"
],
"defaultValue": "0"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"hostingPlanName": "[concat('Oqtane-hostingplan-', uniqueString(resourceGroup().id))]",
"databaseEdition": "Standard",
"databaseCollation": "SQL_Latin1_General_CP1_CI_AS",
"databaseServiceObjectiveName": "Standard"
},
"resources": [
{
"name": "[parameters('sqlServerName')]",
"type": "Microsoft.Sql/servers",
"apiVersion": "2014-04-01",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "SqlServer"
},
"properties": {
"administratorLogin": "[parameters('sqlAdministratorLogin')]",
"administratorLoginPassword": "[parameters('sqlAdministratorLoginPassword')]",
"version": "12.0"
},
"resources": [
{
"name": "[parameters('databaseName')]",
"type": "databases",
"apiVersion": "2015-01-01",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "Database"
},
"properties": {
"edition": "[variables('databaseEdition')]",
"collation": "[variables('databaseCollation')]",
"requestedServiceObjectiveName": "[variables('databaseServiceObjectiveName')]"
},
"dependsOn": [
"[parameters('sqlServerName')]"
],
"resources": [
{
"comments": "Transparent Data Encryption",
"name": "current",
"type": "transparentDataEncryption",
"apiVersion": "2014-04-01-preview",
"properties": {
"status": "Enabled"
},
"dependsOn": [
"[parameters('databaseName')]"
]
}
]
},
{
"name": "AllowAllMicrosoftAzureIps",
"type": "firewallrules",
"apiVersion": "2014-04-01",
"location": "[resourceGroup().location]",
"properties": {
"endIpAddress": "0.0.0.0",
"startIpAddress": "0.0.0.0"
},
"dependsOn": [
"[parameters('sqlServerName')]"
]
}
]
},
{
"name": "[variables('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"apiVersion": "2014-06-01",
"dependsOn": [],
"tags": {
"displayName": "Blazor"
},
"properties": {
"name": "[variables('hostingPlanName')]",
"sku": "[parameters('BlazorSKU')]",
"workerSize": "[parameters('BlazorWorkerSize')]",
"numberOfWorkers": 1
}
},
{
"apiVersion": "2018-02-01",
"name": "[parameters('BlazorWebsiteName')]",
"type": "Microsoft.Web/sites",
"location": "[parameters('location')]",
"dependsOn": [
"[variables('hostingPlanName')]"
],
"tags": {
"[concat('hidden-related:', resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')))]": "empty",
"displayName": "Website"
},
"properties": {
"name": "[parameters('BlazorWebsiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
"siteConfig": {
"webSocketsEnabled": true
}
},
"resources": [
{
"type": "sourcecontrols",
"apiVersion": "2018-02-01",
"name": "web",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]",
"[resourceId('Microsoft.Web/Sites/config', parameters('BlazorWebsiteName'), 'connectionstrings')]"
],
"properties": {
"RepoUrl": "https://github.com/oqtane/oqtane.framework.git",
"branch": "master",
"IsManualIntegration": true
}
},
{
"apiVersion": "2018-02-01",
"type": "config",
"name": "connectionstrings",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('BlazorWebsiteName'))]"
],
"properties": {
"DefaultConnection": {
"value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', parameters('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', parameters('databaseName'), ';User Id=', parameters('sqlAdministratorLogin'), '@', reference(concat('Microsoft.Sql/servers/', parameters('sqlserverName'))).fullyQualifiedDomainName, ';Password=', parameters('sqlAdministratorLoginPassword'), ';')]",
"type": "SQLAzure"
}
}
}
]
}
]
}