Merge pull request #19 from oqtane/master

sync master
This commit is contained in:
Jim Spillane
2020-08-18 09:14:20 -04:00
committed by GitHub
174 changed files with 1900 additions and 998 deletions

2
.deployment Normal file
View File

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

View File

@ -0,0 +1,111 @@
@namespace Oqtane.Modules.Admin.Files
@inherits ModuleBase
@inject IFileService FileService
@inject IFolderService FolderService
@inject NavigationManager NavigationManager
@if (_folders != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label for="name" HelpText="The name of the file">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="parent" HelpText="The folder where the file is located">Folder: </Label>
</td>
<td>
<select id="parent" class="form-control" @bind="@_folderId">
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label for="size" HelpText="The size of the file (in bytes)">Size: </Label>
</td>
<td>
<input id="size" class="form-control" @bind="@_size" readonly />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveFile">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
}
@code {
private int _fileId = -1;
private string _name;
private List<Folder> _folders;
private int _folderId = -1;
private int _size;
private string _createdBy;
private DateTime _createdOn;
private string _modifiedBy;
private DateTime _modifiedOn;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
public override string Title => "File Management";
protected override async Task OnInitializedAsync()
{
try
{
_folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId);
_fileId = Int32.Parse(PageState.QueryString["id"]);
File file = await FileService.GetFileAsync(_fileId);
if (file != null)
{
_name = file.Name;
_folderId = file.FolderId;
_size = file.Size;
_createdBy = file.CreatedBy;
_createdOn = file.CreatedOn;
_modifiedBy = file.ModifiedBy;
_modifiedOn = file.ModifiedOn;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading File {FileId} {Error}", _fileId, ex.Message);
AddModuleMessage("Error Loading File", MessageType.Error);
}
}
private async Task SaveFile()
{
try
{
if (_name.IsPathOrFileValid())
{
File file = await FileService.GetFileAsync(_fileId);
file.Name = _name;
file.FolderId = _folderId;
file = await FileService.UpdateFileAsync(file);
await logger.LogInformation("File Saved {File}", file);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("File Name Not Valid", MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving File {FileId} {Error}", _fileId, ex.Message);
AddModuleMessage("Error Saving File", MessageType.Error);
}
}
}

View File

@ -174,7 +174,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message);
AddModuleMessage("Error Saving Module", MessageType.Error);
AddModuleMessage("Error Saving Folder", MessageType.Error);
}
}
@ -182,9 +182,25 @@
{
try
{
await FolderService.DeleteFolderAsync(_folderId);
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
AddModuleMessage("Folder Deleted", MessageType.Success);
bool isparent = false;
foreach (Folder folder in _folders)
{
if (folder.ParentId == _folderId)
{
isparent = true;
break;
}
}
if (!isparent)
{
await FolderService.DeleteFolderAsync(_folderId);
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("Folder Has Child Folders And Cannot Be Deleted", MessageType.Warning);
}
}
catch (Exception ex)
{

View File

@ -28,13 +28,15 @@
</table>
<Pager Items="@_files">
<Header>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Modified</th>
<th>Type</th>
<th>Size</th>
</Header>
<Row>
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" /></td>
<td><ActionDialog Header="Delete File" Message="@("Are You Sure You Wish To Delete " + context.Name + "?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" /></td>
<td><a href="@(ContentUrl(context.FileId))" target="_new">@context.Name</a></td>
<td>@context.ModifiedOn</td>

View File

@ -63,10 +63,18 @@
</tr>
<tr>
<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>
<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>
</tr>
</table>
@ -83,6 +91,7 @@
private string _startDate = string.Empty;
private string _endDate = string.Empty;
private string _retentionHistory = string.Empty;
private string _nextExecution = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -102,6 +111,7 @@
_startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty;
_endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty;
_retentionHistory = job.RetentionHistory.ToString();
_nextExecution = job.NextExecution.ToString();
}
}
catch (Exception ex)
@ -140,6 +150,15 @@
job.EndDate = DateTime.Parse(_endDate);
}
if (_nextExecution == string.Empty)
{
job.NextExecution = null;
}
else
{
job.NextExecution = DateTime.Parse(_nextExecution);
}
job.RetentionHistory = int.Parse(_retentionHistory);
try

View File

@ -15,14 +15,14 @@ else
<Pager Items="@_jobs">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Status</th>
<th>Frequency</th>
<th>Next Execution</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" /></td>

View File

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

View File

@ -49,7 +49,7 @@ else
{
<Pager Items="@_logs">
<Header>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Date</th>
<th>Level</th>
<th>Feature</th>

View File

@ -16,7 +16,7 @@
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should be in singular form (ie. Car) and not contain spaces or punctuation.">Module Name: </Label>
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation.">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />

View File

@ -16,7 +16,7 @@
<Header>
<th>Name</th>
<th>Version</th>
<th></th>
<th style="width: 1px"></th>
</Header>
<Row>
<td>@context.Name</td>
@ -79,7 +79,7 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}
catch (Exception ex)

View File

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

View File

@ -14,11 +14,11 @@ else
<Pager Items="@_moduleDefinitions">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Version</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" /></td>
@ -86,7 +86,7 @@ else
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}
catch (Exception ex)
@ -102,7 +102,7 @@ else
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
}
catch (Exception ex)

View File

@ -31,9 +31,15 @@
{
try
{
await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
if (success)
{
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)
{

View File

@ -162,17 +162,6 @@
<input id="Icon" class="form-control" @bind="@_icon" />
</td>
</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>
<td>
<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 _url;
private string _ispersonalizable = "False";
private string _mode = "view";
private string _themetype = "-";
private string _layouttype = "-";
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;
switch (_insert)
{
@ -367,7 +361,6 @@
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit" ? true : false);
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
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" />
</td>
</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>
<td>
<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 _url;
private string _ispersonalizable;
private string _mode;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
@ -290,7 +278,6 @@
_isnavigation = page.IsNavigation.ToString();
_url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString();
_mode = (page.EditMode) ? "edit" : "view";
_themetype = page.ThemeType;
if (_themetype == PageState.Site.DefaultThemeType)
{
@ -333,7 +320,7 @@
_children = new List<Page>();
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))
{
@ -433,6 +420,13 @@
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 != "=")
{
Page child;
@ -456,7 +450,6 @@
}
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit");
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
@ -512,4 +505,9 @@
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

@ -9,8 +9,8 @@
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -145,6 +145,7 @@
profile = new Profile();
}
profile.SiteId = PageState.Site.SiteId;
profile.Name = _name;
profile.Title = _title;
profile.Description = _description;

View File

@ -12,8 +12,8 @@ else
<Pager Items="@_profiles">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -16,8 +16,8 @@
{
<Pager Items="@_pages">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Deleted By</th>
<th>Deleted On</th>
@ -42,8 +42,8 @@
{
<Pager Items="@_modules">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Page</th>
<th>Module</th>
<th>Deleted By</th>

View File

@ -12,9 +12,9 @@ else
<Pager Items="@_roles">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

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

View File

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

View File

@ -14,8 +14,8 @@ else
<Pager Items="@_sites">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -11,8 +11,8 @@ else
{
<Pager Items="@tenants">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -16,7 +16,7 @@
<Header>
<th>Name</th>
<th>Version</th>
<th></th>
<th style="width: 1px;"></th>
</Header>
<Row>
<td>@context.Name</td>
@ -79,7 +79,7 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync();
}
catch (Exception ex)

View File

@ -15,10 +15,10 @@ else
<Pager Items="@_themes">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Name</th>
<th>Version</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th scope="col">Name</th>
<th scope="col">Version</th>
<th>&nbsp;</th>
</Header>
<Row>
@ -37,6 +37,7 @@ else
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
}
</td>
<td></td>
</Row>
</Pager>
}
@ -86,7 +87,7 @@ else
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync();
}
catch (Exception ex)
@ -102,7 +103,7 @@ else
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
}
catch (Exception ex)

View File

@ -12,7 +12,7 @@
@if (_upgradeavailable)
{
<ModuleMessage Type="MessageType.Info" Message="Select The Upgrade Button To Install a New Framework Version"></ModuleMessage>
@("Framework") @_package.Version <button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Upgrade</button>
<button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>Upgrade To @_package.Version</button>
}
else
{
@ -26,7 +26,7 @@
<Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label>
</td>
<td>
<FileManager Filter="nupkg" Folder="Framework" />
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" />
</td>
</tr>
</table>
@ -71,7 +71,7 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)
@ -88,7 +88,7 @@
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)

View File

@ -66,6 +66,7 @@
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl());

View File

@ -32,7 +32,7 @@ else
<label for="Name" class="control-label">Password: </label>
</td>
<td>
<input type="password" class="form-control" @bind="@password" />
<input type="password" class="form-control" @bind="@password" autocomplete="new-password" />
</td>
</tr>
<tr>
@ -40,7 +40,7 @@ else
<label for="Name" class="control-label">Confirm Password: </label>
</td>
<td>
<input type="password" class="form-control" @bind="@confirm" />
<input type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
</td>
</tr>
<tr>
@ -75,10 +75,12 @@ else
<TabPanel Name="Profile">
@if (profiles != null && settings != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole))
{
var p = profile;
if (p.Category != category)
{
<tr>
@ -90,14 +92,22 @@ else
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
}
</table>
}
</table>
<button type="button" class="btn btn-primary" @onclick="Save">Save</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
}
@ -111,8 +121,8 @@ else
{
<Pager Items="@notifications">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>From</th>
<th>Subject</th>
<th>Received</th>
@ -241,7 +251,7 @@ else
{
try
{
if (username != string.Empty && email != string.Empty)
if (username != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -261,6 +271,7 @@ else
await UserService.UpdateUserAsync(user);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
await logger.LogInformation("User Profile Saved");
AddModuleMessage("User Profile Updated Successfully", MessageType.Success);
}
else
{
@ -269,7 +280,7 @@ else
}
else
{
AddModuleMessage("You Must Provide A Username and Email Address", MessageType.Warning);
AddModuleMessage("You Must Provide A Username and Email Address As Well As All Required Profile Information", MessageType.Warning);
}
}
catch (Exception ex)
@ -279,6 +290,26 @@ else
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole))
{
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
}
return valid;
}
private void Cancel()
{
NavigationManager.NavigateTo(NavigateUrl(string.Empty));

View File

@ -33,7 +33,7 @@
<td>
<input class="form-control" @bind="@subject" readonly />
</td>
}
}
@if (title == "To")
{
<td>
@ -51,7 +51,7 @@
<input class="form-control" @bind="@createdon" readonly />
</td>
</tr>
}
}
@if (title == "From")
{
<tr>
@ -62,7 +62,7 @@
<textarea class="form-control" @bind="@body" rows="5" readonly />
</td>
</tr>
}
}
@if (title == "To")
{
<tr>
@ -73,7 +73,7 @@
<textarea class="form-control" @bind="@body" rows="5" />
</td>
</tr>
}
}
</table>
@ -192,6 +192,7 @@
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl());

View File

@ -71,10 +71,17 @@
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
}
@ -112,11 +119,14 @@
}
}
private string GetProfileValue(string SettingName, string DefaultValue)
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
private async Task SaveUser()
{
try
{
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty)
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -149,7 +159,7 @@
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage("You Must Provide A Username, Password, Email Address And All Required Profile Information", MessageType.Warning);
}
}
catch (Exception ex)
@ -159,6 +169,23 @@
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
return valid;
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;

View File

@ -98,10 +98,17 @@ else
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
}
@ -180,7 +187,7 @@ else
{
try
{
if (username != string.Empty && email != string.Empty)
if (username != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -213,7 +220,7 @@ else
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage("You Must Provide A Username, Password, Email Address, And All Required Profile Information", MessageType.Warning);
}
}
catch (Exception ex)
@ -223,6 +230,23 @@ else
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
return valid;
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;

View File

@ -20,9 +20,9 @@ else
<Pager Items="@userroles">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@if (_visible)
{
<div class="app-admin-modal">
@ -40,7 +40,7 @@
@code {
private bool _visible = false;
private bool _editmode = true;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -66,7 +66,7 @@
public bool Disabled { get; set; } // optional
[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]
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";
}
if (!string.IsNullOrEmpty(EditMode))
{
_editmode = bool.Parse(EditMode);

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IUserService UserService
@if (_authorized)
@ -21,7 +20,7 @@
private string _parameters = string.Empty;
private string _classname = "btn btn-primary";
private string _style = string.Empty;
private bool _editmode = true;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -47,7 +46,7 @@
public bool Disabled { get; set; } // optional
[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]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
@ -90,7 +89,7 @@
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
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@if (_text != string.Empty)
{

View File

@ -1,7 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IFolderService FolderService
@inject IFileService FileService
@ -56,16 +54,16 @@
<div>
@if (UploadMultiple)
{
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/>
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
}
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 class="float-right">
<button type="button" class="btn btn-success" @onclick="UploadFile">Upload</button>
@if (_showfiles && GetFileId() != -1)
@if (ShowFiles && GetFileId() != -1)
{
<button type="button" class="btn btn-danger" @onclick="DeleteFile">Delete</button>
}
@ -88,7 +86,6 @@
private string _id;
private List<Folder> _folders;
private List<File> _files = new List<File>();
private bool _showfiles = true;
private string _fileinputid = string.Empty;
private string _progressinfoid = string.Empty;
private string _progressbarid = string.Empty;
@ -134,7 +131,7 @@
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;
}
else
@ -163,7 +160,7 @@
await GetFiles();
// create unique id for component
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = _guid + "FileInput";
_progressinfoid = _guid + "ProgressInfo";
@ -211,7 +208,7 @@
_message = string.Empty;
try
{
FolderId = int.Parse((string) e.Value);
FolderId = int.Parse((string)e.Value);
await GetFiles();
FileId = -1;
_image = string.Empty;
@ -227,7 +224,7 @@
private async Task FileChanged(ChangeEventArgs e)
{
_message = string.Empty;
FileId = int.Parse((string) e.Value);
FileId = int.Parse((string)e.Value);
await SetImage();
StateHasChanged();
@ -244,8 +241,8 @@
var maxwidth = 200;
var maxheight = 200;
var ratioX = (double) maxwidth / (double) file.ImageWidth;
var ratioY = (double) maxheight / (double) file.ImageHeight;
var ratioX = (double)maxwidth / (double)file.ImageWidth;
var ratioY = (double)maxheight / (double)file.ImageHeight;
var ratio = ratioX < ratioY ? ratioX : ratioY;
_image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +

View File

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

View File

@ -1,10 +1,16 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject NavigationManager NavigationManager
@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 />
}

View File

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

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@ -10,10 +9,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<tbody>
<tr>
<th>Role</th>
<th scope="col">Role</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
}
</tr>
@foreach (Role role in _roles)
@ -36,10 +35,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<thead>
<tr>
<th>User</th>
<th scope="col">User</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
}
</tr>
</thead>
@ -52,7 +51,7 @@
@foreach (PermissionString permission in _permissions)
{
var p = permission;
<td style="text-align: center;">
<td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
</td>
}
@ -64,9 +63,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<tbody>
<tr>
<td style="text-align: right;"><label for="Username" class="control-label">User: </label></td>
<td><input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" /></td>
<td style="text-align: left;"><button type="button" class="btn btn-primary" @onclick="AddUser">Add</button></td>
<td class="input-group">
<input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" />
<button type="button" class="btn btn-primary" @onclick="AddUser">Add</button>
</td>
</tr>
</tbody>
</table>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,23 +21,23 @@ namespace Oqtane.Modules.HtmlText.Services
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();
}
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)
{
await PutJsonAsync($"{ApiUrl}/{htmlText.HtmlTextId}?entityid={htmlText.ModuleId}", htmlText);
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", htmlText.ModuleId), htmlText);
}
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);
}
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
public void AddModuleMessage(string message, MessageType type)
{
@ -154,12 +202,15 @@ namespace Oqtane.Modules
case "add":
logFunction = LogFunction.Create;
break;
case "edit":
logFunction = LogFunction.Update;
break;
case "delete":
logFunction = LogFunction.Delete;
break;
default:
logFunction = LogFunction.Read;
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>
<RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations>
<Version>1.0.0</Version>
<Version>1.0.3</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -15,7 +15,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>Not for production use.</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
</PropertyGroup>

View File

@ -170,6 +170,19 @@ namespace Oqtane.Services
// can be used to override the default alias
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)]
public string CreateApiUrl(Alias alias, string absoluteUri, string serviceName)
{

View File

@ -1,28 +1,26 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Reflection;
using System;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services
{
public class ThemeService : ServiceBase, IThemeService
{
private readonly HttpClient _http;
private readonly SiteState _siteState;
public ThemeService(HttpClient http) : base(http)
public ThemeService(HttpClient http, SiteState siteState) : base(http)
{
_http = http;
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("Theme");
private string ApiUrl => CreateApiUrl(_siteState.Alias, "Theme");
public async Task<List<Theme>> GetThemesAsync()
{
List<Theme> themes = await GetJsonAsync<List<Theme>>(Apiurl);
List<Theme> themes = await GetJsonAsync<List<Theme>>(ApiUrl);
return themes.OrderBy(item => item.Name).ToList();
}
@ -45,12 +43,12 @@ namespace Oqtane.Services
public async Task InstallThemesAsync()
{
await GetJsonAsync<List<string>>($"{Apiurl}/install");
await GetJsonAsync<List<string>>($"{ApiUrl}/install");
}
public async Task DeleteThemeAsync(string themeName)
{
await DeleteAsync($"{Apiurl}/{themeName}");
await DeleteAsync($"{ApiUrl}/{themeName}");
}
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.BlazorTheme
@inherits ThemeBase
@implements IThemeControl
<div class="breadcrumbs">
<Breadcrumbs />
@ -31,11 +30,11 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css", Integrity = "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css", Integrity = "sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.3.1.slim.min.js", Integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", Integrity = "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js", Integrity = "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM", CrossOrigin = "anonymous" }
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -7,5 +7,6 @@ namespace Oqtane.Themes
{
[CascadingParameter]
protected Module ModuleState { get; set; }
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@if (BreadCrumbPages.Any())
{

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IModuleDefinitionService ModuleDefinitionService
@ -128,7 +127,10 @@
{
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>
@ -197,26 +199,17 @@
@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>
</button>
}
else
{
if (PageState.EditMode)
{
<button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
else
{
<button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
<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

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits LoginBase
@attribute [OqtaneIgnore]
<span class="app-login">
<AuthorizeView>

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@if (PageState.Site.LogoFileId != null)
{

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Themes.Controls
@inherits MenuBase
@attribute [OqtaneIgnore]
@if (MenuPages.Any())
{
<span class="app-menu-toggler">

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Themes.Controls
@inherits MenuBase
@attribute [OqtaneIgnore]
@if (MenuPages.Any())
{
<span class="app-menu-toggler">

View File

@ -2,7 +2,7 @@
@inherits ModuleActionsBase
@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">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
@ -15,7 +15,18 @@
}
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>

View File

@ -19,7 +19,7 @@ namespace Oqtane.Themes.Controls
[Inject] public IPageModuleService PageModuleService { get; set; }
[Inject] public IModuleService ModuleService { get; set; }
protected List<ActionViewModel> Actions;
public List<ActionViewModel> Actions;
protected override void OnParametersSet()
{
@ -31,51 +31,52 @@ namespace Oqtane.Themes.Controls
var actionList = new List<ActionViewModel>();
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
{
actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
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))
{
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
{
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 != "")
{
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 {Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
actionList.Add(new ActionViewModel {Icon=Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
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 = ""});
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)
{
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))
{
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))
{
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)
{
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 string Icon { get; set; }
public string Name { set; get; }
public Func<string, PageModule, Task<string>> Action { set; get; }
}
}

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager
<span class="app-profile">

View File

@ -2,7 +2,6 @@
{
public interface IContainerControl
{
string Name { get; } // friendly name for a container
string Thumbnail { get; } // screen shot of a container - assumed to be in the ThemePath() folder
}
}

View File

@ -2,9 +2,6 @@
{
public interface ILayoutControl
{
string Name { get; } // friendly name for a layout
string Thumbnail { get; } // screen shot of a layout - assumed to be in the ThemePath() folder
string Panes { get; } // identifies all panes in a theme ( delimited by "," or ";" )
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ThemeBase
@implements IThemeControl
<main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
@ -24,10 +23,10 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.5.0/cyborg/bootstrap.min.css", Integrity = "sha384-GKugkVcT8wqoh3M8z1lqHbU+g6j498/ZT/zuXbepz7Dc09/otQZxTimkEMTkRWHP", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.3.1.slim.min.js", Integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", Integrity = "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js", Integrity = "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM", CrossOrigin = "anonymous" }
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace Oqtane.Themes
{
public abstract class ThemeBase : ComponentBase
public abstract class ThemeBase : ComponentBase, IThemeControl
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }

View File

@ -1,5 +1,8 @@
namespace Oqtane.Themes
using Oqtane.Shared;
namespace Oqtane.Themes
{
[OqtaneIgnore]
public abstract class ThemeControlBase : ThemeBase
{

View File

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

View File

@ -14,6 +14,7 @@ namespace Oqtane.UI
public List<Module> Modules { get; set; }
public Uri Uri { get; set; }
public Dictionary<string, string> QueryString { get; set; }
public string UrlParameters { get; set; }
public int ModuleId { get; set; }
public string Action { 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 IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService
@ -25,7 +26,7 @@
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";
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
@ -38,12 +39,12 @@
DynamicComponent = builder =>
{
if (PageState.ModuleId != -1 && PageState.Action != "")
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
{
if (Name.ToLower() == Constants.AdminPane.ToLower())
{
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null)
if (module != null && !module.IsDeleted)
{
var typename = module.ModuleType;
// check for core module actions component
@ -88,10 +89,7 @@
{
module.Title = module.ControlTitle;
}
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
else
@ -106,14 +104,12 @@
if (PageState.ModuleId != -1)
{
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
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
}
@ -124,14 +120,19 @@
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
}
}
};
}
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;
List<Module> modules;
var moduleid = -1;
var action = "";
var action = string.Empty;
var urlparameters = string.Empty;
var editmode = false;
var reload = Reload.None;
var lastsyncdate = DateTime.UtcNow;
@ -179,21 +180,60 @@
// extract admin route elements from path
var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
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;
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))
if (segments[i] == Constants.UrlParametersDelimiter)
{
moduleid = result;
path = path.Replace(moduleid.ToString() + "/", "");
urlParametersPos = i + 1;
}
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
@ -220,17 +260,14 @@
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = Reload.Page;
if (page != null)
{
editmode = page.EditMode;
}
editmode = false;
}
if (page != null)
{
if (PageState == null)
{
editmode = page.EditMode;
editmode = false;
}
// check if user is authorized to view page
@ -263,6 +300,7 @@
Modules = modules,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring,
UrlParameters = urlparameters,
ModuleId = moduleid,
Action = action,
EditMode = editmode,
@ -360,6 +398,13 @@
string panes = "";
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)
{
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
@ -375,7 +420,7 @@
Type layouttype = Type.GetType(page.LayoutType);
if (layouttype != null)
{
var layoutobject = Activator.CreateInstance(layouttype) as ILayoutControl;
var layoutobject = Activator.CreateInstance(layouttype) as IThemeControl;
if (layoutobject != null)
{
panes = layoutobject.Panes;
@ -401,7 +446,7 @@
if (module.PageId == page.PageId || module.ModuleId == moduleid)
{
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;
}

View File

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

View File

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

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Framework</id>
<version>1.0.0</version>
<version>1.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,16 +13,11 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane framework</tags>
<releaseNotes>Initial Release</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\wwwroot\**\*.*" target="wwwroot" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\*.*" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\wwwroot\**\*.*" target="wwwroot" />
</files>
</package>

View File

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

View File

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

View File

@ -0,0 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.1.0.3.Install.zip" -Force

Binary file not shown.

View File

@ -1,5 +1,18 @@
DEL "*.nupkg"
del "*.nupkg"
dotnet clean -c Release ..\Oqtane.sln
dotnet build -c Release ..\Oqtane.sln
dotnet pack -o .\ -c Release ..\Oqtane.sln
nuget.exe pack Oqtane.Client.nuspec
nuget.exe pack Oqtane.Server.nuspec
nuget.exe pack Oqtane.Shared.nuspec
del /F/Q/S "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish" > NUL
rmdir /Q/S "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish"
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
del "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\appsettings.json"
ren "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\appsettings.release.json" "appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
del "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
del "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\Oqtane.Upgrade.*"
nuget.exe pack Oqtane.Framework.nuspec

View File

@ -0,0 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.1.0.3.Upgrade.zip" -Force

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -135,8 +135,20 @@ namespace Oqtane.Controllers
[Authorize(Roles = Constants.RegisteredRole)]
public Models.File Put(int id, [FromBody] Models.File file)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
{
file.Folder = _folders.GetFolder(file.FolderId);
Models.File _file = _files.GetFile(id, false);
if (_file.Name != file.Name || _file.FolderId != file.FolderId)
{
string folderpath = GetFolderPath(file.Folder);
if (!Directory.Exists(folderpath))
{
Directory.CreateDirectory(folderpath);
}
System.IO.File.Move(Path.Combine(GetFolderPath(_file.Folder), _file.Name), Path.Combine(folderpath, file.Name));
}
file.Extension = Path.GetExtension(file.Name).ToLower().Replace(".", "");
file = _files.UpdateFile(file);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
}
@ -483,7 +495,7 @@ namespace Oqtane.Controllers
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
foreach (string folder in folders)
{
path = Utilities.PathCombine(path, folder, "\\");
path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -10,20 +11,25 @@ using Oqtane.Extensions;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using Microsoft.AspNetCore.Hosting;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
public class FolderController : Controller
{
private readonly IWebHostEnvironment _environment;
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ILogManager _logger;
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger)
public FolderController(IWebHostEnvironment environment, IFolderRepository folders, IUserPermissions userPermissions, ITenantResolver tenants, ILogManager logger)
{
_environment = environment;
_folders = folders;
_userPermissions = userPermissions;
_tenants = tenants;
_logger = logger;
}
@ -112,7 +118,7 @@ namespace Oqtane.Controllers
Folder parent = _folders.GetFolder(folder.ParentId.Value);
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);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
}
@ -142,12 +148,19 @@ namespace Oqtane.Controllers
{
if (folder.IsPathValid())
{
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
if (folder.ParentId != null)
{
Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
}
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
Models.Folder _folder = _folders.GetFolder(id, false);
if (_folder.Path != folder.Path && Directory.Exists(GetFolderPath(_folder)))
{
Directory.Move(GetFolderPath(_folder), GetFolderPath(folder));
}
folder = _folders.UpdateFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
}
@ -210,5 +223,10 @@ namespace Oqtane.Controllers
HttpContext.Response.StatusCode = 401;
}
}
private string GetFolderPath(Folder folder)
{
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
}
}
}

View File

@ -122,7 +122,7 @@ namespace Oqtane.Controllers
var pages = _pages.GetPages(module.SiteId).ToList();
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 });
}

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

@ -94,8 +94,8 @@ namespace Oqtane.Controllers
[Authorize(Roles = Constants.HostRole)]
public void InstallModules()
{
_installationManager.InstallPackages("Modules", true);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Modules Installed");
_installationManager.InstallPackages("Modules", true);
}
// DELETE api/<controller>/5?siteid=x
@ -170,19 +170,19 @@ namespace Oqtane.Controllers
{
string rootPath;
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")
{
rootPath = Utilities.PathCombine(rootFolder.FullName,"\\");
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
rootPath = Utilities.PathCombine(rootFolder.FullName,Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
}
else
{
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\");
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";
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name,Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
@ -196,8 +196,8 @@ namespace Oqtane.Controllers
{
// add embedded resources to project
List<string> resources = new List<string>();
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + ".1.0.0.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + ".Uninstall.sql"));
EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources);
}

View File

@ -125,7 +125,7 @@ namespace Oqtane.Controllers
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
_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();
foreach (Module module in modules)
@ -163,7 +163,6 @@ namespace Oqtane.Controllers
page.Order = 0;
page.IsNavigation = false;
page.Url = "";
page.EditMode = false;
page.ThemeType = parent.ThemeType;
page.LayoutType = parent.LayoutType;
page.DefaultContainerType = parent.DefaultContainerType;

View File

@ -43,8 +43,8 @@ namespace Oqtane.Controllers
[Authorize(Roles = Constants.HostRole)]
public void InstallThemes()
{
_installationManager.InstallPackages("Themes", true);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Themes Installed");
_installationManager.InstallPackages("Themes", true);
}
// DELETE api/<controller>/xxx

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Security.Claims;
using Oqtane.Shared;
using System;
using System.IO;
using System.Net;
using Oqtane.Enums;
using Oqtane.Infrastructure;
@ -148,7 +149,7 @@ namespace Oqtane.Controllers
notification.SiteId = user.SiteId;
notification.FromUserId = null;
notification.ToUserId = newUser.UserId;
notification.ToEmail = "";
notification.ToEmail = newUser.Email;
notification.Subject = "User Account Verification";
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
@ -157,6 +158,7 @@ namespace Oqtane.Controllers
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
_notifications.AddNotification(notification);
}
@ -173,7 +175,7 @@ namespace Oqtane.Controllers
}
// 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)
{
_folders.AddFolder(new Folder
@ -181,7 +183,7 @@ namespace Oqtane.Controllers
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"),
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),Path.DirectorySeparatorChar.ToString()),
Order = 1,
IsSystem = true,
Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" +
@ -385,6 +387,7 @@ namespace Oqtane.Controllers
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
_notifications.AddNotification(notification);
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
}

View File

@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.Hosting;
using Oqtane.Extensions;
using Oqtane.Infrastructure;
using Oqtane.Modules;
using Oqtane.Services;
using Oqtane.Shared;
using Oqtane.UI;
// ReSharper disable once CheckNamespace
@ -64,22 +61,23 @@ namespace Microsoft.Extensions.DependencyInjection
if (runtime == Runtime.Server)
{
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(services));
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(services));
}
}
return services;
}
private static void LoadAssemblies()
{
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
if (assemblyPath == null) return;
var assembliesFolder = new DirectoryInfo(assemblyPath);
AssemblyLoadContext.Default.Resolving += ResolveDependencies;
var assembliesFolder = new DirectoryInfo(assemblyPath);
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process )
foreach (var dll in assembliesFolder.EnumerateFiles($"*.dll", SearchOption.TopDirectoryOnly).Where(f => f.IsOqtaneAssembly()))
@ -95,7 +93,6 @@ namespace Microsoft.Extensions.DependencyInjection
continue;
}
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
if (!assemblies.Any(a => AssemblyName.ReferenceMatchesDefinition(assemblyName, a.GetName())))
{
try
@ -121,5 +118,19 @@ namespace Microsoft.Extensions.DependencyInjection
}
}
}
private static Assembly ResolveDependencies(AssemblyLoadContext context, AssemblyName name)
{
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) + "\\" + name.Name + ".dll";
if (File.Exists(assemblyPath))
{
return context.LoadFromStream(new MemoryStream(File.ReadAllBytes(assemblyPath)));
}
else
{
return null;
}
}
}
}

View File

@ -342,46 +342,48 @@ namespace Oqtane.Infrastructure
if (!string.IsNullOrEmpty(moduledefinition.ReleaseVersions) && !string.IsNullOrEmpty(moduledefinition.ServerManagerType))
{
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
if (moduletype != null)
{
foreach (var tenant in db.Tenant.ToList())
string[] versions = moduledefinition.ReleaseVersions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
using (var db = new InstallationContext(NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
{
int index = Array.FindIndex(versions, item => item == moduledefinition.Version);
if (tenant.Name == install.TenantName && install.TenantName != Constants.MasterTenant)
foreach (var tenant in db.Tenant.ToList())
{
index = -1;
}
if (index != (versions.Length - 1))
{
if (index == -1) index = 0;
for (int i = index; i < versions.Length; i++)
int index = Array.FindIndex(versions, item => item == moduledefinition.Version);
if (tenant.Name == install.TenantName && install.TenantName != Constants.MasterTenant)
{
try
index = -1;
}
if (index != (versions.Length - 1))
{
if (index == -1) index = 0;
for (int i = index; i < versions.Length; i++)
{
if (moduletype.GetInterface("IInstallable") != null)
try
{
var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype);
if (moduletype.GetInterface("IInstallable") != null)
{
var moduleobject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduletype);
((IInstallable)moduleobject).Install(tenant, versions[i]);
}
else
{
sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "." + versions[i] + ".sql");
}
}
else
catch (Exception ex)
{
sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "." + versions[i] + ".sql");
result.Message = "An Error Occurred Installing " + moduledefinition.Name + " Version " + versions[i] + " - " + ex.Message.ToString();
}
}
catch (Exception ex)
{
result.Message = "An Error Occurred Installing " + moduledefinition.Name + " Version " + versions[i] + " - " + ex.Message.ToString();
}
}
}
}
if (string.IsNullOrEmpty(result.Message) && moduledefinition.Version != versions[versions.Length - 1])
{
moduledefinition.Version = versions[versions.Length - 1];
db.Entry(moduledefinition).State = EntityState.Modified;
db.SaveChanges();
if (string.IsNullOrEmpty(result.Message) && moduledefinition.Version != versions[versions.Length - 1])
{
moduledefinition.Version = versions[versions.Length - 1];
db.Entry(moduledefinition).State = EntityState.Modified;
db.SaveChanges();
}
}
}
}
@ -461,7 +463,7 @@ namespace Oqtane.Infrastructure
userroles.AddUserRole(userRole);
// 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)
{
folders.AddFolder(new Folder
@ -469,7 +471,7 @@ namespace Oqtane.Infrastructure
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), "\\"),
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
Order = 1,
IsSystem = true,
Permissions = new List<Permission>

View File

@ -1,13 +1,13 @@
using System.Reflection;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
using System.Reflection;
using System.Xml;
using Oqtane.Shared;
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
@ -88,7 +88,7 @@ namespace Oqtane.Infrastructure
// deploy to appropriate locations
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);
switch (foldername)
@ -98,7 +98,12 @@ namespace Oqtane.Infrastructure
ExtractFile(entry, filename);
break;
case "wwwroot":
filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("wwwroot/", "").Split('/')));
filename = Path.Combine(webRootPath.Replace(Path.DirectorySeparatorChar + "wwwroot", ""), Utilities.PathCombine(entry.FullName.Split('/')));
ExtractFile(entry, filename);
break;
case "runtimes":
var destSubFolder = Path.GetDirectoryName(entry.FullName);
filename = Path.Combine(binFolder, destSubFolder, filename);
ExtractFile(entry, filename);
break;
}
@ -121,7 +126,15 @@ namespace Oqtane.Infrastructure
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
}
entry.ExtractToFile(filename, true);
try
{
entry.ExtractToFile(filename, true);
}
catch
{
// an error occurred extracting the file
}
}
public void UpgradeFramework()
@ -131,7 +144,7 @@ namespace Oqtane.Infrastructure
{
// get package with highest version and clean up any others
string packagename = "";
foreach(string package in Directory.GetFiles(folder, "Oqtane.Framework.*.nupkg"))
foreach (string package in Directory.GetFiles(folder, "Oqtane.Framework.*.nupkg"))
{
if (packagename != "")
{
@ -163,12 +176,13 @@ namespace Oqtane.Infrastructure
packageversion = node.InnerText;
}
reader.Close();
break;
}
}
}
// ensure package version is higher than current framework version
if (packageversion != "" && Version.Parse(Constants.Version).CompareTo(Version.Parse(packageversion)) < 0)
// ensure package version is greater than or equal to current framework version
if (packageversion != "" && Version.Parse(Constants.Version).CompareTo(Version.Parse(packageversion)) <= 0)
{
FinishUpgrade();
}
@ -179,28 +193,26 @@ namespace Oqtane.Infrastructure
private void FinishUpgrade()
{
// check if upgrade application exists
string Upgrader = "Oqtane.Upgrade.dll";
string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
if (folder == null || !File.Exists(Path.Combine(folder, "Oqtane.Upgrade.exe"))) return;
if (folder == null || !File.Exists(Path.Combine(folder, Upgrader))) return;
// run upgrade application
var process = new Process
using (var process = new Process())
{
StartInfo =
process.StartInfo = new ProcessStartInfo
{
FileName = Path.Combine(folder, "Oqtane.Upgrade.exe"),
Arguments = "\"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
ErrorDialog = false,
WorkingDirectory = folder,
FileName = "dotnet",
Arguments = Path.Combine(folder, Upgrader) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
UseShellExecute = false,
ErrorDialog = false,
CreateNoWindow = true,
RedirectStandardOutput = false,
RedirectStandardError = false
}
};
process.Start();
};
process.Start();
process.Dispose();
// stop application so upgrade application can proceed
RestartApplication();
}
public void RestartApplication()

View File

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

View File

@ -20,11 +20,15 @@ namespace Oqtane.Infrastructure
{
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>();
List<Alias> aliases = aliasRepository.GetAliases().ToList();
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
var siteState = provider.GetRequiredService<SiteState>();
siteState.Alias = alias;
@ -34,11 +38,11 @@ namespace Oqtane.Infrastructure
var settingRepository = provider.GetRequiredService<ISettingRepository>();
var notificationRepository = provider.GetRequiredService<INotificationRepository>();
// iterate through sites
// iterate through sites for this tenant
List<Site> sites = siteRepository.GetSites().ToList();
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
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
@ -101,14 +105,14 @@ namespace Oqtane.Infrastructure
catch (Exception ex)
{
// error
log += ex.Message + "\n\n";
log += ex.Message + "<br />";
}
}
log += "Notifications Delivered: " + sent + "\n\n";
log += "Notifications Delivered: " + sent + "<br />";
}
else
{
log += "SMTP Not Configured" + "\n\n";
log += "SMTP Not Configured" + "<br />";
}
}
}
@ -116,7 +120,6 @@ namespace Oqtane.Infrastructure
return log;
}
private Dictionary<string, string> GetSettings(List<Setting> settings)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();

View File

@ -42,7 +42,6 @@ namespace Oqtane.SiteTemplates
Icon = "home",
IsNavigation = true,
IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.AllUsersRole, true),
new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -89,7 +88,6 @@ namespace Oqtane.SiteTemplates
Icon = "lock-locked",
IsNavigation = true,
IsPersonalizable = false,
EditMode = false,
PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.RegisteredRole, true),
new Permission(PermissionNames.View, Constants.AdminRole, true),
@ -114,7 +112,6 @@ namespace Oqtane.SiteTemplates
Icon = "target",
IsNavigation = true,
IsPersonalizable = true,
EditMode = false,
PagePermissions = new List<Permission> {
new Permission(PermissionNames.View, Constants.AllUsersRole, 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")))
{
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);
if (!System.IO.File.Exists(Path.Combine(folderpath, "logo-white.png")))
{

View File

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

View File

@ -8,24 +8,18 @@ using System;
using System.Collections.Generic;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Controllers;
namespace Oqtane.Modules.HtmlText.Controllers
{
[Route("{alias}/api/[controller]")]
public class HtmlTextController : Controller
public class HtmlTextController : ModuleControllerBase
{
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;
_logger = logger;
if (httpContextAccessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(httpContextAccessor.HttpContext.Request.Query["entityid"]);
}
}
// GET api/<controller>/5

View File

@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>7.3</LangVersion>
<Configurations>Debug;Release</Configurations>
<Version>1.0.0</Version>
<Version>1.0.3</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>Not for production use.</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
</PropertyGroup>
@ -31,6 +31,8 @@
<EmbeddedResource Include="Scripts\Tenant.00.09.01.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.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.Uninstall.sql" />
</ItemGroup>
@ -48,4 +50,14 @@
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
</ItemGroup>
</Project>
<ItemGroup>
<UpgradeFiles Include="$(ProjectDir)bin\Release\netcoreapp3.1\Oqtane.Upgrade.deps.json" />
<UpgradeFiles Include="$(ProjectDir)bin\Release\netcoreapp3.1\Oqtane.Upgrade.dll" />
<UpgradeFiles Include="$(ProjectDir)bin\Release\netcoreapp3.1\Oqtane.Upgrade.pdb" />
<UpgradeFiles Include="$(ProjectDir)bin\Release\netcoreapp3.1\Oqtane.Upgrade.runtimeconfig.json" />
<TemplateFiles Include="$(ProjectDir)wwwroot\Modules\Templates\**\*.*" />
</ItemGroup>
<Target Name="AddPayloadsFolder" AfterTargets="Publish">
<Copy SourceFiles="@(UpgradeFiles)" DestinationFiles="@(UpgradeFiles->'$(PublishDir)%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
<Copy SourceFiles="@(TemplateFiles)" DestinationFiles="@(TemplateFiles->'$(PublishDir)wwwroot\Modules\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
</Target></Project>

View File

@ -45,7 +45,21 @@ namespace Oqtane.Repository
public File GetFile(int fileId)
{
File file = _db.File.Where(item => item.FileId == fileId).Include(item => item.Folder).FirstOrDefault();
return GetFile(fileId, true);
}
public File GetFile(int fileId, bool tracking)
{
File file;
if (tracking)
{
file = _db.File.Where(item => item.FileId == fileId).Include(item => item.Folder).FirstOrDefault();
}
else
{
file = _db.File.AsNoTracking().Where(item => item.FileId == fileId).Include(item => item.Folder).FirstOrDefault();
}
if (file != null)
{
IEnumerable<Permission> permissions = _permissions.GetPermissions(EntityNames.Folder, file.FolderId).ToList();

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