Merge pull request #2669 from oqtane/dev

3.4.0 release
This commit is contained in:
Shaun Walker
2023-03-12 10:11:44 -04:00
committed by GitHub
147 changed files with 2248 additions and 1426 deletions

View File

@ -55,7 +55,7 @@
else else
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">String:</Label> <Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea> <textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
</div> </div>

View File

@ -7,7 +7,7 @@
<div class="row"> <div class="row">
@foreach (var p in _pages) @foreach (var p in _pages)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
string url = NavigateUrl(p.Path); string url = NavigateUrl(p.Path);
<div class="col-md-2 mx-auto text-center mb-3"> <div class="col-md-2 mx-auto text-center mb-3">

View File

@ -62,8 +62,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<div class="col-sm-12"> <div class="col-sm-12">
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label> <Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" PermissionList="@_permissions" @ref="_permissionGrid" />
</div> </div>
</div> </div>
</div> </div>
@ -99,7 +98,7 @@
private string _imagesizes = string.Empty; private string _imagesizes = string.Empty;
private string _capacity = "0"; private string _capacity = "0";
private bool _isSystem; private bool _isSystem;
private string _permissions = string.Empty; private List<Permission> _permissions = null;
private string _createdBy; private string _createdBy;
private DateTime _createdOn; private DateTime _createdOn;
private string _modifiedBy; private string _modifiedBy;
@ -131,7 +130,7 @@
_imagesizes = folder.ImageSizes; _imagesizes = folder.ImageSizes;
_capacity = folder.Capacity.ToString(); _capacity = folder.Capacity.ToString();
_isSystem = folder.IsSystem; _isSystem = folder.IsSystem;
_permissions = folder.Permissions; _permissions = folder.PermissionList;
_createdBy = folder.CreatedBy; _createdBy = folder.CreatedBy;
_createdOn = folder.CreatedOn; _createdOn = folder.CreatedOn;
_modifiedBy = folder.ModifiedBy; _modifiedBy = folder.ModifiedBy;
@ -196,7 +195,7 @@
folder.ImageSizes = _imagesizes; folder.ImageSizes = _imagesizes;
folder.Capacity = int.Parse(_capacity); folder.Capacity = int.Parse(_capacity);
folder.IsSystem = _isSystem; folder.IsSystem = _isSystem;
folder.Permissions = _permissionGrid.GetPermissions(); folder.PermissionList = _permissionGrid.GetPermissionList();
if (_folderId != -1) if (_folderId != -1)
{ {

View File

@ -1,3 +1,4 @@
@using System.Net
@namespace Oqtane.Modules.Admin.Login @namespace Oqtane.Modules.Admin.Login
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@ -205,7 +206,7 @@
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider
.GetService(typeof(IdentityAuthenticationStateProvider)); .GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged(); authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true)); NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
} }
else else
{ {

View File

@ -106,12 +106,6 @@ else
{ {
try try
{ {
// external link to log item will display Details component
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
{
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"/{id}"));
}
if (UrlParameters.ContainsKey("level")) if (UrlParameters.ContainsKey("level"))
{ {
_level = UrlParameters["level"]; _level = UrlParameters["level"];
@ -241,4 +235,15 @@ else
_page = page; _page = page;
} }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// external link to log item will display Details component
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
{
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"/{id}"));
}
}
}
} }

View File

@ -174,7 +174,7 @@ else
private bool IsValid(string name) private bool IsValid(string name)
{ {
// must contain letters, underscores and digits and first character must be letter or underscore // must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$"); return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
} }
private void TemplateChanged(ChangeEventArgs e) private void TemplateChanged(ChangeEventArgs e)

View File

@ -139,7 +139,7 @@
private bool IsValid(string name) private bool IsValid(string name)
{ {
// must contain letters, underscores and digits and first character must be letter or underscore // must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$"); return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
} }
private void TemplateChanged(ChangeEventArgs e) private void TemplateChanged(ChangeEventArgs e)

View File

@ -9,189 +9,194 @@
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip> @if (_initialized)
<TabPanel Name="Definition" ResourceKey="Definition"> {
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <TabStrip>
<div class="container"> <TabPanel Name="Definition" ResourceKey="Definition">
<div class="row mb-1 align-items-center"> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<Label Class="col-sm-3" For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label> <div class="container">
<div class="col-sm-9"> <div class="row mb-1 align-items-center">
<input id="name" class="form-control" @bind="@_name" maxlength="200" required /> <Label Class="col-sm-3" For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label>
</div> <div class="col-sm-9">
</div> <input id="name" class="form-control" @bind="@_name" maxlength="200" required />
<div class="row mb-1 align-items-center"> </div>
<Label Class="col-sm-3" For="description" HelpText="The description of the module" ResourceKey="Description">Description: </Label> </div>
<div class="col-sm-9"> <div class="row mb-1 align-items-center">
<textarea id="description" class="form-control" @bind="@_description" rows="2" maxlength="2000" required></textarea> <Label Class="col-sm-3" For="description" HelpText="The description of the module" ResourceKey="Description">Description: </Label>
</div> <div class="col-sm-9">
</div> <textarea id="description" class="form-control" @bind="@_description" rows="2" maxlength="2000" required></textarea>
<div class="row mb-1 align-items-center"> </div>
<Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label> </div>
<div class="col-sm-9"> <div class="row mb-1 align-items-center">
<input id="categories" class="form-control" @bind="@_categories" maxlength="200" required /> <Label Class="col-sm-3" For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
</div> <div class="col-sm-9">
</div> <input id="categories" class="form-control" @bind="@_categories" maxlength="200" required />
</div> </div>
</form>
<Section Name="Information" ResourceKey="Information">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="moduledefinitionname" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
<div class="col-sm-9">
<input id="moduledefinitionname" class="form-control" @bind="@_moduledefinitionname" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="version" HelpText="The version of the module" ResourceKey="Version">Version: </Label>
<div class="col-sm-9">
<input id="version" class="form-control" @bind="@_version" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed" ResourceKey="PackageName">Package Name: </Label>
<div class="col-sm-9">
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
</div> </div>
</div> </div>
</form>
<Section Name="Information" ResourceKey="Information">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="moduledefinitionname" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
<div class="col-sm-9">
<input id="moduledefinitionname" class="form-control" @bind="@_moduledefinitionname" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="version" HelpText="The version of the module" ResourceKey="Version">Version: </Label>
<div class="col-sm-9">
<input id="version" class="form-control" @bind="@_version" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed" ResourceKey="PackageName">Package Name: </Label>
<div class="col-sm-9">
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
<div class="col-sm-9">
<input id="owner" class="form-control" @bind="@_owner" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="contact" HelpText="The contact for the module" ResourceKey="Contact">Contact: </Label>
<div class="col-sm-9">
<input id="contact" class="form-control" @bind="@_contact" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="license" HelpText="The module license terms" ResourceKey="License">License: </Label>
<div class="col-sm-9">
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label>
<div class="col-sm-9">
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</div>
</div>
</div>
</Section>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
</TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions">
<div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label> <PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" PermissionList="@_permissions" @ref="_permissionGrid" />
<div class="col-sm-9"> </div>
<input id="owner" class="form-control" @bind="@_owner" disabled /> </div>
</div> <br />
</div> <button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<div class="row mb-1 align-items-center"> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<Label Class="col-sm-3" For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label> </TabPanel>
<div class="col-sm-9"> <TabPanel Name="Translations" ResourceKey="Translations">
<input id="url" class="form-control" @bind="@_url" disabled /> @if (_languages != null && _languages.Count > 0)
</div> {
</div> <Pager Items="@_languages">
<div class="row mb-1 align-items-center"> <Header>
<Label Class="col-sm-3" For="contact" HelpText="The contact for the module" ResourceKey="Contact">Contact: </Label>
<div class="col-sm-9">
<input id="contact" class="form-control" @bind="@_contact" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="license" HelpText="The module license terms" ResourceKey="License">License: </Label>
<div class="col-sm-9">
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label>
<div class="col-sm-9">
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</div>
</div>
</div>
</Section>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
</TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions">
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
</div>
</div>
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel>
<TabPanel Name="Translations" ResourceKey="Translations">
@if (_languages != null && _languages.Count > 0)
{
<Pager Items="@_languages">
<Header>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
<th>@Localizer["Code"]</th> <th>@Localizer["Code"]</th>
<th style="width: 1px;">@Localizer["Version"]</th> <th style="width: 1px;">@Localizer["Version"]</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
</Header> </Header>
<Row> <Row>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.Code</td> <td>@context.Code</td>
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td> <td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
<td> <td>
@switch (TranslationAvailable(_packagename + "." + context.Code, context.Version)) @switch (TranslationAvailable(_packagename + "." + context.Code, context.Version))
{ {
case "install": case "install":
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button> <button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
break; break;
case "upgrade": case "upgrade":
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button> <button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button>
break; break;
} }
</td> </td>
</Row> </Row>
</Pager> </Pager>
@if (_install) @if (_install)
{ {
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button> <button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
}
} }
} else
else {
{ <br />
<br /> <div class="mx-auto text-center">
<div class="mx-auto text-center"> @if (string.IsNullOrEmpty(_packagename))
@if (string.IsNullOrEmpty(_packagename)) {
{ @Localizer["Search.PackageNameMissing"]
@Localizer["Search.PackageNameMissing"] }
} else
else {
{ @Localizer["Search.NoResults"]
@Localizer["Search.NoResults"] }
} </div>
</div> <br />
<br /> }
} </TabPanel>
</TabPanel> </TabStrip>
</TabStrip>
@if (_package != null) @if (_package != null)
{ {
<div class="app-actiondialog"> <div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog"> <div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5> <h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button> <button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p style="height: 200px; overflow-y: scroll;"> <p style="height: 200px; overflow-y: scroll;">
<h4 style="display: inline;"><a href="@_package.ProductUrl" target="_new">@_package.Name</a></h4><br /> <h4 style="display: inline;"><a href="@_package.ProductUrl" target="_new">@_package.Name</a></h4><br />
@SharedLocalizer["Search.By"]:&nbsp;&nbsp;<strong><a href="@_package.OwnerUrl" target="new">@_package.Owner</a></strong><br /> @SharedLocalizer["Search.By"]:&nbsp;&nbsp;<strong><a href="@_package.OwnerUrl" target="new">@_package.Owner</a></strong><br />
@(_package.Description.Length > 400 ? (_package.Description.Substring(0, 400) + "...") : _package.Description)<br /> @(_package.Description.Length > 400 ? (_package.Description.Substring(0, 400) + "...") : _package.Description)<br />
<strong>@(String.Format("{0:n0}", _package.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp; <strong>@(String.Format("{0:n0}", _package.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@_package.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp; @SharedLocalizer["Search.Released"]: <strong>@_package.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@_package.Version</strong> @SharedLocalizer["Search.Version"]: <strong>@_package.Version</strong>
@((MarkupString)(!string.IsNullOrEmpty(_package.PackageUrl) ? "&nbsp;&nbsp;|&nbsp;&nbsp;" + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(_package.PackageUrl).Host + "</strong>" : "")) @((MarkupString)(!string.IsNullOrEmpty(_package.PackageUrl) ? "&nbsp;&nbsp;|&nbsp;&nbsp;" + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(_package.PackageUrl).Host + "</strong>" : ""))
<br /><br /> <br /><br />
@if (!string.IsNullOrEmpty(_package.License)) @if (!string.IsNullOrEmpty(_package.License))
{ {
@((MarkupString)_package.License.Replace("\n", "<br />")) @((MarkupString)_package.License.Replace("\n", "<br />"))
} }
else else
{ {
@SharedLocalizer["License Not Specified"] @SharedLocalizer["License Not Specified"]
} }
</p> </p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button> <button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> }
} }
@code { @code {
private bool _initialized = false;
private ElementReference form; private ElementReference form;
private bool validated = false; private bool validated = false;
private int _moduleDefinitionId; private int _moduleDefinitionId;
@ -206,7 +211,7 @@
private string _contact = ""; private string _contact = "";
private string _license = ""; private string _license = "";
private string _runtimes = ""; private string _runtimes = "";
private string _permissions; private List<Permission> _permissions = null;
private string _createdby; private string _createdby;
private DateTime _createdon; private DateTime _createdon;
private string _modifiedby; private string _modifiedby;
@ -242,7 +247,7 @@
_contact = moduleDefinition.Contact; _contact = moduleDefinition.Contact;
_license = moduleDefinition.License; _license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes; _runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.Permissions; _permissions = moduleDefinition.PermissionList;
_createdby = moduleDefinition.CreatedBy; _createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn; _createdon = moduleDefinition.CreatedOn;
_modifiedby = moduleDefinition.ModifiedBy; _modifiedby = moduleDefinition.ModifiedBy;
@ -262,6 +267,8 @@
} }
_languages = _languages.OrderBy(item => item.Name).ToList(); _languages = _languages.OrderBy(item => item.Name).ToList();
} }
_initialized = true;
} }
} }
catch (Exception ex) catch (Exception ex)
@ -292,7 +299,7 @@
{ {
moduledefinition.Categories = _categories; moduledefinition.Categories = _categories;
} }
moduledefinition.Permissions = _permissionGrid.GetPermissions(); moduledefinition.PermissionList = _permissionGrid.GetPermissionList();
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition); await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition); await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());

View File

@ -46,7 +46,7 @@
<select id="page" class="form-select" @bind="@_pageId" required> <select id="page" class="form-select" @bind="@_pageId" required>
@foreach (Page p in PageState.Pages) @foreach (Page p in PageState.Pages)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option> <option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
} }
@ -62,7 +62,7 @@
{ {
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
</div> </div>
</div> </div>
@ -101,7 +101,7 @@
private string _containerType; private string _containerType;
private string _allPages = "false"; private string _allPages = "false";
private string _permissionNames = ""; private string _permissionNames = "";
private string _permissions = null; private List<Permission> _permissions = null;
private string _pageId; private string _pageId;
private PermissionGrid _permissionGrid; private PermissionGrid _permissionGrid;
private Type _moduleSettingsType; private Type _moduleSettingsType;
@ -123,7 +123,7 @@
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType); _containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
_containerType = ModuleState.ContainerType; _containerType = ModuleState.ContainerType;
_allPages = ModuleState.AllPages.ToString(); _allPages = ModuleState.AllPages.ToString();
_permissions = ModuleState.Permissions; _permissions = ModuleState.PermissionList;
_pageId = ModuleState.PageId.ToString(); _pageId = ModuleState.PageId.ToString();
createdby = ModuleState.CreatedBy; createdby = ModuleState.CreatedBy;
createdon = ModuleState.CreatedOn; createdon = ModuleState.CreatedOn;
@ -207,7 +207,7 @@
var module = ModuleState; var module = ModuleState;
module.AllPages = bool.Parse(_allPages); module.AllPages = bool.Parse(_allPages);
module.PageModuleId = ModuleState.PageModuleId; module.PageModuleId = ModuleState.PageModuleId;
module.Permissions = _permissionGrid.GetPermissions(); module.PermissionList = _permissionGrid.GetPermissionList();
await ModuleService.UpdateModuleAsync(module); await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null) if (_moduleSettingsType != null)

View File

@ -183,7 +183,7 @@
private string _themetype = string.Empty; private string _themetype = string.Empty;
private string _containertype = string.Empty; private string _containertype = string.Empty;
private string _icon = string.Empty; private string _icon = string.Empty;
private string _permissions = string.Empty; private string _permissions = null;
private PermissionGrid _permissionGrid; private PermissionGrid _permissionGrid;
private Type _themeSettingsType; private Type _themeSettingsType;
private object _themeSettings; private object _themeSettings;
@ -202,7 +202,6 @@
_containers = ThemeService.GetContainerControls(_themeList, _themetype); _containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = PageState.Site.DefaultContainerType; _containertype = PageState.Site.DefaultContainerType;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_permissions = string.Empty;
ThemeSettings(); ThemeSettings();
} }
catch (Exception ex) catch (Exception ex)
@ -222,7 +221,7 @@
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_children.Add(p); _children.Add(p);
} }
@ -232,7 +231,7 @@
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_children.Add(p); _children.Add(p);
} }
@ -378,7 +377,7 @@
page.DefaultContainerType = string.Empty; page.DefaultContainerType = string.Empty;
} }
page.Icon = (_icon == null ? string.Empty : _icon); page.Icon = (_icon == null ? string.Empty : _icon);
page.Permissions = _permissionGrid.GetPermissions(); page.PermissionList = _permissionGrid.GetPermissionList();
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
page.UserId = null; page.UserId = null;
page.Meta = _meta; page.Meta = _meta;

View File

@ -158,7 +158,7 @@
{ {
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
</div> </div>
</div> </div>
} }
@ -174,8 +174,8 @@
<th>@Localizer["ModuleDefinition"]</th> <th>@Localizer["ModuleDefinition"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" ResourceKey="ModuleSettings" /></td> <td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td> <td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@context.Title</td> <td>@context.Title</td>
<td>@context.ModuleDefinition?.Name</td> <td>@context.ModuleDefinition?.Name</td>
</Row> </Row>
@ -221,7 +221,7 @@
private string _themetype; private string _themetype;
private string _containertype = "-"; private string _containertype = "-";
private string _icon; private string _icon;
private string _permissions = null; private List<Permission> _permissions = null;
private string _createdby; private string _createdby;
private DateTime _createdon; private DateTime _createdon;
private string _modifiedby; private string _modifiedby;
@ -292,7 +292,7 @@
_containertype = PageState.Site.DefaultContainerType; _containertype = PageState.Site.DefaultContainerType;
} }
_icon = page.Icon; _icon = page.Icon;
_permissions = page.Permissions; _permissions = page.PermissionList;
_createdby = page.CreatedBy; _createdby = page.CreatedBy;
_createdon = page.CreatedOn; _createdon = page.CreatedOn;
_modifiedby = page.ModifiedBy; _modifiedby = page.ModifiedBy;
@ -339,7 +339,7 @@
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_children.Add(p); _children.Add(p);
} }
@ -349,7 +349,7 @@
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid))) foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_children.Add(p); _children.Add(p);
} }
@ -509,7 +509,7 @@
page.DefaultContainerType = string.Empty; page.DefaultContainerType = string.Empty;
} }
page.Icon = _icon ?? string.Empty; page.Icon = _icon ?? string.Empty;
page.Permissions = _permissionGrid.GetPermissions(); page.PermissionList = _permissionGrid.GetPermissionList();
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
page.UserId = null; page.UserId = null;
page.Meta = _meta; page.Meta = _meta;

View File

@ -22,7 +22,7 @@ else
} }
else else
{ {
<Pager Items="@_pages.Where(item => item.IsDeleted)"> <Pager Items="@_pages.Where(item => item.IsDeleted)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
@ -50,7 +50,7 @@ else
} }
else else
{ {
<Pager Items="@_modules.Where(item => item.IsDeleted)"> <Pager Items="@_modules.Where(item => item.IsDeleted)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
@ -76,9 +76,10 @@ else
} }
@code { @code {
private List<Page> _pages; private List<Page> _pages;
private List<Module> _modules; private List<Module> _modules;
private int _pagePage = 1;
private int _pageModule = 1;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@ -140,7 +141,7 @@ else
try try
{ {
ModuleInstance.ShowProgressIndicator(); ModuleInstance.ShowProgressIndicator();
foreach (Page page in _pages.Where(item => item.IsDeleted)) foreach (Page page in _pages.Where(item => item.IsDeleted))
{ {
await PageService.DeletePageAsync(page.PageId); await PageService.DeletePageAsync(page.PageId);
await logger.LogInformation("Page Permanently Deleted {Page}", page); await logger.LogInformation("Page Permanently Deleted {Page}", page);
@ -185,7 +186,7 @@ else
await PageModuleService.DeletePageModuleAsync(module.PageModuleId); await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
// check if there are any remaining module instances in the site // check if there are any remaining module instances in the site
if (!_modules.Exists(item => item.ModuleId == module.ModuleId)) if (!_modules.Exists (item => item.ModuleId == module.ModuleId && item.PageModuleId != module.PageModuleId))
{ {
await ModuleService.DeleteModuleAsync(module.ModuleId); await ModuleService.DeleteModuleAsync(module.ModuleId);
} }
@ -206,12 +207,14 @@ else
try try
{ {
ModuleInstance.ShowProgressIndicator(); ModuleInstance.ShowProgressIndicator();
foreach (Module module in _modules.Where(item => item.IsDeleted)) foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
{ {
await PageModuleService.DeletePageModuleAsync(module.PageModuleId); await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
// DeletePageModuleAsync does not update _modules so remove it.
_modules.Remove(module);
// check if there are any remaining module instances in the site // check if there are any remaining module instances in the site
if (!_modules.Exists(item => item.ModuleId == module.ModuleId)) if (!_modules.Exists(item => item.ModuleId == module.ModuleId && item.PageModuleId != module.PageModuleId))
{ {
await ModuleService.DeleteModuleAsync(module.ModuleId); await ModuleService.DeleteModuleAsync(module.ModuleId);
} }
@ -229,4 +232,12 @@ else
ModuleInstance.HideProgressIndicator(); ModuleInstance.HideProgressIndicator();
} }
} }
private void OnPageChangePage(int page)
{
_pagePage = page;
}
private void OnPageChangeModule(int page)
{
_pageModule = page;
}
} }

View File

@ -78,7 +78,7 @@
<option value="-">&lt;@Localizer["Not Specified"]&gt;</option> <option value="-">&lt;@Localizer["Not Specified"]&gt;</option>
@foreach (Page page in PageState.Pages) @foreach (Page page in PageState.Pages)
{ {
if (UserSecurity.ContainsRole(page.Permissions, PermissionNames.View, RoleNames.Everyone)) if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
{ {
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option> <option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
} }
@ -95,6 +95,12 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing" ResourceKey="SiteMap">Site Map: </Label>
<div class="col-sm-9">
<input id="sitemap" class="form-control" @bind="@_sitemap" required disabled />
</div>
</div>
</div> </div>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings"> <Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<div class="container"> <div class="container">
@ -267,16 +273,16 @@
</div> </div>
</div> </div>
</Section> </Section>
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation"> <Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label> <Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly /> <input id="tenant" class="form-control" @bind="@_tenant" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label> <Label Class="col-sm-3" For="database" HelpText="The type of database" ResourceKey="Database">Type: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="database" class="form-control" @bind="@_database" readonly /> <input id="database" class="form-control" @bind="@_database" readonly />
</div> </div>
@ -284,7 +290,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label> <Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea> <input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
</div> </div>
</div> </div>
</div> </div>
@ -321,6 +327,7 @@
private string _containertype = "-"; private string _containertype = "-";
private string _admincontainertype = "-"; private string _admincontainertype = "-";
private string _homepageid = "-"; private string _homepageid = "-";
private string _sitemap = "";
private string _smtphost = string.Empty; private string _smtphost = string.Empty;
private string _smtpport = string.Empty; private string _smtpport = string.Empty;
private string _smtpssl = "False"; private string _smtpssl = "False";
@ -361,6 +368,7 @@
_runtime = site.Runtime; _runtime = site.Runtime;
_prerender = site.RenderMode.Replace(_runtime, ""); _prerender = site.RenderMode.Replace(_runtime, "");
_isdeleted = site.IsDeleted.ToString(); _isdeleted = site.IsDeleted.ToString();
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
await GetAliases(); await GetAliases();

View File

@ -103,7 +103,7 @@ else
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label> <Label Class="col-sm-3" For="tenant" HelpText="Select the database for the site" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required> <select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
<option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option> <option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option>
@ -121,13 +121,13 @@ else
<hr class="app-rule" /> <hr class="app-rule" />
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label> <Label Class="col-sm-3" For="name" HelpText="Enter the name for the database" ResourceKey="TenantName">Name: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required /> <input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label> <Label Class="col-sm-3" For="databaseType" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@if (_databases != null) @if (_databases != null)
{ {
@ -160,7 +160,7 @@ else
else else
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">String:</Label> <Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea> <textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
</div> </div>
@ -315,7 +315,7 @@ else
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); _urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
var duplicates = new List<string>(); var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync(); var aliases = await AliasService.GetAliasesAsync();
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
{ {
if (aliases.Exists(item => item.Name == name)) if (aliases.Exists(item => item.Name == name))
{ {
@ -329,7 +329,7 @@ else
if (_tenantid == "+") if (_tenantid == "+")
{ {
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null) if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
{ {
// validate host credentials // validate host credentials
var user = new User(); var user = new User();

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Admin.Sql @namespace Oqtane.Modules.Admin.Sql
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ISystemService SystemService
@inject ITenantService TenantService @inject ITenantService TenantService
@inject IDatabaseService DatabaseService @inject IDatabaseService DatabaseService
@inject ISqlService SqlService @inject ISqlService SqlService
@ -14,123 +15,284 @@
else else
{ {
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center">
<div class="row mb-1 align-items-center"> <Label Class="col-sm-3" For="connection" HelpText="Select a database connection (from appsettings.json)" ResourceKey="Connection">Connection: </Label>
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant associated with the database server" ResourceKey="Tenant">Tenant: </Label> <div class="col-sm-9">
<div class="col-sm-9"> <select id="tenant" class="form-select" value="@_connection" @onchange="(e => ConnectionChanged(e))">
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))"> <option value="-">&lt;@Localizer["Connection.Select"]&gt;</option>
<option value="-1">&lt;@Localizer["Tenant.Select"]&gt;</option> <option value="+">&lt;@Localizer["Connection.Add"]&gt;</option>
@foreach (Tenant tenant in _tenants) @foreach (var connection in _connections)
{ {
<option value="@tenant.TenantId">@tenant.Name</option> <option value="@connection.Key">@connection.Key</option>
} }
</select> </select>
</div> </div>
</div> </div>
@if (_tenantid != "-1") @if (_connection == "+")
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label> <Label Class="col-sm-3" For="name" HelpText="Enter the name of the connection" ResourceKey="Name">Name: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="database" class="form-control" @bind="@_database" readonly /> <input id="name" class="form-control" @bind="@_name" maxlength="100" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<div class="col-sm-9">
<div class="input-group">
<input id="connectionstring" type="@_connectionstringtype" class="form-control" @bind="@_connectionstring" readonly />
<button type="button" class="btn btn-secondary" @onclick="@ToggleConnectionString">@_connectionstringtoggle</button>
</div>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sqlQuery" HelpText="Enter the SQL query for the database server" ResourceKey="SqlQuery">SQL Query: </Label> <Label Class="col-sm-3" For="databasetype" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="sqlQuery" class="form-control" @bind="@_sql" rows="3"></textarea> @if (_databases != null)
</div> {
</div> <div class="input-group">
} <select id="databasetype" class="form-select" value="@_databasetype" @onchange="(e => DatabaseTypeChanged(e))" required>
</div> @foreach (var database in _databases)
<br /> {
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button> <option value="@database.Name">@Localizer[@database.Name]</option>
<br /> }
<br /> </select>
@if (_results != null) @if (!_showConnectionString)
{ {
@if (_results.Count > 0) <button type="button" class="btn btn-secondary" @onclick="ShowConnectionString">@Localizer["EnterConnectionString"]</button>
{ }
<Pager Class="table table-bordered" Items="@_results"> else
<Header> {
@foreach (KeyValuePair<string, string> kvp in _results.First()) <button type="button" class="btn btn-secondary" @onclick="ShowConnectionString">@Localizer["EnterConnectionParameters"]</button>
{ }
<th>@kvp.Key</th> </div>
} }
</Header> </div>
<Row> </div>
@foreach (KeyValuePair<string, string> kvp in context) @if (!_showConnectionString)
{ {
<td>@kvp.Value</td> if (_databaseConfigType != null)
} {
</Row> @DatabaseConfigComponent
</Pager> }
} }
else else
{ {
@Localizer["Return.NoResult"] <div class="row mb-1 align-items-center">
} <Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
<br /> <div class="col-sm-9">
<br /> <textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="3"></textarea>
} </div>
</div>
}
<br />
<button type="button" class="btn btn-success" @onclick="Add">@Localizer["Add"]</button>
}
else
{
@if (_connection != "-")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9">
@if (_databases != null)
{
<select id="databasetype" class="form-select" @bind="@_databasetype" required>
<option value="-">&lt;@Localizer["Type.Select"]&gt;</option>
@foreach (var database in _databases)
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
</select>
}
</div>
</div>
@if (!string.IsNullOrEmpty(_tenant))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection string" ResourceKey="ConnectionString">Settings: </Label>
<div class="col-sm-9">
<div class="input-group">
<input id="connectionstring" type="@_connectionstringtype" class="form-control" @bind="@_connectionstring" readonly />
<button type="button" class="btn btn-secondary" @onclick="@ToggleConnectionString">@_connectionstringtoggle</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sqlQuery" HelpText="Enter a valid SQL query for the database" ResourceKey="SqlQuery">SQL Query: </Label>
<div class="col-sm-9">
<textarea id="sqlQuery" class="form-control" @bind="@_sql" rows="3"></textarea>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button>
<br />
<br />
@if (_results != null)
{
@if (_results.Count > 0)
{
<Pager Class="table table-bordered" Items="@_results">
<Header>
@foreach (KeyValuePair<string, string> kvp in _results.First())
{
<th>@kvp.Key</th>
}
</Header>
<Row>
@foreach (KeyValuePair<string, string> kvp in context)
{
<td>@kvp.Value</td>
}
</Row>
</Pager>
}
else
{
@Localizer["Return.NoResult"]
}
<br />
<br />
}
}
}
</div>
} }
@code { @code {
private List<Tenant> _tenants; private string _connection = "-";
private string _tenantid = "-1"; private Dictionary<string, object> _connections;
private string _database = string.Empty; private List<Tenant> _tenants;
private List<Database> _databases;
private string _name = string.Empty;
private string _databasetype = string.Empty;
private Type _databaseConfigType;
private object _databaseConfig;
private RenderFragment DatabaseConfigComponent { get; set; }
private bool _showConnectionString = false;
private string _tenant = string.Empty;
private string _connectionstring = string.Empty; private string _connectionstring = string.Empty;
private string _connectionstringtype = "password"; private string _connectionstringtype = "password";
private string _connectionstringtoggle = string.Empty; private string _connectionstringtoggle = string.Empty;
private string _sql = string.Empty; private string _sql = string.Empty;
private List<Dictionary<string, string>> _results; private List<Dictionary<string, string>> _results;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_tenants = await TenantService.GetTenantsAsync(); _connections = await SystemService.GetSystemInfoAsync("connectionstrings");
_tenants = await TenantService.GetTenantsAsync();
_databases = await DatabaseService.GetDatabasesAsync();
_connectionstringtoggle = SharedLocalizer["ShowPassword"]; _connectionstringtoggle = SharedLocalizer["ShowPassword"];
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message); await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message);
AddModuleMessage(ex.Message, MessageType.Error); AddModuleMessage(ex.Message, MessageType.Error);
} }
} }
private async void TenantChanged(ChangeEventArgs e) private async void ConnectionChanged(ChangeEventArgs e)
{ {
try try
{ {
_tenantid = (string)e.Value; _connection = (string)e.Value;
var tenants = await TenantService.GetTenantsAsync(); if (_connection != "-" && _connection != "+")
var _databases = await DatabaseService.GetDatabasesAsync(); {
var tenant = tenants.Find(item => item.TenantId == int.Parse(_tenantid)); _connectionstring = _connections[_connection].ToString();
if (tenant != null) _tenant = "";
{ _databasetype = "-";
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name; var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
_connectionstring = tenant.DBConnectionString; if (tenant != null)
} {
StateHasChanged(); _tenant = tenant.Name;
} _databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name;
catch (Exception ex) }
{ }
await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message); else
AddModuleMessage(ex.Message, MessageType.Error); {
} if (_databases.Exists(item => item.IsDefault))
} {
_databasetype = _databases.Find(item => item.IsDefault).Name;
}
else
{
_databasetype = "LocalDB";
}
_showConnectionString = false;
LoadDatabaseConfigComponent();
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Connection {Connection} {Error}", _connection, ex.Message);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
private void DatabaseTypeChanged(ChangeEventArgs eventArgs)
{
try
{
_databasetype = (string)eventArgs.Value;
_showConnectionString = false;
LoadDatabaseConfigComponent();
}
catch
{
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
}
}
private void LoadDatabaseConfigComponent()
{
var database = _databases.SingleOrDefault(d => d.Name == _databasetype);
if (database != null)
{
_databaseConfigType = Type.GetType(database.ControlType);
DatabaseConfigComponent = builder =>
{
builder.OpenComponent(0, _databaseConfigType);
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
builder.CloseComponent();
};
}
}
private void ShowConnectionString()
{
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
_connectionstring = databaseConfigControl.GetConnectionString();
}
_showConnectionString = !_showConnectionString;
}
private async Task Add()
{
var connectionstring = _connectionstring;
if (!_showConnectionString && _databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
connectionstring = databaseConfigControl.GetConnectionString();
}
if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(connectionstring))
{
var settings = new Dictionary<string, object>();
settings.Add($"{SettingKeys.ConnectionStringsSection}:{_name}", connectionstring);
await SystemService.UpdateSystemInfoAsync(settings);
_connections = await SystemService.GetSystemInfoAsync("connectionstrings");
_connection = "-";
AddModuleMessage(Localizer["Message.Connection.Added"], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Required.Connection"], MessageType.Warning);
}
}
private void ToggleConnectionString() private void ToggleConnectionString()
{ {
@ -145,14 +307,15 @@ else
_connectionstringtoggle = SharedLocalizer["ShowPassword"]; _connectionstringtoggle = SharedLocalizer["ShowPassword"];
} }
} }
private async Task Execute() private async Task Execute()
{ {
try try
{ {
if (_tenantid != "-1" && !string.IsNullOrEmpty(_sql)) if (_databasetype != "-" && !string.IsNullOrEmpty(_sql))
{ {
var sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql }; var dbtype = _databases.FirstOrDefault(item => item.Name == _databasetype).DBType;
var sqlquery = new SqlQuery { DBConnectionString = _connection, DBType = dbtype, Query = _sql };
sqlquery = await SqlService.ExecuteQueryAsync(sqlquery); sqlquery = await SqlService.ExecuteQueryAsync(sqlquery);
_results = sqlquery.Results; _results = sqlquery.Results;
AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success); AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success);

View File

@ -156,6 +156,8 @@
</div> </div>
</div> </div>
</div> </div>
<br /><br />
<button type="button" class="btn btn-danger" @onclick="ClearLog">@Localizer["Clear"]</button>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
<br /><br /> <br /><br />
@ -188,7 +190,7 @@
{ {
_version = Constants.Version; _version = Constants.Version;
Dictionary<string, object> systeminfo = await SystemService.GetSystemInfoAsync("environment"); var systeminfo = await SystemService.GetSystemInfoAsync("environment");
if (systeminfo != null) if (systeminfo != null)
{ {
_clrversion = systeminfo["CLRVersion"].ToString(); _clrversion = systeminfo["CLRVersion"].ToString();
@ -222,24 +224,41 @@
} }
private async Task SaveConfig() private async Task SaveConfig()
{ {
try try
{ {
var settings = new Dictionary<string, object>(); var settings = new Dictionary<string, object>();
settings.Add("DetailedErrors", _detailederrors); settings.Add("DetailedErrors", _detailederrors);
settings.Add("Logging:LogLevel:Default", _logginglevel); settings.Add("Logging:LogLevel:Default", _logginglevel);
settings.Add("Logging:LogLevel:Notify", _notificationlevel); settings.Add("Logging:LogLevel:Notify", _notificationlevel);
settings.Add("UseSwagger", _swagger); settings.Add("UseSwagger", _swagger);
settings.Add("PackageService", _packageservice); settings.Add("PackageService", _packageservice);
await SystemService.UpdateSystemInfoAsync(settings); await SystemService.UpdateSystemInfoAsync(settings);
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success); AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Saving Configuration"); await logger.LogError(ex, "Error Saving Configuration");
AddModuleMessage(Localizer["Error.UpdateConfig"], MessageType.Error); AddModuleMessage(Localizer["Error.UpdateConfig"], MessageType.Error);
} }
} }
private async Task ClearLog()
{
try
{
var settings = new Dictionary<string, object>();
settings.Add("clearlog", "true");
await SystemService.UpdateSystemInfoAsync(settings);
_log = string.Empty;
AddModuleMessage(Localizer["Success.ClearLog"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Clearing Log");
AddModuleMessage(Localizer["Error.ClearLog"], MessageType.Error);
}
}
private async Task RestartApplication() private async Task RestartApplication()
{ {

View File

@ -121,7 +121,7 @@
private bool IsValid(string name) private bool IsValid(string name)
{ {
// must contain letters, underscores and digits and first character must be letter or underscore // must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && name.ToLower() != "theme" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$"); return !string.IsNullOrEmpty(name) && name.ToLower() != "theme" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
} }
private void TemplateChanged(ChangeEventArgs e) private void TemplateChanged(ChangeEventArgs e)

View File

@ -211,8 +211,11 @@ else
</Detail> </Detail>
</Pager> </Pager>
} }
<br /> @if (notifications.Any())
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" /> {
<br />
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
}
<br /><hr /> <br /><hr />
<select class="form-select" @onchange="(e => FilterChanged(e))"> <select class="form-select" @onchange="(e => FilterChanged(e))">
<option value="to">@Localizer["Inbox"]</option> <option value="to">@Localizer["Inbox"]</option>

View File

@ -294,6 +294,12 @@ else
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" /> <input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
<div class="col-sm-9">
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
</div>
</div>
} }
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label> <Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
@ -395,6 +401,7 @@ else
private string _identifierclaimtype; private string _identifierclaimtype;
private string _emailclaimtype; private string _emailclaimtype;
private string _roleclaimtype; private string _roleclaimtype;
private string _profileclaimtypes;
private string _domainfilter; private string _domainfilter;
private string _createusers; private string _createusers;
@ -449,6 +456,7 @@ else
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub"); _identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email"); _emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", ""); _roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", ""); _domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true"); _createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
@ -568,6 +576,7 @@ else
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true); settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true); settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@using System.Text.Json
@inherits LocalizableComponent @inherits LocalizableComponent
@if (_visible) @if (_visible)
@ -40,7 +41,7 @@
@code { @code {
private bool _visible = false; private bool _visible = false;
private string _permissions = string.Empty; private List<Permission> _permissions;
private bool _editmode = false; private bool _editmode = false;
private bool _authorized = false; private bool _authorized = false;
private string _iconSpan = string.Empty; private string _iconSpan = string.Empty;
@ -61,7 +62,10 @@
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter] [Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string public string Permissions { get; set; } // deprecated - use PermissionList instead
[Parameter]
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
[Parameter] [Parameter]
public string Class { get; set; } // optional public string Class { get; set; } // optional
@ -78,7 +82,15 @@
[Parameter] [Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
protected override void OnParametersSet() protected override void OnInitialized()
{
if (!string.IsNullOrEmpty(Permissions))
{
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
}
}
protected override void OnParametersSet()
{ {
base.OnParametersSet(); base.OnParametersSet();
@ -109,7 +121,7 @@
Header = Localize(nameof(Header), Header); Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message); Message = Localize(nameof(Message), Message);
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions; _permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
_authorized = IsAuthorized(); _authorized = IsAuthorized();
} }

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@using System.Net @using System.Net
@using System.Text.Json
@inherits LocalizableComponent @inherits LocalizableComponent
@inject IUserService UserService @inject IUserService UserService
@ -26,7 +27,7 @@
private string _text = string.Empty; private string _text = string.Empty;
private string _parameters = string.Empty; private string _parameters = string.Empty;
private string _url = string.Empty; private string _url = string.Empty;
private string _permissions = string.Empty; private List<Permission> _permissions;
private bool _editmode = false; private bool _editmode = false;
private bool _authorized = false; private bool _authorized = false;
private string _classname = "btn btn-primary"; private string _classname = "btn btn-primary";
@ -52,7 +53,10 @@
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter] [Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string public string Permissions { get; set; } // deprecated - use PermissionList instead
[Parameter]
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
[Parameter] [Parameter]
public bool Disabled { get; set; } // optional public bool Disabled { get; set; } // optional
@ -75,6 +79,14 @@
[Parameter] [Parameter]
public string ReturnUrl { get; set; } // optional - used to set a url to redirect to public string ReturnUrl { get; set; } // optional - used to set a url to redirect to
protected override void OnInitialized()
{
if (!string.IsNullOrEmpty(Permissions))
{
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
}
}
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
base.OnParametersSet(); base.OnParametersSet();
@ -119,7 +131,7 @@
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}"; _iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
} }
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions; _permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
_text = Localize(nameof(Text), _text); _text = Localize(nameof(Text), _text);
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters); _url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
if (!string.IsNullOrEmpty(ReturnUrl)) if (!string.IsNullOrEmpty(ReturnUrl))

View File

@ -6,7 +6,7 @@
@inject IStringLocalizer<FileManager> Localizer @inject IStringLocalizer<FileManager> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_folders != null) @if (_initialized)
{ {
<div id="@Id" class="container-fluid px-0"> <div id="@Id" class="container-fluid px-0">
<div class="row"> <div class="row">
@ -87,6 +87,7 @@
} }
@code { @code {
private bool _initialized = false;
private List<Folder> _folders; private List<Folder> _folders;
private List<File> _files = new List<File>(); private List<File> _files = new List<File>();
private string _fileinputid = string.Empty; private string _fileinputid = string.Empty;
@ -205,6 +206,8 @@
_fileinputid = "FileInput_" + _guid; _fileinputid = "FileInput_" + _guid;
_progressinfoid = "ProgressInfo_" + _guid; _progressinfoid = "ProgressInfo_" + _guid;
_progressbarid = "ProgressBar_" + _guid; _progressbarid = "ProgressBar_" + _guid;
_initialized = true;
} }
private async Task GetFiles() private async Task GetFiles()
@ -220,7 +223,7 @@
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId); Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
if (folder != null) if (folder != null)
{ {
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions); _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.PermissionList);
_files = await FileService.GetFilesAsync(FolderId); _files = await FileService.GetFilesAsync(FolderId);
} }
else else

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@using System.Text.Json
@inherits ModuleControlBase @inherits ModuleControlBase
@inject IRoleService RoleService @inject IRoleService RoleService
@inject IUserService UserService @inject IUserService UserService
@ -15,20 +16,19 @@
<tbody> <tbody>
<tr> <tr>
<th scope="col">@Localizer["Role"]</th> <th scope="col">@Localizer["Role"]</th>
@foreach (PermissionString permission in _permissions) @foreach (var permissionname in _permissionnames)
{ {
<th style="text-align: center; width: 1px;">@((MarkupString)GetPermissionName(permission).Replace(" ", "<br />"))</th> <th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
} }
</tr> </tr>
@foreach (Role role in _roles) @foreach (Role role in _roles)
{ {
<tr> <tr>
<td>@role.Name</td> <td>@role.Name</td>
@foreach (PermissionString permission in _permissions) @foreach (var permissionname in _permissionnames)
{ {
var p = permission;
<td style="text-align: center;"> <td style="text-align: center;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, role.Name)" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, role.Name))" /> <TriStateCheckBox Value=@GetPermissionValue(permissionname, role.Name, -1) Disabled="@GetPermissionDisabled(permissionname, role.Name)" OnChange="@(e => PermissionChanged(e, permissionname, role.Name, -1))" />
</td> </td>
} }
</tr> </tr>
@ -50,23 +50,21 @@
<thead> <thead>
<tr> <tr>
<th scope="col">@Localizer["User"]</th> <th scope="col">@Localizer["User"]</th>
@foreach (PermissionString permission in _permissions) @foreach (var permissionname in _permissionnames)
{ {
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th> <th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
} }
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (User user in _users) @foreach (User user in _users)
{ {
string userid = "[" + user.UserId.ToString() + "]";
<tr> <tr>
<td>@user.DisplayName</td> <td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions) @foreach (var permissionname in _permissionnames)
{ {
var p = permission;
<td style="text-align: center; width: 1px;"> <td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, "")" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, userid))" /> <TriStateCheckBox Value=@GetPermissionValue(permissionname, "", user.UserId) Disabled="@GetPermissionDisabled(permissionname, "")" OnChange="@(e => PermissionChanged(e, permissionname, "", user.UserId))" />
</td> </td>
} }
</tr> </tr>
@ -94,9 +92,9 @@
} }
@code { @code {
private string _permissionnames = string.Empty; private List<string> _permissionnames;
private List<Permission> _permissions;
private List<Role> _roles; private List<Role> _roles;
private List<PermissionString> _permissions;
private List<User> _users = new List<User>(); private List<User> _users = new List<User>();
private AutoComplete _user; private AutoComplete _user;
private string _message = string.Empty; private string _message = string.Empty;
@ -108,17 +106,16 @@
public string PermissionNames { get; set; } public string PermissionNames { get; set; }
[Parameter] [Parameter]
public string Permissions { get; set; } public string Permissions { get; set; } // deprecated - use PermissionList instead
[Parameter]
public List<Permission> PermissionList { get; set; }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
if (string.IsNullOrEmpty(PermissionNames)) if (!string.IsNullOrEmpty(Permissions))
{ {
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit; PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
}
else
{
_permissionnames = PermissionNames;
} }
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true); _roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
@ -127,88 +124,103 @@
_roles.RemoveAll(item => item.Name == RoleNames.Host); _roles.RemoveAll(item => item.Name == RoleNames.Host);
} }
_permissions = new List<PermissionString>(); // get permission names
if (string.IsNullOrEmpty(PermissionNames))
foreach (string permissionname in _permissionnames.Split(',', StringSplitOptions.RemoveEmptyEntries))
{ {
// permission names can be in the form of "EntityName:PermissionName:Roles" _permissionnames = new List<string>();
if (permissionname.Contains(":")) _permissionnames.Add(Shared.PermissionNames.View);
{ _permissionnames.Add(Shared.PermissionNames.Edit);
var segments = permissionname.Split(':');
if (segments.Length == 3)
{
if (!segments[2].Contains(RoleNames.Admin))
{
segments[2] = RoleNames.Admin + ";" + segments[2]; // ensure admin access
}
_permissions.Add(new PermissionString { EntityName = segments[0], PermissionName = segments[1], Permissions = segments[2] });
}
}
else
{
_permissions.Add(new PermissionString { EntityName = EntityName, PermissionName = permissionname, Permissions = RoleNames.Admin });
}
}
if (!string.IsNullOrEmpty(Permissions))
{
// populate permissions
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
{
int index = _permissions.FindIndex(item => item.EntityName == permissionstring.EntityName && item.PermissionName == permissionstring.PermissionName);
if (index != -1)
{
_permissions[index].Permissions = permissionstring.Permissions;
}
if (permissionstring.Permissions.Contains("["))
{
foreach (string user in permissionstring.Permissions.Split('[', StringSplitOptions.RemoveEmptyEntries))
{
if (user.Contains("]"))
{
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
{
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
}
}
}
}
}
}
}
private string GetPermissionName(PermissionString permission)
{
var permissionname = Localizer[permission.PermissionName].ToString();
if (!string.IsNullOrEmpty(EntityName))
{
permissionname += " " + Localizer[permission.EntityName].ToString();
}
return permissionname;
}
private bool? GetPermissionValue(string permissions, string securityKey)
{
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
{
return false; // deny permission
} }
else else
{ {
if ((";" + permissions + ";").Contains(";" + securityKey + ";")) _permissionnames = PermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
}
// initialize permissions
_permissions = new List<Permission>();
if (PermissionList != null && PermissionList.Any())
{
foreach (var permission in PermissionList)
{ {
return true; // grant permission _permissions.Add(permission);
if (permission.UserId != null)
{
if (!_users.Any(item => item.UserId == permission.UserId.Value))
{
_users.Add(await UserService.GetUserAsync(permission.UserId.Value, ModuleState.SiteId));
}
}
} }
else }
else
{
foreach (string permissionname in _permissionnames)
{ {
return null; // not specified // permission names can be in the form of "EntityName:PermissionName:Roles"
if (permissionname.Contains(":"))
{
var segments = permissionname.Split(':');
if (segments.Length == 3)
{
foreach (var role in segments[2].Split(';'))
{
_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], role, null, true));
}
// ensure admin access
if (!_permissions.Any(item => item.EntityName == segments[0] && item.PermissionName == segments[1] && item.RoleName == RoleNames.Admin))
{
_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], RoleNames.Admin, null, true));
}
}
}
else
{
_permissions.Add(new Permission(ModuleState.SiteId, EntityName, permissionname, RoleNames.Admin, null, true));
}
} }
} }
} }
private bool GetPermissionDisabled(string entityName, string permissionName, string roleName) private string GetPermissionName(string permissionName)
{
return (permissionName.Contains(":")) ? permissionName.Split(':')[1] : permissionName;
}
private string GetEntityName(string permissionName)
{
return (permissionName.Contains(":")) ? permissionName.Split(':')[0] : EntityName;
}
private string DisplayPermissionName(string permissionName)
{
var name = Localizer[GetPermissionName(permissionName)].ToString();
name += " " + Localizer[GetEntityName(permissionName)].ToString();
return name;
}
private bool? GetPermissionValue(string permissionName, string roleName, int userId)
{
bool? isauthorized = null;
if (roleName != "")
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
if (permission != null)
{
isauthorized = permission.IsAuthorized;
}
}
else
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
if (permission != null)
{
isauthorized = permission.IsAuthorized;
}
}
return isauthorized;
}
private bool GetPermissionDisabled(string permissionName, string roleName)
{ {
if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
@ -216,7 +228,7 @@
} }
else else
{ {
if (entityName != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) if (GetEntityName(permissionName) != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{ {
return true; return true;
} }
@ -227,6 +239,34 @@
} }
} }
private void PermissionChanged(bool? value, string permissionName, string roleName, int userId)
{
if (roleName != "")
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
if (permission != null)
{
_permissions.Remove(permission);
}
if (value != null)
{
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), roleName, null, value.Value));
}
}
else
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
if (permission != null)
{
_permissions.Remove(permission);
}
if (value != null)
{
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), null, userId, value.Value));
}
}
}
private async Task<Dictionary<string, string>> GetUsers(string filter) private async Task<Dictionary<string, string>> GetUsers(string filter)
{ {
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered); var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
@ -251,62 +291,45 @@
_user.Clear(); _user.Clear();
} }
private void PermissionChanged(bool? value, string entityName, string permissionName, string securityId)
{
var selected = value;
int index = _permissions.FindIndex(item => item.EntityName == entityName && item.PermissionName == permissionName);
if (index != -1)
{
var permission = _permissions[index];
var ids = permission.Permissions.Split(';').ToList();
ids.Remove(securityId); // remove grant permission
ids.Remove("!" + securityId); // remove deny permission
switch (selected)
{
case true:
ids.Add(securityId); // add grant permission
break;
case false:
ids.Add("!" + securityId); // add deny permission
break;
case null:
break; // permission not specified
}
_permissions[index].Permissions = string.Join(";", ids.ToArray());
}
}
public string GetPermissions() public string GetPermissions()
{ {
ValidatePermissions(); ValidatePermissions();
return UserSecurity.SetPermissionStrings(_permissions); return JsonSerializer.Serialize(_permissions);
}
public List<Permission> GetPermissionList()
{
ValidatePermissions();
return _permissions;
} }
private void ValidatePermissions() private void ValidatePermissions()
{ {
PermissionString permission; // remove deny all users, unauthenticated, and registered users
for (int index = 0; index < _permissions.Count; index++) var permissions = _permissions.Where(item => !item.IsAuthorized &&
(item.RoleName == RoleNames.Everyone || item.RoleName == RoleNames.Unauthenticated || item.RoleName == RoleNames.Registered)).ToList();
foreach (var permission in permissions)
{ {
permission = _permissions[index]; _permissions.Remove(permission);
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList(); }
ids.Remove("!" + RoleNames.Everyone); // remove deny all users if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated {
ids.Remove("!" + RoleNames.Registered); // remove deny registered users // remove deny administrators and host users
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) permissions = _permissions.Where(item => !item.IsAuthorized &&
(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)).ToList();
foreach (var permission in permissions)
{ {
ids.Remove("!" + RoleNames.Admin); // remove deny administrators _permissions.Remove(permission);
ids.Remove("!" + RoleNames.Host); // remove deny host users }
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin)) foreach (var permissionname in _permissionnames)
{
// add administrators role if neither host or administrator is assigned
if (!_permissions.Any(item => item.EntityName == GetEntityName(permissionname) && item.PermissionName == GetPermissionName(permissionname) &&
(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)))
{ {
// add administrators role if host user role is not assigned _permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionname), GetPermissionName(permissionname), RoleNames.Admin, null, true));
ids.Add(RoleNames.Admin);
} }
} }
permission.Permissions = string.Join(";", ids.ToArray()); }
_permissions[index] = permission;
}
} }
} }

View File

@ -93,10 +93,10 @@
authorized = true; authorized = true;
break; break;
case SecurityAccessLevel.View: case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.PermissionList);
break; break;
case SecurityAccessLevel.Edit: case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList);
break; break;
case SecurityAccessLevel.Admin: case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin); authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);

View File

@ -190,12 +190,7 @@ namespace Oqtane.Modules
public string AddUrlParameters(params object[] parameters) public string AddUrlParameters(params object[] parameters)
{ {
var url = ""; return Utilities.AddUrlParameters(parameters);
for (var i = 0; i < parameters.Length; i++)
{
url += "/" + parameters[i].ToString();
}
return url;
} }
// template is in the form of a standard route template ie. "/{id}/{name}" and produces dictionary of key/value pairs // template is in the form of a standard route template ie. "/{id}/{name}" and produces dictionary of key/value pairs

View File

@ -5,7 +5,7 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RazorLangVersion>3.0</RazorLangVersion> <RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>

View File

@ -172,7 +172,7 @@
<value>Enter a complete connection string including all parameters and delimiters</value> <value>Enter a complete connection string including all parameters and delimiters</value>
</data> </data>
<data name="ConnectionString.Text" xml:space="preserve"> <data name="ConnectionString.Text" xml:space="preserve">
<value>String:</value> <value>Settings:</value>
</data> </data>
<data name="EnterConnectionParameters" xml:space="preserve"> <data name="EnterConnectionParameters" xml:space="preserve">
<value>Enter Connection Parameters</value> <value>Enter Connection Parameters</value>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -193,6 +193,6 @@
<value>Execute Once</value> <value>Execute Once</value>
</data> </data>
<data name="Message.NoJobs" xml:space="preserve"> <data name="Message.NoJobs" xml:space="preserve">
<value>Please Note That After An Initial Installation You Must &amp;lt;a href={0}&amp;gt;Restart&amp;lt;/a&amp;gt; The Application In Order To Activate The Default Scheduled Jobs.</value> <value>Please Note That After An Initial Installation You Must &lt;a href={0}&gt;Restart&lt;/a&gt; The Application In Order To Activate The Default Scheduled Jobs.</value>
</data> </data>
</root> </root>

View File

@ -163,7 +163,7 @@
<value>Enter the site name</value> <value>Enter the site name</value>
</data> </data>
<data name="Tenant.HelpText" xml:space="preserve"> <data name="Tenant.HelpText" xml:space="preserve">
<value>Enter the tenant for the site</value> <value>The name of the database used for the site</value>
</data> </data>
<data name="Aliases.HelpText" xml:space="preserve"> <data name="Aliases.HelpText" xml:space="preserve">
<value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder).</value> <value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder).</value>
@ -214,7 +214,7 @@
<value>Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.</value> <value>Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.</value>
</data> </data>
<data name="Tenant.Text" xml:space="preserve"> <data name="Tenant.Text" xml:space="preserve">
<value>Tenant: </value> <value>Database: </value>
</data> </data>
<data name="Aliases.Text" xml:space="preserve"> <data name="Aliases.Text" xml:space="preserve">
<value>Aliases: </value> <value>Aliases: </value>
@ -292,7 +292,7 @@
<value>Browse</value> <value>Browse</value>
</data> </data>
<data name="TenantInformation.Heading" xml:space="preserve"> <data name="TenantInformation.Heading" xml:space="preserve">
<value>Tenant Information</value> <value>Database</value>
</data> </data>
<data name="PWASettings.Heading" xml:space="preserve"> <data name="PWASettings.Heading" xml:space="preserve">
<value>PWA Settings</value> <value>PWA Settings</value>
@ -304,13 +304,13 @@
<value>Connection:</value> <value>Connection:</value>
</data> </data>
<data name="Database.Text" xml:space="preserve"> <data name="Database.Text" xml:space="preserve">
<value>Database:</value> <value>Type:</value>
</data> </data>
<data name="ConnectionString.HelpText" xml:space="preserve"> <data name="ConnectionString.HelpText" xml:space="preserve">
<value>The connection information for the database</value> <value>The connection information for the database</value>
</data> </data>
<data name="Database.HelpText" xml:space="preserve"> <data name="Database.HelpText" xml:space="preserve">
<value>The database for the tenant</value> <value>The type of database</value>
</data> </data>
<data name="DeleteSite.Text" xml:space="preserve"> <data name="DeleteSite.Text" xml:space="preserve">
<value>Delete Site</value> <value>Delete Site</value>
@ -345,4 +345,10 @@
<data name="SmtpRelay.Text" xml:space="preserve"> <data name="SmtpRelay.Text" xml:space="preserve">
<value>Relay Configured?</value> <value>Relay Configured?</value>
</data> </data>
<data name="SiteMap.HelpText" xml:space="preserve">
<value>The site map url for this site which can be submitted to search engines for indexing</value>
</data>
<data name="SiteMap.Text" xml:space="preserve">
<value>Site Map:</value>
</data>
</root> </root>

View File

@ -123,9 +123,6 @@
<data name="SqlServer" xml:space="preserve"> <data name="SqlServer" xml:space="preserve">
<value>SQL Server</value> <value>SQL Server</value>
</data> </data>
<data name="Server.Text" xml:space="preserve">
<value>Server: </value>
</data>
<data name="Container.Select" xml:space="preserve"> <data name="Container.Select" xml:space="preserve">
<value>Select Container</value> <value>Select Container</value>
</data> </data>
@ -145,7 +142,7 @@
<value>Select the default container for the site</value> <value>Select the default container for the site</value>
</data> </data>
<data name="Tenant.Text" xml:space="preserve"> <data name="Tenant.Text" xml:space="preserve">
<value>Tenant: </value> <value>Database: </value>
</data> </data>
<data name="Aliases.Text" xml:space="preserve"> <data name="Aliases.Text" xml:space="preserve">
<value>Aliases: </value> <value>Aliases: </value>
@ -157,10 +154,10 @@
<value>Select Site Template</value> <value>Select Site Template</value>
</data> </data>
<data name="Tenant.Select" xml:space="preserve"> <data name="Tenant.Select" xml:space="preserve">
<value>Select Tenant</value> <value>Select Database</value>
</data> </data>
<data name="Tenant.Add" xml:space="preserve"> <data name="Tenant.Add" xml:space="preserve">
<value>Create New Tenant</value> <value>Create Database</value>
</data> </data>
<data name="Error.Theme.LoadContainers" xml:space="preserve"> <data name="Error.Theme.LoadContainers" xml:space="preserve">
<value>Error Loading Containers For Theme</value> <value>Error Loading Containers For Theme</value>
@ -172,19 +169,19 @@
<value>Invalid Host Password</value> <value>Invalid Host Password</value>
</data> </data>
<data name="Error.TenantName.Exists" xml:space="preserve"> <data name="Error.TenantName.Exists" xml:space="preserve">
<value>Tenant Name Is Missing Or Already Exists</value> <value>Database Name Is Missing Or Already Exists</value>
</data> </data>
<data name="Message.SiteName.InUse" xml:space="preserve"> <data name="Message.SiteName.InUse" xml:space="preserve">
<value>{0} Already Used For Another Site</value> <value>{0} Already Used For Another Site</value>
</data> </data>
<data name="Message.Required.Tenant" xml:space="preserve"> <data name="Message.Required.Tenant" xml:space="preserve">
<value>You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template</value> <value>You Must Provide A Database, Site Name, Alias, Default Theme/Container, And Site Template</value>
</data> </data>
<data name="Name.HelpText" xml:space="preserve"> <data name="Name.HelpText" xml:space="preserve">
<value>Enter the name of the site</value> <value>Enter the name of the site</value>
</data> </data>
<data name="DefaultTheme.HelpText" xml:space="preserve"> <data name="DefaultTheme.HelpText" xml:space="preserve">
<value>Select the default theme for the website</value> <value>Select the default theme for the site</value>
</data> </data>
<data name="AdminContainer.HelpText" xml:space="preserve"> <data name="AdminContainer.HelpText" xml:space="preserve">
<value>Select the admin container for the site</value> <value>Select the admin container for the site</value>
@ -193,28 +190,13 @@
<value>Select the site template</value> <value>Select the site template</value>
</data> </data>
<data name="Tenant.HelpText" xml:space="preserve"> <data name="Tenant.HelpText" xml:space="preserve">
<value>Select the tenant for the site</value> <value>Select the database for the site</value>
</data> </data>
<data name="TenantName.HelpText" xml:space="preserve"> <data name="TenantName.HelpText" xml:space="preserve">
<value>Enter the name for the tenant</value> <value>Enter the name for the database</value>
</data> </data>
<data name="DatabaseType.HelpText" xml:space="preserve"> <data name="DatabaseType.HelpText" xml:space="preserve">
<value>Select the database type for the tenant</value> <value>Select the database type</value>
</data>
<data name="DatabaseServer.HelpText" xml:space="preserve">
<value>Enter the server for the tenant</value>
</data>
<data name="Database.HelpText" xml:space="preserve">
<value>Enter the database for the tenant</value>
</data>
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
<value>Select if you want integrated security or not</value>
</data>
<data name="DatabaseUsername.HelpText" xml:space="preserve">
<value>Enter the username for the integrated security</value>
</data>
<data name="DatabasePassword.HelpText" xml:space="preserve">
<value>Enter the password for the integrated security</value>
</data> </data>
<data name="HostUsername.HelpText" xml:space="preserve"> <data name="HostUsername.HelpText" xml:space="preserve">
<value>Enter the username of an existing host user</value> <value>Enter the username of an existing host user</value>
@ -232,23 +214,14 @@
<value>Site Template: </value> <value>Site Template: </value>
</data> </data>
<data name="TenantName.Text" xml:space="preserve"> <data name="TenantName.Text" xml:space="preserve">
<value>Tenant Name: </value> <value>Name: </value>
</data> </data>
<data name="DatabaseType.Text" xml:space="preserve"> <data name="DatabaseType.Text" xml:space="preserve">
<value>Database Type: </value> <value>Type: </value>
</data> </data>
<data name="Database.Text" xml:space="preserve"> <data name="Database.Text" xml:space="preserve">
<value>Database: </value> <value>Database: </value>
</data> </data>
<data name="IntegratedSecurity.Text" xml:space="preserve">
<value>Integrated Security: </value>
</data>
<data name="DatabaseUsername.Text" xml:space="preserve">
<value>Database Username: </value>
</data>
<data name="DatabasePassword.Text" xml:space="preserve">
<value>Database Password: </value>
</data>
<data name="HostUsername.Text" xml:space="preserve"> <data name="HostUsername.Text" xml:space="preserve">
<value>Host Username:</value> <value>Host Username:</value>
</data> </data>
@ -274,7 +247,7 @@
<value>Enter a complete connection string including all parameters and delimiters</value> <value>Enter a complete connection string including all parameters and delimiters</value>
</data> </data>
<data name="ConnectionString.Text" xml:space="preserve"> <data name="ConnectionString.Text" xml:space="preserve">
<value>String:</value> <value>Settings:</value>
</data> </data>
<data name="EnterConnectionParameters" xml:space="preserve"> <data name="EnterConnectionParameters" xml:space="preserve">
<value>Enter Connection Parameters</value> <value>Enter Connection Parameters</value>

View File

@ -117,30 +117,75 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Connection.Text" xml:space="preserve">
<value>Connection: </value>
</data>
<data name="Connection.HelpText" xml:space="preserve">
<value>Select a database connection (from appsettings.json)</value>
</data>
<data name="Connection.Select" xml:space="preserve">
<value>Select Connection</value>
</data>
<data name="Connection.Add" xml:space="preserve">
<value>Add Connection</value>
</data>
<data name="Name.Text" xml:space="preserve">
<value>Name: </value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name of the connection</value>
</data>
<data name="DatabaseType.Text" xml:space="preserve">
<value>Type: </value>
</data>
<data name="DatabaseType.HelpText" xml:space="preserve">
<value>Select the database type</value>
</data>
<data name="Type.Select" xml:space="preserve">
<value>Select Type</value>
</data>
<data name="EnterConnectionParameters" xml:space="preserve">
<value>Enter Connection Parameters</value>
</data>
<data name="EnterConnectionString" xml:space="preserve">
<value>Enter Connection String</value>
</data>
<data name="ConnectionString.Text" xml:space="preserve">
<value>Settings: </value>
</data>
<data name="ConnectionString.HelpText" xml:space="preserve">
<value>A complete connection string including all parameters and delimiters</value>
</data>
<data name="Add" xml:space="preserve">
<value>Add</value>
</data>
<data name="Tenant.Text" xml:space="preserve"> <data name="Tenant.Text" xml:space="preserve">
<value>Tenant: </value> <value>Database: </value>
</data>
<data name="Tenant.Select" xml:space="preserve">
<value>Select Tenant</value>
</data>
<data name="Execute" xml:space="preserve">
<value>Execute</value>
</data>
<data name="Message.Required.Tenant" xml:space="preserve">
<value>You Must Select A Tenant And Provide A Valid SQL Query</value>
</data>
<data name="Return.NoResult" xml:space="preserve">
<value>No Results Returned</value>
</data> </data>
<data name="Tenant.HelpText" xml:space="preserve"> <data name="Tenant.HelpText" xml:space="preserve">
<value>Select the tenant associated with the database server</value> <value>The database using this connection</value>
</data>
<data name="SqlQuery.HelpText" xml:space="preserve">
<value>Enter the SQL query for the database server</value>
</data> </data>
<data name="SqlQuery.Text" xml:space="preserve"> <data name="SqlQuery.Text" xml:space="preserve">
<value>SQL Query: </value> <value>SQL Query: </value>
</data> </data>
<data name="SqlQuery.HelpText" xml:space="preserve">
<value>Enter a valid SQL query for the database</value>
</data>
<data name="Execute" xml:space="preserve">
<value>Execute</value>
</data>
<data name="Message.Required.Tenant" xml:space="preserve">
<value>You Must Select A Database Type And Provide A Valid SQL Query</value>
</data>
<data name="Message.Required.Connection" xml:space="preserve">
<value>You Must Provide A Connection Name And Settings</value>
</data>
<data name="Message.Connection.Added" xml:space="preserve">
<value>Connection Added Successfully</value>
</data>
<data name="Return.NoResult" xml:space="preserve">
<value>No Results Returned</value>
</data>
<data name="Success.QueryExecuted" xml:space="preserve"> <data name="Success.QueryExecuted" xml:space="preserve">
<value>SQL Query Executed</value> <value>SQL Query Executed</value>
</data> </data>

View File

@ -285,4 +285,13 @@
<data name="Log.HelpText" xml:space="preserve"> <data name="Log.HelpText" xml:space="preserve">
<value>System log information for current day</value> <value>System log information for current day</value>
</data> </data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
</data>
<data name="Success.ClearLog" xml:space="preserve">
<value>System Log Has Been Successfully Cleared</value>
</data>
<data name="Error.ClearLog" xml:space="preserve">
<value>Ann Error Occurred Clearing The System Log</value>
</data>
</root> </root>

View File

@ -387,7 +387,13 @@
<data name="RoleClaimType.HelpText" xml:space="preserve"> <data name="RoleClaimType.HelpText" xml:space="preserve">
<value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value> <value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
</data> </data>
<data name="RoleClaimType.Text" xml:space="preserve"> <data name="RoleClaimType.Text" xml:space="preserve">
<value>Role Claim Type:</value> <value>Role Claim:</value>
</data>
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
<value>Optionally provide a comma delimited list of user profile claims provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
</data>
<data name="ProfileClaimTypes.Text" xml:space="preserve">
<value>User Profile Claims:</value>
</data> </data>
</root> </root>

View File

@ -62,7 +62,7 @@ namespace Oqtane.Services
/// <summary> /// <summary>
/// Returns a key-value dictionary of all page module settings for the given page module /// Returns a key-value dictionary of all page module settings for the given page module
/// </summary> /// </summary>
/// <param name="pageId"></param> /// <param name="pageModuleId"></param>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<string, string>> GetPageModuleSettingsAsync(int pageModuleId); Task<Dictionary<string, string>> GetPageModuleSettingsAsync(int pageModuleId);
@ -107,7 +107,7 @@ namespace Oqtane.Services
/// <summary> /// <summary>
/// Returns a key-value dictionary of all user settings for the given user /// Returns a key-value dictionary of all user settings for the given user
/// </summary> /// </summary>
/// <param name="pageId"></param> /// <param name="userId"></param>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<string, string>> GetUserSettingsAsync(int userId); Task<Dictionary<string, string>> GetUserSettingsAsync(int userId);
@ -122,7 +122,7 @@ namespace Oqtane.Services
/// <summary> /// <summary>
/// Returns a key-value dictionary of all folder settings for the given folder /// Returns a key-value dictionary of all folder settings for the given folder
/// </summary> /// </summary>
/// <param name="pageId"></param> /// <param name="folderId"></param>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<string, string>> GetFolderSettingsAsync(int folderId); Task<Dictionary<string, string>> GetFolderSettingsAsync(int folderId);
@ -148,6 +148,21 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task UpdateHostSettingsAsync(Dictionary<string, string> hostSettings); Task UpdateHostSettingsAsync(Dictionary<string, string> hostSettings);
/// <summary>
/// Returns a key-value dictionary of all settings for the given visitor
/// </summary>
/// <param name="visitorId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetVisitorSettingsAsync(int visitorId);
/// <summary>
/// Updates a visitor setting
/// </summary>
/// <param name="visitorSettings"></param>
/// <param name="visitorId"></param>
/// <returns></returns>
Task UpdateVisitorSettingsAsync(Dictionary<string, string> visitorSettings, int visitorId);
/// <summary> /// <summary>
/// Returns a key-value dictionary of all settings for the given entityName /// Returns a key-value dictionary of all settings for the given entityName
/// </summary> /// </summary>

View File

@ -32,11 +32,5 @@ namespace Oqtane.Services
/// <param name="settings"></param> /// <param name="settings"></param>
/// <returns></returns> /// <returns></returns>
Task UpdateSystemInfoAsync(Dictionary<string, object> settings); Task UpdateSystemInfoAsync(Dictionary<string, object> settings);
/// <summary>
/// updates a config value
/// </summary>
/// <returns></returns>
Task UpdateSystemInfoAsync(string settingKey, object settingValue);
} }
} }

View File

@ -111,14 +111,36 @@ namespace Oqtane.Services
await UpdateSettingsAsync(hostSettings, EntityNames.Host, -1); await UpdateSettingsAsync(hostSettings, EntityNames.Host, -1);
} }
public async Task<Dictionary<string, string>> GetVisitorSettingsAsync(int visitorId)
{
if (visitorId != -1)
{
return await GetSettingsAsync(EntityNames.Visitor, visitorId);
}
else
{
return new Dictionary<string, string>();
}
}
public async Task UpdateVisitorSettingsAsync(Dictionary<string, string> visitorSettings, int visitorId)
{
if (visitorId != -1)
{
await UpdateSettingsAsync(visitorSettings, EntityNames.Visitor, visitorId);
}
}
public async Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId) public async Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId)
{ {
var dictionary = new Dictionary<string, string>(); var dictionary = new Dictionary<string, string>();
var settings = await GetJsonAsync<List<Setting>>($"{Apiurl}?entityname={entityName}&entityid={entityId}"); var settings = await GetJsonAsync<List<Setting>>($"{Apiurl}?entityname={entityName}&entityid={entityId}");
if (settings != null)
foreach(Setting setting in settings.OrderBy(item => item.SettingName).ToList())
{ {
dictionary.Add(setting.SettingName, setting.SettingValue); foreach (Setting setting in settings.OrderBy(item => item.SettingName).ToList())
{
dictionary.Add(setting.SettingName, setting.SettingValue);
}
} }
return dictionary; return dictionary;
} }

View File

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Documentation; using Oqtane.Documentation;
using Oqtane.Shared; using Oqtane.Shared;
using System.Net;
namespace Oqtane.Services namespace Oqtane.Services
{ {
@ -32,9 +33,5 @@ namespace Oqtane.Services
{ {
await PostJsonAsync(Apiurl, settings); await PostJsonAsync(Apiurl, settings);
} }
public async Task UpdateSystemInfoAsync(string settingKey, object settingValue)
{
await PutJsonAsync($"{Apiurl}/{settingKey}/{settingValue}", "");
}
} }
} }

View File

@ -2,7 +2,7 @@
@inherits ModuleActionsBase @inherits ModuleActionsBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction) @if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
{ {
<div class="app-moduleactions py-2 px-3"> <div class="app-moduleactions py-2 px-3">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a> <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>

View File

@ -30,11 +30,11 @@ namespace Oqtane.Themes.Controls
{ {
var actionList = new List<ActionViewModel>(); var actionList = new List<ActionViewModel>();
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
actionList.Add(new ActionViewModel { Icon = Icons.Cog, 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.ContainsRole(ModuleState.Permissions, PermissionNames.View, RoleNames.Everyone)) if (UserSecurity.ContainsRole(ModuleState.PermissionList, PermissionNames.View, RoleNames.Everyone))
{ {
actionList.Add(new ActionViewModel { Icon = Icons.CircleX, 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) });
} }
@ -44,7 +44,7 @@ namespace Oqtane.Themes.Controls
} }
actionList.Add(new ActionViewModel { Icon = Icons.Trash, Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) }); actionList.Add(new ActionViewModel { Icon = Icons.Trash, Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "") if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.IsPortable)
{ {
actionList.Add(new ActionViewModel { Name = "" }); actionList.Add(new ActionViewModel { Name = "" });
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.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import") });
@ -93,7 +93,7 @@ namespace Oqtane.Themes.Controls
protected async Task ModuleAction(ActionViewModel action) protected async Task ModuleAction(ActionViewModel action)
{ {
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions)) if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList))
{ {
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
@ -136,36 +136,32 @@ namespace Oqtane.Themes.Controls
private async Task<string> Publish(string url, PageModule pagemodule) private async Task<string> Publish(string url, PageModule pagemodule)
{ {
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions); var permissions = pagemodule.Module.PermissionList;
foreach (var permissionstring in permissions) if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
{ {
if (permissionstring.PermissionName == PermissionNames.View) permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
} }
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions); if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
{
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Registered, null, true));
}
pagemodule.Module.PermissionList = permissions;
await ModuleService.UpdateModuleAsync(pagemodule.Module); await ModuleService.UpdateModuleAsync(pagemodule.Module);
return url; return url;
} }
private async Task<string> Unpublish(string url, PageModule pagemodule) private async Task<string> Unpublish(string url, PageModule pagemodule)
{ {
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions); var permissions = pagemodule.Module.PermissionList;
foreach (var permissionstring in permissions) if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
{ {
if (permissionstring.PermissionName == PermissionNames.View) permissions.Remove(permissions.First(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone));
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
} }
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions); if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
{
permissions.Remove(permissions.First(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered));
}
pagemodule.Module.PermissionList = permissions;
await ModuleService.UpdateModuleAsync(pagemodule.Module); await ModuleService.UpdateModuleAsync(pagemodule.Module);
return url; return url;
} }

View File

@ -33,7 +33,7 @@
} }
} }
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) @if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
<button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel"> <button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
<span class="oi oi-cog"></span> <span class="oi oi-cog"></span>
@ -71,7 +71,7 @@
</div> </div>
<div class="row d-flex"> <div class="row d-flex">
<div class="col"> <div class="col">
@if (UserSecurity.ContainsRole(PageState.Page.Permissions, PermissionNames.View, RoleNames.Everyone)) @if (UserSecurity.ContainsRole(PageState.Page.PermissionList, PermissionNames.View, RoleNames.Everyone))
{ {
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button> <button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
} }
@ -107,7 +107,7 @@
} }
} }
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) @if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
@ -144,7 +144,7 @@
} }
@foreach (var moduledefinition in _moduleDefinitions) @foreach (var moduledefinition in _moduleDefinitions)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.PermissionList))
{ {
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString())) if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{ {
@ -294,14 +294,14 @@
{ {
_canViewAdminDashboard = CanViewAdminDashboard(); _canViewAdminDashboard = CanViewAdminDashboard();
_showEditMode = false; _showEditMode = false;
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
_showEditMode = true; _showEditMode = true;
_pages?.Clear(); _pages?.Clear();
foreach (Page p in PageState.Pages) foreach (Page p in PageState.Pages)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_pages.Add(p); _pages.Add(p);
} }
@ -319,7 +319,7 @@
{ {
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId)) foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.PermissionList))
{ {
_showEditMode = true; _showEditMode = true;
break; break;
@ -335,7 +335,7 @@
{ {
foreach (var page in PageState.Pages.Where(item => item.ParentId == admin?.PageId)) foreach (var page in PageState.Pages.Where(item => item.ParentId == admin?.PageId))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
{ {
return true; return true;
} }
@ -371,7 +371,7 @@
{ {
_modules = PageState.Modules _modules = PageState.Modules
.Where(module => module.PageId == int.Parse(PageId) && .Where(module => module.PageId == int.Parse(PageId) &&
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
.ToList(); .ToList();
} }
ModuleId = "-"; ModuleId = "-";
@ -380,7 +380,7 @@
private async Task AddModule() private async Task AddModule()
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
if ((ModuleType == "new" && ModuleDefinitionName != "-") || (ModuleType != "new" && ModuleId != "-")) if ((ModuleType == "new" && ModuleDefinitionName != "-") || (ModuleType != "new" && ModuleId != "-"))
{ {
@ -392,20 +392,20 @@
module.ModuleDefinitionName = ModuleDefinitionName; module.ModuleDefinitionName = ModuleDefinitionName;
module.AllPages = false; module.AllPages = false;
List<PermissionString> permissions = UserSecurity.GetPermissionStrings(PageState.Page.Permissions); var permissions = new List<Permission>();
if (Visibility == "view") if (Visibility == "view")
{ {
// set module view permissions to page view permissions // set module view permissions to page view permissions
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions; permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.View);
} }
else else
{ {
// set module view permissions to page edit permissions // set module view permissions to page edit permissions
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.Edit).Permissions; permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.Edit);
} }
// set entityname // set module edit permissions to page edit permissions
permissions.ForEach(item => item.EntityName = EntityNames.Module); permissions = SetPermissions(permissions, module.SiteId, PermissionNames.Edit, PermissionNames.Edit);
module.Permissions = UserSecurity.SetPermissionStrings(permissions); module.PermissionList = permissions;
module = await ModuleService.AddModuleAsync(module); module = await ModuleService.AddModuleAsync(module);
ModuleId = module.ModuleId.ToString(); ModuleId = module.ModuleId.ToString();
@ -456,6 +456,15 @@
} }
} }
private List<Permission> SetPermissions(List<Permission> permissions, int siteId, string modulePermission, string pagePermission)
{
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
{
permissions.Add(new Permission { SiteId = siteId, EntityName = EntityNames.Module, PermissionName = modulePermission, RoleId = permission.RoleId, UserId = permission.UserId, IsAuthorized = permission.IsAuthorized });
}
return permissions;
}
private async Task ToggleEditMode(bool EditMode) private async Task ToggleEditMode(bool EditMode)
{ {
if (_showEditMode) if (_showEditMode)
@ -469,7 +478,7 @@
PageState.EditMode = true; PageState.EditMode = true;
} }
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"))); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
} }
else else
{ {
@ -477,7 +486,7 @@
{ {
await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId); await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
PageState.EditMode = true; PageState.EditMode = true;
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"))); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
} }
} }
} }
@ -525,34 +534,19 @@
private async void Publish(string action) private async void Publish(string action)
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
List<PermissionString> permissions; var permissions = PageState.Page.PermissionList;
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
// publish/unpublish page {
var page = PageState.Page; permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
permissions = UserSecurity.GetPermissionStrings(page.Permissions); }
foreach (var permissionstring in permissions) if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
{ {
if (permissionstring.PermissionName == PermissionNames.View) permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
{ }
List<string> ids = permissionstring.Permissions.Split(';').ToList(); PageState.Page.PermissionList = permissions;
switch (action) await PageService.UpdatePageAsync(PageState.Page);
{
case "publish":
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
break;
case "unpublish":
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
break;
}
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
await PageService.UpdatePageAsync(page);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true)); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
} }
} }

View File

@ -1,8 +1,10 @@
using System; using System;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Models;
using Oqtane.Providers; using Oqtane.Providers;
using Oqtane.Security; using Oqtane.Security;
using Oqtane.Services; using Oqtane.Services;
@ -22,21 +24,19 @@ namespace Oqtane.Themes.Controls
protected void LoginUser() protected void LoginUser()
{ {
var returnurl = PageState.Alias.Path; Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
if (PageState.Page.Path != "/") NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
{
returnurl += "/" + PageState.Page.Path;
}
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
} }
protected async Task LogoutUser() protected async Task LogoutUser()
{ {
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username); await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username);
// check if anonymous user can access page Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
var url = PageState.Alias.Path + "/" + PageState.Page.Path; var url = route.PathAndQuery;
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
// verify if anonymous users can access page
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList))
{ {
url = PageState.Alias.Path; url = PageState.Alias.Path;
} }

View File

@ -32,7 +32,7 @@ namespace Oqtane.Themes.Controls
var securityLevel = int.MaxValue; var securityLevel = int.MaxValue;
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation)) foreach (Page p in PageState.Pages.Where(item => item.IsNavigation))
{ {
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions)) if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
securityLevel = int.MaxValue; securityLevel = int.MaxValue;
yield return p; yield return p;

View File

@ -43,7 +43,7 @@
container = (!string.IsNullOrEmpty(PageState.Site.AdminContainerType)) ? PageState.Site.AdminContainerType : Constants.DefaultAdminContainer; container = (!string.IsNullOrEmpty(PageState.Site.AdminContainerType)) ? PageState.Site.AdminContainerType : Constants.DefaultAdminContainer;
} }
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction) if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
{ {
_useadminborder = true; _useadminborder = true;
} }

View File

@ -30,7 +30,7 @@ else
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction) if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
{ {
_useadminborder = true; _useadminborder = true;
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>"; _panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
@ -67,7 +67,7 @@ else
bool authorized = false; bool authorized = false;
if (Constants.DefaultModuleActions.Contains(PageState.Action)) if (Constants.DefaultModuleActions.Contains(PageState.Action))
{ {
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList);
} }
else else
{ {
@ -77,10 +77,10 @@ else
authorized = true; authorized = true;
break; break;
case SecurityAccessLevel.View: case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList);
break; break;
case SecurityAccessLevel.Edit: case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.PermissionList);
break; break;
case SecurityAccessLevel.Admin: case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin); authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
@ -111,7 +111,7 @@ else
if (module != null && module.Pane.ToLower() == Name.ToLower()) if (module != null && module.Pane.ToLower() == Name.ToLower())
{ {
// check if user is authorized to view module // check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
{ {
CreateComponent(builder, module); CreateComponent(builder, module);
} }
@ -122,7 +122,7 @@ else
foreach (Module module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId && item.Pane.ToLower() == Name.ToLower()).OrderBy(x => x.Order).ToArray()) foreach (Module module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId && item.Pane.ToLower() == Name.ToLower()).OrderBy(x => x.Order).ToArray())
{ {
// check if user is authorized to view module // check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
{ {
CreateComponent(builder, module); CreateComponent(builder, module);
} }

View File

@ -124,6 +124,10 @@
editmode = PageState.EditMode; editmode = PageState.EditMode;
lastsyncdate = PageState.LastSyncDate; lastsyncdate = PageState.LastSyncDate;
} }
if (PageState?.Page.Path != route.PagePath)
{
editmode = false; // reset edit mode when navigating to different page
}
// get user // get user
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId) if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
@ -181,7 +185,6 @@
if (PageState == null || refresh || PageState.Page.Path != route.PagePath) if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
{ {
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase)); page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
editmode = false;
} }
else else
{ {
@ -204,7 +207,7 @@
if (page != null) if (page != null)
{ {
// check if user is authorized to view page // check if user is authorized to view page
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.Permissions)) if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList))
{ {
// load additional metadata for current page // load additional metadata for current page
page = await ProcessPage(page, site, user); page = await ProcessPage(page, site, user);
@ -250,7 +253,7 @@
if (user == null) if (user == null)
{ {
// redirect to login page if user not logged in as they may need to be authenticated // redirect to login page if user not logged in as they may need to be authenticated
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + route.AbsolutePath)); NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
} }
else else
{ {
@ -299,18 +302,24 @@
{ {
query = query.Substring(1); // ignore "?" query = query.Substring(1); // ignore "?"
} }
foreach (string kvp in query.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
{ {
if (kvp != "") if (kvp != "")
{ {
if (kvp.Contains("=")) if (kvp.Contains("="))
{ {
string[] pair = kvp.Split('='); string[] pair = kvp.Split('=');
querystring.Add(pair[0], pair[1]); if (!querystring.ContainsKey(pair[0]))
{
querystring.Add(pair[0], pair[1]);
}
} }
else else
{ {
querystring.Add(kvp, "true"); // default parameter when no value is provided if (!querystring.ContainsKey(kvp))
{
querystring.Add(kvp, "true"); // default parameter when no value is provided
}
} }
} }
} }
@ -358,7 +367,7 @@
} }
if (!string.IsNullOrEmpty(panes)) if (!string.IsNullOrEmpty(panes))
{ {
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); page.Panes = panes.Replace(";", ",").Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
if (!page.Panes.Contains(PaneNames.Default) && !page.Panes.Contains(PaneNames.Admin)) if (!page.Panes.Contains(PaneNames.Default) && !page.Panes.Contains(PaneNames.Admin))
{ {
_error = "The Current Theme Does Not Contain A Default Or Admin Pane"; _error = "The Current Theme Does Not Contain A Default Or Admin Pane";
@ -407,7 +416,7 @@
// check if the module defines custom action routes // check if the module defines custom action routes
if (module.ModuleDefinition.ControlTypeRoutes != "") if (module.ModuleDefinition.ControlTypeRoutes != "")
{ {
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
{ {
if (route.StartsWith(action + "=")) if (route.StartsWith(action + "="))
{ {

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.MySQL</id> <id>Oqtane.Database.MySQL</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane MySQL Provider</title> <title>Oqtane MySQL Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.PostgreSQL</id> <id>Oqtane.Database.PostgreSQL</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane PostgreSQL Provider</title> <title>Oqtane PostgreSQL Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.SqlServer</id> <id>Oqtane.Database.SqlServer</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane SQL Server Provider</title> <title>Oqtane SQL Server Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.Sqlite</id> <id>Oqtane.Database.Sqlite</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane SQLite Provider</title> <title>Oqtane SQLite Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -6,7 +6,7 @@
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET --> <!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> --> <!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -14,7 +14,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane.Maui</RootNamespace> <RootNamespace>Oqtane.Maui</RootNamespace>
@ -31,7 +31,7 @@
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid> <ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
<!-- Versions --> <!-- Versions -->
<ApplicationDisplayVersion>3.3.1</ApplicationDisplayVersion> <ApplicationDisplayVersion>3.4.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion> <ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
@ -71,8 +71,8 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" /> <PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" /> <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
<PackageReference Include="Oqtane.Client" Version="3.3.1" /> <PackageReference Include="Oqtane.Client" Version="3.4.0" />
<PackageReference Include="Oqtane.Shared" Version="3.3.1" /> <PackageReference Include="Oqtane.Shared" Version="3.4.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Client</id> <id>Oqtane.Client</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Framework</id> <id>Oqtane.Framework</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -11,8 +11,8 @@
<copyright>.NET Foundation</copyright> <copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.3.1/Oqtane.Framework.3.3.1.Upgrade.zip</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.4.0/Oqtane.Framework.3.4.0.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane framework</tags> <tags>oqtane framework</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Server</id> <id>Oqtane.Server</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Shared</id> <id>Oqtane.Shared</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Updater</id> <id>Oqtane.Updater</id>
<version>3.3.1</version> <version>3.4.0</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.3.1.Install.zip" -Force Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.4.0.Install.zip" -Force

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.3.1.Upgrade.zip" -Force Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.4.0.Upgrade.zip" -Force

View File

@ -20,7 +20,6 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using System.Net.Http; using System.Net.Http;
using Oqtane.Migrations.Tenant;
// ReSharper disable StringIndexOfIsCultureSpecific.1 // ReSharper disable StringIndexOfIsCultureSpecific.1
@ -57,7 +56,7 @@ namespace Oqtane.Controllers
if (int.TryParse(folder, out folderid)) if (int.TryParse(folder, out folderid))
{ {
Folder Folder = _folders.GetFolder(folderid); Folder Folder = _folders.GetFolder(folderid);
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, Folder.Permissions)) if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, Folder.PermissionList))
{ {
files = _files.GetFiles(folderid).ToList(); files = _files.GetFiles(folderid).ToList();
} }
@ -99,7 +98,7 @@ namespace Oqtane.Controllers
List<Models.File> files; List<Models.File> files;
Folder folder = _folders.GetFolder(siteId, WebUtility.UrlDecode(path)); Folder folder = _folders.GetFolder(siteId, WebUtility.UrlDecode(path));
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions)) if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{ {
files = _files.GetFiles(folder.FolderId).ToList(); files = _files.GetFiles(folder.FolderId).ToList();
} }
@ -118,7 +117,7 @@ namespace Oqtane.Controllers
public Models.File Get(int id) public Models.File Get(int id)
{ {
Models.File file = _files.GetFile(id); Models.File file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{ {
return file; return file;
} }
@ -142,7 +141,7 @@ namespace Oqtane.Controllers
{ {
if (File.Name != file.Name || File.FolderId != file.FolderId) if (File.Name != file.Name || File.FolderId != file.FolderId)
{ {
file.Folder = _folders.GetFolder(file.FolderId); file.Folder = _folders.GetFolder(file.FolderId, false);
string folderpath = _folders.GetFolderPath(file.Folder); string folderpath = _folders.GetFolderPath(file.Folder);
if (!Directory.Exists(folderpath)) if (!Directory.Exists(folderpath))
{ {
@ -151,7 +150,7 @@ namespace Oqtane.Controllers
System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name)); System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name));
} }
var newfile = CreateFile(file.Name, file.Folder.FolderId, _files.GetFilePath(file)); var newfile = CreateFile(File.Name, file.Folder.FolderId, _files.GetFilePath(file));
if (newfile != null) if (newfile != null)
{ {
file.Extension = newfile.Extension; file.Extension = newfile.Extension;
@ -216,7 +215,7 @@ namespace Oqtane.Controllers
folder = _folders.GetFolder(FolderId); folder = _folders.GetFolder(FolderId);
} }
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.PermissionList))
{ {
string folderPath = _folders.GetFolderPath(folder); string folderPath = _folders.GetFolderPath(folder);
CreateDirectory(folderPath); CreateDirectory(folderPath);
@ -311,7 +310,7 @@ namespace Oqtane.Controllers
if (int.TryParse(folder, out FolderId)) if (int.TryParse(folder, out FolderId))
{ {
Folder Folder = _folders.GetFolder(FolderId); Folder Folder = _folders.GetFolder(FolderId);
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.Permissions)) if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.PermissionList))
{ {
folderPath = _folders.GetFolderPath(Folder); folderPath = _folders.GetFolderPath(Folder);
} }
@ -498,7 +497,7 @@ namespace Oqtane.Controllers
private IActionResult Download(int id, bool asAttachment) private IActionResult Download(int id, bool asAttachment)
{ {
var file = _files.GetFile(id); var file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{ {
var filepath = _files.GetFilePath(file); var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath)) if (System.IO.File.Exists(filepath))
@ -533,7 +532,7 @@ namespace Oqtane.Controllers
public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate) public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate)
{ {
var file = _files.GetFile(id); var file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{ {
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower())) if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
{ {
@ -551,7 +550,7 @@ namespace Oqtane.Controllers
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png"); string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate)) if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
{ {
if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.Permissions) || if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
!string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))) !string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString())))
{ {
imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath); imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
@ -659,10 +658,10 @@ namespace Oqtane.Controllers
var file = _files.GetFile(folderid, filename); var file = _files.GetFile(folderid, filename);
int size = 0; int size = 0;
var folder = _folders.GetFolder(folderid); var folder = _folders.GetFolder(folderid, false);
if (folder.Capacity != 0) if (folder.Capacity != 0)
{ {
foreach (var f in _files.GetFiles(folderid)) foreach (var f in _files.GetFiles(folderid, false))
{ {
size += f.Size; size += f.Size;
} }

View File

@ -43,7 +43,7 @@ namespace Oqtane.Controllers
{ {
foreach (Folder folder in _folders.GetFolders(SiteId)) foreach (Folder folder in _folders.GetFolders(SiteId))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{ {
folders.Add(folder); folders.Add(folder);
} }
@ -64,7 +64,7 @@ namespace Oqtane.Controllers
public Folder Get(int id) public Folder Get(int id)
{ {
Folder folder = _folders.GetFolder(id); Folder folder = _folders.GetFolder(id);
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions)) if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{ {
return folder; return folder;
} }
@ -85,7 +85,7 @@ namespace Oqtane.Controllers
folderPath += "/"; folderPath += "/";
} }
Folder folder = _folders.GetFolder(siteId, folderPath); Folder folder = _folders.GetFolder(siteId, folderPath);
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions)) if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{ {
return folder; return folder;
} }
@ -104,16 +104,16 @@ namespace Oqtane.Controllers
{ {
if (ModelState.IsValid && folder.SiteId == _alias.SiteId) if (ModelState.IsValid && folder.SiteId == _alias.SiteId)
{ {
string permissions; List<Permission> permissions;
if (folder.ParentId != null) if (folder.ParentId != null)
{ {
permissions = _folders.GetFolder(folder.ParentId.Value).Permissions; permissions = _folders.GetFolder(folder.ParentId.Value).PermissionList;
} }
else else
{ {
permissions = new List<Permission> { permissions = new List<Permission> {
new Permission(PermissionNames.Edit, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true),
}.EncodePermissions(); };
} }
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
{ {

View File

@ -52,7 +52,7 @@ namespace Oqtane.Controllers
{ {
var installation = new Installation { Success = false, Message = "" }; var installation = new Installation { Success = false, Message = "" };
if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_configManager.GetSetting("ConnectionStrings:" + SettingKeys.ConnectionStringKey, "")))) if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", ""))))
{ {
installation = _databaseManager.Install(config); installation = _databaseManager.Install(config);

View File

@ -47,17 +47,18 @@ namespace Oqtane.Controllers
int SiteId; int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{ {
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(SiteId).ToList();
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList(); List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId)) foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{ {
Module module = new Module(); Module module = new Module();
module.SiteId = pagemodule.Module.SiteId; module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages; module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions; module.PermissionList = pagemodule.Module.PermissionList;
module.CreatedBy = pagemodule.Module.CreatedBy; module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn; module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy; module.ModifiedBy = pagemodule.Module.ModifiedBy;
@ -74,8 +75,10 @@ namespace Oqtane.Controllers
module.Order = pagemodule.Order; module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType; module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId) module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
modules.Add(module); modules.Add(module);
@ -92,17 +95,40 @@ namespace Oqtane.Controllers
return modules; return modules;
} }
private ModuleDefinition FilterModuleDefinition(ModuleDefinition moduleDefinition)
{
if (moduleDefinition != null)
{
moduleDefinition.Description = "";
moduleDefinition.Categories = "";
moduleDefinition.Version = "";
moduleDefinition.Owner = "";
moduleDefinition.Url = "";
moduleDefinition.Contact = "";
moduleDefinition.License = "";
moduleDefinition.Dependencies = "";
moduleDefinition.PermissionNames = "";
moduleDefinition.ServerManagerType = "";
moduleDefinition.ReleaseVersions = "";
moduleDefinition.PackageName = "";
moduleDefinition.AssemblyName = "";
moduleDefinition.PermissionList = null;
moduleDefinition.Template = "";
}
return moduleDefinition;
}
// GET api/<controller>/5 // GET api/<controller>/5
[HttpGet("{id}")] [HttpGet("{id}")]
public Module Get(int id) public Module Get(int id)
{ {
Module module = _modules.GetModule(id); Module module = _modules.GetModule(id);
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, module.Permissions)) if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, module.PermissionList))
{ {
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList(); List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList();
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName); module.ModuleDefinition = FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
module.Settings = _settings.GetSettings(EntityNames.Module, id) module.Settings = _settings.GetSettings(EntityNames.Module, id)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return module; return module;
} }

View File

@ -15,6 +15,7 @@ using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Text.Json; using System.Text.Json;
using System.Net; using System.Net;
using Oqtane.Modules;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -22,6 +23,9 @@ namespace Oqtane.Controllers
public class ModuleDefinitionController : Controller public class ModuleDefinitionController : Controller
{ {
private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pagemodules;
private readonly IPermissionRepository _permissions;
private readonly ITenantRepository _tenants; private readonly ITenantRepository _tenants;
private readonly ISqlRepository _sql; private readonly ISqlRepository _sql;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
@ -33,9 +37,12 @@ namespace Oqtane.Controllers
private readonly ILogManager _logger; private readonly ILogManager _logger;
private readonly Alias _alias; private readonly Alias _alias;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository module, IPageModuleRepository pageModule, IPermissionRepository permission, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{ {
_moduleDefinitions = moduleDefinitions; _moduleDefinitions = moduleDefinitions;
_modules = module;
_pagemodules = pageModule;
_permissions = permission;
_tenants = tenants; _tenants = tenants;
_sql = sql; _sql = sql;
_userPermissions = userPermissions; _userPermissions = userPermissions;
@ -58,7 +65,7 @@ namespace Oqtane.Controllers
List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>(); List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>();
foreach (ModuleDefinition moduledefinition in _moduleDefinitions.GetModuleDefinitions(SiteId)) foreach (ModuleDefinition moduledefinition in _moduleDefinitions.GetModuleDefinitions(SiteId))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList))
{ {
if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString(); if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString();
moduledefinitions.Add(moduledefinition); moduledefinitions.Add(moduledefinition);
@ -82,7 +89,7 @@ namespace Oqtane.Controllers
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{ {
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId); ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId);
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList))
{ {
if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString(); if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString();
return moduledefinition; return moduledefinition;
@ -228,6 +235,27 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
} }
// remove PageModule and Module
List<Models.Module> modulesToRemove = _modules.GetModules(moduledefinition.SiteId).Where(m => m.ModuleDefinitionName == moduledefinition.ModuleDefinitionName).ToList();
foreach (Models.Module moduleToRemove in modulesToRemove)
{
// Get the PageModule items associated with the Module item to be removed
List<PageModule> pageModulesToRemove = _pagemodules.GetPageModules(moduledefinition.SiteId).Where(pm => pm.ModuleId == moduleToRemove.ModuleId).ToList();
foreach(PageModule pageModule in pageModulesToRemove)
{
// Remove the PageModule item
_pagemodules.DeletePageModule(pageModule.PageModuleId);
}
// Remove Permissions
_permissions.DeletePermissions(moduledefinition.SiteId, EntityNames.Module, moduleToRemove.ModuleId);
// Remove the Module item
_modules.DeleteModule(moduleToRemove.ModuleId);
}
// remove module definition // remove module definition
_moduleDefinitions.DeleteModuleDefinition(id); _moduleDefinitions.DeleteModuleDefinition(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete);

View File

@ -54,10 +54,10 @@ namespace Oqtane.Controllers
foreach (Page page in _pages.GetPages(SiteId)) foreach (Page page in _pages.GetPages(SiteId))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{ {
page.Settings = settings.Where(item => item.EntityId == page.PageId) page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
pages.Add(page); pages.Add(page);
} }
@ -86,10 +86,10 @@ namespace Oqtane.Controllers
{ {
page = _pages.GetPage(id, int.Parse(userid)); page = _pages.GetPage(id, int.Parse(userid));
} }
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions)) if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, page.PermissionList))
{ {
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId) page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page; return page;
} }
@ -106,10 +106,10 @@ namespace Oqtane.Controllers
public Page Get(string path, int siteid) public Page Get(string path, int siteid)
{ {
Page page = _pages.GetPage(WebUtility.UrlDecode(path), siteid); Page page = _pages.GetPage(WebUtility.UrlDecode(path), siteid);
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions)) if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{ {
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId) page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page; return page;
} }
@ -128,16 +128,16 @@ namespace Oqtane.Controllers
{ {
if (ModelState.IsValid && page.SiteId == _alias.SiteId) if (ModelState.IsValid && page.SiteId == _alias.SiteId)
{ {
string permissions; List<Permission> permissions;
if (page.ParentId != null) if (page.ParentId != null)
{ {
permissions = _pages.GetPage(page.ParentId.Value).Permissions; permissions = _pages.GetPage(page.ParentId.Value).PermissionList;
} }
else else
{ {
permissions = new List<Permission> { permissions = new List<Permission> {
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(); };
} }
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
@ -194,10 +194,10 @@ namespace Oqtane.Controllers
page.ThemeType = parent.ThemeType; page.ThemeType = parent.ThemeType;
page.DefaultContainerType = parent.DefaultContainerType; page.DefaultContainerType = parent.DefaultContainerType;
page.Icon = parent.Icon; page.Icon = parent.Icon;
page.Permissions = new List<Permission> { page.PermissionList = new List<Permission> {
new Permission(PermissionNames.View, int.Parse(userid), true), new Permission(PermissionNames.View, int.Parse(userid), true),
new Permission(PermissionNames.Edit, int.Parse(userid), true) new Permission(PermissionNames.Edit, int.Parse(userid), true)
}.EncodePermissions(); };
page.IsPersonalizable = false; page.IsPersonalizable = false;
page.UserId = int.Parse(userid); page.UserId = int.Parse(userid);
page = _pages.AddPage(page); page = _pages.AddPage(page);
@ -213,10 +213,10 @@ namespace Oqtane.Controllers
module.PageId = page.PageId; module.PageId = page.PageId;
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
module.AllPages = false; module.AllPages = false;
module.Permissions = new List<Permission> { module.PermissionList = new List<Permission> {
new Permission(PermissionNames.View, int.Parse(userid), true), new Permission(PermissionNames.View, int.Parse(userid), true),
new Permission(PermissionNames.Edit, int.Parse(userid), true) new Permission(PermissionNames.Edit, int.Parse(userid), true)
}.EncodePermissions(); };
module = _modules.AddModule(module); module = _modules.AddModule(module);
string content = _modules.ExportModule(pm.ModuleId); string content = _modules.ExportModule(pm.ModuleId);
@ -274,9 +274,8 @@ namespace Oqtane.Controllers
} }
// get differences between current and new page permissions // get differences between current and new page permissions
var newPermissions = _permissionRepository.DecodePermissions(page.Permissions, page.SiteId, EntityNames.Page, page.PageId).ToList(); var added = GetPermissionsDifferences(page.PermissionList, currentPermissions);
var added = GetPermissionsDifferences(newPermissions, currentPermissions); var removed = GetPermissionsDifferences(currentPermissions, page.PermissionList);
var removed = GetPermissionsDifferences(currentPermissions, newPermissions);
// synchronize module permissions // synchronize module permissions
if (added.Count > 0 || removed.Count > 0) if (added.Count > 0 || removed.Count > 0)

View File

@ -38,7 +38,7 @@ namespace Oqtane.Controllers
public PageModule Get(int id) public PageModule Get(int id)
{ {
PageModule pagemodule = _pageModules.GetPageModule(id); PageModule pagemodule = _pageModules.GetPageModule(id);
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions)) if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{ {
return pagemodule; return pagemodule;
} }
@ -55,7 +55,7 @@ namespace Oqtane.Controllers
public PageModule Get(int pageid, int moduleid) public PageModule Get(int pageid, int moduleid)
{ {
PageModule pagemodule = _pageModules.GetPageModule(pageid, moduleid); PageModule pagemodule = _pageModules.GetPageModule(pageid, moduleid);
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions)) if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{ {
return pagemodule; return pagemodule;
} }

View File

@ -46,7 +46,7 @@ namespace Oqtane.Controllers
_identityCache = identityCache; _identityCache = identityCache;
_logger = logger; _logger = logger;
_alias = tenantManager.GetAlias(); _alias = tenantManager.GetAlias();
_visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString(); _visitorCookie = Constants.VisitorCookiePrefix + _alias.SiteId.ToString();
} }
// GET: api/<controller> // GET: api/<controller>
@ -64,8 +64,12 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityName, entityId); // suppress unauthorized visitor logging as it is usually caused by clients that do not support cookies
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; if (entityName != EntityNames.Visitor)
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityName, entityId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
} }
return settings; return settings;
} }
@ -85,8 +89,11 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {EntityName} {SettingId}", entityName, id); if (entityName != EntityNames.Visitor)
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {EntityName} {SettingId}", entityName, id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return null; return null;
} }
} }
@ -103,8 +110,11 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", setting); if (setting.EntityName != EntityNames.Visitor)
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", setting);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
setting = null; setting = null;
} }
return setting; return setting;
@ -122,8 +132,11 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", setting); if (setting.EntityName != EntityNames.Visitor)
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; {
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", setting);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
setting = null; setting = null;
} }
return setting; return setting;
@ -142,8 +155,11 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Setting {Setting}", setting); if (entityName != EntityNames.Visitor)
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; {
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Setting {Setting}", setting);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
} }
} }
@ -219,6 +235,7 @@ namespace Oqtane.Controllers
authorized = User.IsInRole(RoleNames.Admin); authorized = User.IsInRole(RoleNames.Admin);
if (!authorized) if (!authorized)
{ {
// a visitor may have cookies disabled
if (int.TryParse(Request.Cookies[_visitorCookie], out int visitorId)) if (int.TryParse(Request.Cookies[_visitorCookie], out int visitorId))
{ {
authorized = (visitorId == entityId); authorized = (visitorId == entityId);

View File

@ -13,6 +13,7 @@ using System.Globalization;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Oqtane.Extensions; using Oqtane.Extensions;
using System; using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -89,10 +90,10 @@ namespace Oqtane.Controllers
site.Pages = new List<Page>(); site.Pages = new List<Page>();
foreach (Page page in _pages.GetPages(site.SiteId)) foreach (Page page in _pages.GetPages(site.SiteId))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{ {
page.Settings = settings.Where(item => item.EntityId == page.PageId) page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Pages.Add(page); site.Pages.Add(page);
} }
@ -105,13 +106,13 @@ namespace Oqtane.Controllers
site.Modules = new List<Module>(); site.Modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId)) foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{ {
Module module = new Module(); Module module = new Module();
module.SiteId = pagemodule.Module.SiteId; module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName; module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages; module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions; module.PermissionList = pagemodule.Module.PermissionList;
module.CreatedBy = pagemodule.Module.CreatedBy; module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn; module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy; module.ModifiedBy = pagemodule.Module.ModifiedBy;
@ -128,9 +129,10 @@ namespace Oqtane.Controllers
module.Order = pagemodule.Order; module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType; module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName); module.ModuleDefinition = FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId) module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions)) .Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Modules.Add(module); site.Modules.Add(module);
@ -152,6 +154,29 @@ namespace Oqtane.Controllers
} }
} }
private ModuleDefinition FilterModuleDefinition(ModuleDefinition moduleDefinition)
{
if (moduleDefinition != null)
{
moduleDefinition.Description = "";
moduleDefinition.Categories = "";
moduleDefinition.Version = "";
moduleDefinition.Owner = "";
moduleDefinition.Url = "";
moduleDefinition.Contact = "";
moduleDefinition.License = "";
moduleDefinition.Dependencies = "";
moduleDefinition.PermissionNames = "";
moduleDefinition.ServerManagerType = "";
moduleDefinition.ReleaseVersions = "";
moduleDefinition.PackageName = "";
moduleDefinition.AssemblyName = "";
moduleDefinition.PermissionList = null;
moduleDefinition.Template = "";
}
return moduleDefinition;
}
// POST api/<controller> // POST api/<controller>
[HttpPost] [HttpPost]
[Authorize(Roles = RoleNames.Host)] [Authorize(Roles = RoleNames.Host)]

View File

@ -32,13 +32,23 @@ namespace Oqtane.Controllers
{ {
var results = new List<Dictionary<string, string>>(); var results = new List<Dictionary<string, string>>();
Dictionary<string, string> row; Dictionary<string, string> row;
Tenant tenant = _tenants.GetTenant(sqlquery.TenantId);
if (string.IsNullOrEmpty(sqlquery.DBType) || string.IsNullOrEmpty(sqlquery.DBConnectionString))
{
Tenant tenant = _tenants.GetTenant(sqlquery.TenantId);
if (tenant != null)
{
sqlquery.DBType = tenant.DBType;
sqlquery.DBConnectionString = tenant.DBConnectionString;
}
}
try try
{ {
foreach (string query in sqlquery.Query.Split("GO", StringSplitOptions.RemoveEmptyEntries)) foreach (string query in sqlquery.Query.Split("GO", StringSplitOptions.RemoveEmptyEntries))
{ {
IDataReader dr = _sql.ExecuteReader(tenant, query); IDataReader dr = _sql.ExecuteReader(sqlquery.DBType, sqlquery.DBConnectionString, query);
_logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId}", query, sqlquery.TenantId); _logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Database {DBType} and Connection {DBConnectionString}", query, sqlquery.DBType, sqlquery.DBConnectionString);
while (dr.Read()) while (dr.Read())
{ {
row = new Dictionary<string, string>(); row = new Dictionary<string, string>();
@ -53,7 +63,7 @@ namespace Oqtane.Controllers
catch (Exception ex) catch (Exception ex)
{ {
results.Add(new Dictionary<string, string>() { { "Error", ex.Message } }); results.Add(new Dictionary<string, string>() { { "Error", ex.Message } });
_logger.Log(LogLevel.Warning, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message); _logger.Log(LogLevel.Warning, this, LogFunction.Other, "Sql Query {Query} Executed on Database {DBType} and Connection {DBConnectionString} Resulted In An Error {Error}", sqlquery.Query, sqlquery.DBType, sqlquery.DBConnectionString, ex.Message);
} }
sqlquery.Results = results; sqlquery.Results = results;
return sqlquery; return sqlquery;

View File

@ -63,6 +63,12 @@ namespace Oqtane.Controllers
} }
systeminfo.Add("Log", log); systeminfo.Add("Log", log);
break; break;
case "connectionstrings":
foreach (var kvp in _configManager.GetSettings(SettingKeys.ConnectionStringsSection))
{
systeminfo.Add(kvp.Key, kvp.Value);
}
break;
} }
return systeminfo; return systeminfo;
@ -84,16 +90,25 @@ namespace Oqtane.Controllers
{ {
foreach(KeyValuePair<string, object> kvp in settings) foreach(KeyValuePair<string, object> kvp in settings)
{ {
_configManager.AddOrUpdateSetting(kvp.Key, kvp.Value, false); UpdateSetting(kvp.Key, kvp.Value);
} }
} }
// PUT: api/<controller> private void UpdateSetting(string key, object value)
[HttpPut("{key}/{value}")]
[Authorize(Roles = RoleNames.Host)]
public void Put(string key, object value)
{ {
_configManager.AddOrUpdateSetting(key, value, false); switch (key.ToLower())
{
case "clearlog":
string path = Path.Combine(_environment.ContentRootPath, "Content", "Log", "error.log");
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
break;
default:
_configManager.AddOrUpdateSetting(key, value, false);
break;
}
} }
} }
} }

View File

@ -50,7 +50,7 @@ namespace Oqtane.Controllers
bool authorized = User.IsInRole(RoleNames.Admin); bool authorized = User.IsInRole(RoleNames.Admin);
if (!authorized) if (!authorized)
{ {
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString(); var visitorCookie = Constants.VisitorCookiePrefix + _alias.SiteId.ToString();
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId)) if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
{ {
authorized = (visitorId == id); authorized = (visitorId == id);

View File

@ -72,6 +72,7 @@ namespace Microsoft.Extensions.DependencyInjection
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services) internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
{ {
services.AddTransient<IDBContextDependencies, DBContextDependencies>();
services.AddTransient<ITenantManager, TenantManager>(); services.AddTransient<ITenantManager, TenantManager>();
services.AddTransient<IAliasAccessor, AliasAccessor>(); services.AddTransient<IAliasAccessor, AliasAccessor>();
services.AddTransient<IUserPermissions, UserPermissions>(); services.AddTransient<IUserPermissions, UserPermissions>();

View File

@ -197,7 +197,7 @@ namespace Oqtane.Extensions
} }
// validate user // validate user
var identity = await ValidateUser(email, id, claims, context.HttpContext); var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
if (identity.Label == ExternalLoginStatus.Success) if (identity.Label == ExternalLoginStatus.Success)
{ {
identity.AddClaim(new Claim("access_token", context.AccessToken)); identity.AddClaim(new Claim("access_token", context.AccessToken));
@ -232,7 +232,7 @@ namespace Oqtane.Extensions
var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray()); var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray());
// validate user // validate user
var identity = await ValidateUser(email, id, claims, context.HttpContext); var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
if (identity.Label == ExternalLoginStatus.Success) if (identity.Label == ExternalLoginStatus.Success)
{ {
// external roles // external roles
@ -278,7 +278,7 @@ namespace Oqtane.Extensions
return Task.CompletedTask; return Task.CompletedTask;
} }
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext) private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
{ {
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>(); var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme); ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
@ -427,6 +427,55 @@ namespace Oqtane.Extensions
user.LastLoginOn = DateTime.UtcNow; user.LastLoginOn = DateTime.UtcNow;
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString(); user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
_users.UpdateUser(user); _users.UpdateUser(user);
// user profile claims
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "")))
{
var _settings = httpContext.RequestServices.GetRequiredService<ISettingRepository>();
var _profiles = httpContext.RequestServices.GetRequiredService<IProfileRepository>();
var profiles = _profiles.GetProfiles(alias.SiteId).ToList();
foreach (var mapping in httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "").Split(',', StringSplitOptions.RemoveEmptyEntries))
{
if (mapping.Contains(":"))
{
var claim = claimsPrincipal.Claims.FirstOrDefault(item => item.Type == mapping.Split(":")[0]);
if (claim != null)
{
var profile = profiles.FirstOrDefault(item => item.Name == mapping.Split(":")[1]);
if (profile != null)
{
if (!string.IsNullOrEmpty(claim.Value))
{
var setting = _settings.GetSetting(EntityNames.User, user.UserId, profile.Name);
if (setting != null)
{
setting.SettingValue = claim.Value;
_settings.UpdateSetting(setting);
}
else
{
setting = new Setting { EntityName = EntityNames.User, EntityId = user.UserId, SettingName = profile.Name, SettingValue = claim.Value, IsPrivate = profile.IsPrivate };
_settings.AddSetting(setting);
}
}
}
else
{
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile {ProfileName} Does Not Exist For The Site. Please Verify Your User Profile Definitions.", mapping.Split(":")[1]);
}
}
else
{
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim {ClaimType} Does Not Exist. The Valid Claims Are {Claims}.", mapping.Split(":")[0], claims);
}
}
else
{
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim Mapping {Mapping} Is Not Specified Correctly. It Should Be In The Format 'ClaimType:ProfileName'.", mapping);
}
}
}
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName); _logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
} }
} }

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.Json; using System.Text.Json;
using Oqtane.Models; using Oqtane.Models;
@ -8,59 +7,9 @@ namespace Oqtane.Extensions
{ {
public static class PermissionExtension public static class PermissionExtension
{ {
public static string EncodePermissions(this IEnumerable<Permission> permissionList) public static string EncodePermissions(this IEnumerable<Permission> permissions)
{ {
List<PermissionString> permissionstrings = new List<PermissionString>(); return JsonSerializer.Serialize(permissions);
string entityname = "";
string permissionname = "";
string permissions = "";
StringBuilder permissionsbuilder = new StringBuilder();
string securityid = "";
foreach (Permission permission in permissionList.OrderBy(item => item.EntityName).ThenBy(item => item.PermissionName))
{
// permission collections are grouped by entityname and permissionname
if (entityname != permission.EntityName || permissionname != permission.PermissionName)
{
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { EntityName = entityname, PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
entityname = permission.EntityName;
permissionname = permission.PermissionName;
permissionsbuilder = new StringBuilder();
}
// deny permissions are prefixed with a "!"
string prefix = !permission.IsAuthorized ? "!" : "";
// encode permission
if (permission.UserId == null)
{
securityid = prefix + permission.Role.Name + ";";
}
else
{
securityid = prefix + "[" + permission.UserId + "];";
}
// insert deny permissions at the beginning and append grant permissions at the end
if (prefix == "!")
{
permissionsbuilder.Insert(0, securityid);
}
else
{
permissionsbuilder.Append(securityid);
}
}
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { EntityName = entityname, PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
return JsonSerializer.Serialize(permissionstrings);
} }
} }
} }

View File

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -42,6 +44,16 @@ namespace Oqtane.Infrastructure
return value; return value;
} }
public Dictionary<string, string> GetSettings(string sectionKey)
{
var settings = new Dictionary<string, string>();
foreach (var kvp in _config.GetSection(sectionKey).GetChildren().AsEnumerable())
{
settings.Add(kvp.Key, kvp.Value);
}
return settings;
}
public void AddOrUpdateSetting<T>(string key, T value, bool reload) public void AddOrUpdateSetting<T>(string key, T value, bool reload)
{ {
AddOrUpdateSetting("appsettings.json", key, value, reload); AddOrUpdateSetting("appsettings.json", key, value, reload);

View File

@ -62,7 +62,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "Master Database Not Installed Correctly. " + ex.Message; result.Message = "Master Database Not Installed Correctly. " + ex.ToString();
} }
} }
else // cannot connect else // cannot connect
@ -74,7 +74,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "Cannot Connect To Master Database. " + ex.Message; result.Message = "Cannot Connect To Master Database. " + ex.ToString();
} }
} }
} }
@ -162,6 +162,16 @@ namespace Oqtane.Infrastructure
{ {
install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer); install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer);
} }
// add new site
if (install.TenantName != TenantNames.Master && install.ConnectionString.Contains("="))
{
_configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{install.TenantName}", install.ConnectionString, false);
}
if (install.TenantName == TenantNames.Master && !install.ConnectionString.Contains("="))
{
install.ConnectionString = _config.GetConnectionString(install.ConnectionString);
}
} }
else else
{ {
@ -247,7 +257,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = ex.Message; result.Message = ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message)); _filelogger.LogError(Utilities.LogMessage(this, result.Message));
} }
@ -273,7 +283,7 @@ namespace Oqtane.Infrastructure
var database = Activator.CreateInstance(type) as IDatabase; var database = Activator.CreateInstance(type) as IDatabase;
// create data directory if does not exist // create data directory if does not exist
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty); if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty);
var dbOptions = new DbContextOptionsBuilder().UseOqtaneDatabase(database, NormalizeConnectionString(install.ConnectionString)).Options; var dbOptions = new DbContextOptionsBuilder().UseOqtaneDatabase(database, NormalizeConnectionString(install.ConnectionString)).Options;
@ -286,7 +296,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "An Error Occurred Creating The Database. This Is Usually Related To Your User Not Having Sufficient Rights To Perform This Operation. Please Note That You Can Also Create The Database Manually Prior To Initiating The Install Wizard. " + ex.Message; result.Message = "An Error Occurred Creating The Database. This Is Usually Related To Your User Not Having Sufficient Rights To Perform This Operation. Please Note That You Can Also Create The Database Manually Prior To Initiating The Install Wizard. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message)); _filelogger.LogError(Utilities.LogMessage(this, result.Message));
} }
} }
@ -316,10 +326,7 @@ namespace Oqtane.Infrastructure
using (var masterDbContext = new MasterDBContext(new DbContextOptions<MasterDBContext>(), null, _config)) using (var masterDbContext = new MasterDBContext(new DbContextOptions<MasterDBContext>(), null, _config))
{ {
if (installation.Success && (install.DatabaseType == Constants.DefaultDBType)) AddEFMigrationsHistory(sql, install.ConnectionString, install.DatabaseType, "", true);
{
UpgradeSqlServer(sql, install.ConnectionString, install.DatabaseType, true);
}
// push latest model into database // push latest model into database
masterDbContext.Database.Migrate(); masterDbContext.Database.Migrate();
result.Success = true; result.Success = true;
@ -327,7 +334,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "An Error Occurred Provisioning The Master Database. This Is Usually Related To The Master Database Not Being In A Supported State. " + ex.Message; result.Message = "An Error Occurred Provisioning The Master Database. This Is Usually Related To The Master Database Not Being In A Supported State. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message)); _filelogger.LogError(Utilities.LogMessage(this, result.Message));
} }
} }
@ -354,7 +361,7 @@ namespace Oqtane.Infrastructure
tenant = new Tenant tenant = new Tenant
{ {
Name = install.TenantName, Name = install.TenantName,
DBConnectionString = DenormalizeConnectionString(install.ConnectionString), DBConnectionString = (install.TenantName == TenantNames.Master) ? SettingKeys.ConnectionStringKey : install.TenantName,
DBType = install.DatabaseType, DBType = install.DatabaseType,
CreatedBy = "", CreatedBy = "",
CreatedOn = DateTime.UtcNow, CreatedOn = DateTime.UtcNow,
@ -370,7 +377,7 @@ namespace Oqtane.Infrastructure
tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName); tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName);
} }
var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray(); var aliasNames = install.Aliases.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var firstAlias = aliasNames[0]; var firstAlias = aliasNames[0];
foreach (var aliasName in aliasNames) foreach (var aliasName in aliasNames)
{ {
@ -406,28 +413,26 @@ namespace Oqtane.Infrastructure
{ {
var result = new Installation { Success = false, Message = string.Empty }; var result = new Installation { Success = false, Message = string.Empty };
var versions = Constants.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var versions = Constants.ReleaseVersions.Split(',', StringSplitOptions.RemoveEmptyEntries);
using (var scope = _serviceScopeFactory.CreateScope()) using (var scope = _serviceScopeFactory.CreateScope())
{ {
var upgrades = scope.ServiceProvider.GetRequiredService<IUpgradeManager>(); var upgrades = scope.ServiceProvider.GetRequiredService<IUpgradeManager>();
var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>(); var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>();
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>(); var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
var DBContextDependencies = scope.ServiceProvider.GetRequiredService<IDBContextDependencies>();
using (var db = GetInstallationContext()) using (var db = GetInstallationContext())
{ {
foreach (var tenant in db.Tenant.ToList()) foreach (var tenant in db.Tenant.ToList())
{ {
tenantManager.SetTenant(tenant.TenantId); tenantManager.SetTenant(tenant.TenantId);
tenant.DBConnectionString = MigrateConnectionString(db, tenant);
try try
{ {
using (var tenantDbContext = new TenantDBContext(tenantManager, null)) using (var tenantDbContext = new TenantDBContext(DBContextDependencies))
{ {
if (install.DatabaseType == Constants.DefaultDBType) AddEFMigrationsHistory(sql, _configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.DBConnectionString}", ""), tenant.DBType, tenant.Version, false);
{
UpgradeSqlServer(sql, tenant.DBConnectionString, tenant.DBType, false);
}
// push latest model into database // push latest model into database
tenantDbContext.Database.Migrate(); tenantDbContext.Database.Migrate();
result.Success = true; result.Success = true;
@ -435,7 +440,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "An Error Occurred Migrating A Tenant Database. This Is Usually Related To A Tenant Database Not Being In A Supported State. " + ex.Message; result.Message = "An Error Occurred Migrating A Tenant Database. This Is Usually Related To A Tenant Database Not Being In A Supported State. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message)); _filelogger.LogError(Utilities.LogMessage(this, result.Message));
} }
@ -456,7 +461,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "An Error Occurred Executing Upgrade Logic. " + ex.Message; result.Message = "An Error Occurred Executing Upgrade Logic. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message)); _filelogger.LogError(Utilities.LogMessage(this, result.Message));
} }
} }
@ -486,7 +491,7 @@ namespace Oqtane.Infrastructure
{ {
if (!string.IsNullOrEmpty(moduleDefinition.ReleaseVersions)) if (!string.IsNullOrEmpty(moduleDefinition.ReleaseVersions))
{ {
var versions = moduleDefinition.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var versions = moduleDefinition.ReleaseVersions.Split(',', StringSplitOptions.RemoveEmptyEntries);
using (var db = GetInstallationContext()) using (var db = GetInstallationContext())
{ {
if (!string.IsNullOrEmpty(moduleDefinition.ServerManagerType)) if (!string.IsNullOrEmpty(moduleDefinition.ServerManagerType))
@ -526,7 +531,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.Message; result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.ToString();
} }
} }
} }
@ -575,7 +580,7 @@ namespace Oqtane.Infrastructure
{ {
// set the alias explicitly so the tenant can be resolved // set the alias explicitly so the tenant can be resolved
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>(); var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray(); var aliasNames = install.Aliases.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var firstAlias = aliasNames[0]; var firstAlias = aliasNames[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias); var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>(); var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
@ -664,7 +669,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
result.Message = "An Error Occurred Creating Site. " + ex.Message; result.Message = "An Error Occurred Creating Site. " + ex.ToString();
} }
} }
@ -737,7 +742,7 @@ namespace Oqtane.Infrastructure
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Log(alias.SiteId, Shared.LogLevel.Error, "Site Migration", LogFunction.Other, "An Error Occurred Executing Site Migration {Type} For {Alias} And Version {Version} {Error}", upgrade.Value, alias.Name, version, ex.Message); logger.Log(alias.SiteId, Shared.LogLevel.Error, "Site Migration", LogFunction.Other, ex, "An Error Occurred Executing Site Migration {Type} For {Alias} And Version {Version}", upgrade.Value, alias.Name, version);
} }
} }
} }
@ -753,8 +758,8 @@ namespace Oqtane.Infrastructure
private string DenormalizeConnectionString(string connectionString) private string DenormalizeConnectionString(string connectionString)
{ {
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
connectionString = connectionString.Replace(dataDirectory ?? String.Empty, "|DataDirectory|"); connectionString = connectionString.Replace(dataDirectory ?? String.Empty, $"|{Constants.DataDirectory}|");
return connectionString; return connectionString;
} }
@ -780,8 +785,8 @@ namespace Oqtane.Infrastructure
private string NormalizeConnectionString(string connectionString) private string NormalizeConnectionString(string connectionString)
{ {
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
connectionString = connectionString.Replace("|DataDirectory|", dataDirectory); connectionString = connectionString.Replace($"|{Constants.DataDirectory}|", dataDirectory);
return connectionString; return connectionString;
} }
@ -799,14 +804,39 @@ namespace Oqtane.Infrastructure
_configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType, true); _configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType, true);
} }
public void UpgradeSqlServer(ISqlRepository sql, string connectionString, string databaseType, bool isMaster) public void AddEFMigrationsHistory(ISqlRepository sql, string connectionString, string databaseType, string version, bool isMaster)
{ {
var script = (isMaster) ? "MigrateMaster.sql" : "MigrateTenant.sql"; // in version 2.1.0 the __EFMigrationsHistory tables were introduced and must be added to existing SQL Server installations
if ((isMaster || (version != null && Version.Parse(version).CompareTo(Version.Parse("2.1.0")) < 0)) && databaseType == Constants.DefaultDBType)
{
var script = (isMaster) ? "MigrateMaster.sql" : "MigrateTenant.sql";
var query = sql.GetScriptFromAssembly(Assembly.GetExecutingAssembly(), script); var query = sql.GetScriptFromAssembly(Assembly.GetExecutingAssembly(), script);
query = query.Replace("{{Version}}", Constants.Version); query = query.Replace("{{Version}}", Constants.Version);
sql.ExecuteNonQuery(connectionString, databaseType, query); sql.ExecuteNonQuery(connectionString, databaseType, query);
}
}
public string MigrateConnectionString(InstallationContext db, Tenant tenant)
{
// migrate connection strings from the Tenant table to appsettings
if (tenant.DBConnectionString.Contains("="))
{
var defaultConnection = _configManager.GetConnectionString(SettingKeys.ConnectionStringKey);
if (tenant.DBConnectionString == defaultConnection)
{
tenant.DBConnectionString = SettingKeys.ConnectionStringKey;
}
else
{
_configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.Name}", tenant.DBConnectionString, false);
tenant.DBConnectionString = tenant.Name;
}
db.Entry(tenant).State = EntityState.Modified;
db.SaveChanges();
}
return tenant.DBConnectionString;
} }
private void ValidateConfiguration() private void ValidateConfiguration()

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
namespace Oqtane.Infrastructure namespace Oqtane.Infrastructure
@ -7,6 +8,7 @@ namespace Oqtane.Infrastructure
public IConfigurationSection GetSection(string sectionKey); public IConfigurationSection GetSection(string sectionKey);
public T GetSetting<T>(string settingKey, T defaultValue); public T GetSetting<T>(string settingKey, T defaultValue);
public T GetSetting<T>(string sectionKey, string settingKey, T defaultValue); public T GetSetting<T>(string sectionKey, string settingKey, T defaultValue);
public Dictionary<string, string> GetSettings(string sectionKey);
void AddOrUpdateSetting<T>(string key, T value, bool reload); void AddOrUpdateSetting<T>(string key, T value, bool reload);
void AddOrUpdateSetting<T>(string file, string key, T value, bool reload); void AddOrUpdateSetting<T>(string file, string key, T value, bool reload);
void RemoveSetting(string key, bool reload); void RemoveSetting(string key, bool reload);

View File

@ -43,10 +43,10 @@ namespace Oqtane.Infrastructure
}); });
context.Items.Add(Constants.HttpContextSiteSettingsKey, sitesettings); context.Items.Add(Constants.HttpContextSiteSettingsKey, sitesettings);
// rewrite path by removing alias path prefix from api and pages requests (for consistent routing) // rewrite path by removing alias path prefix from reserved route (api,pages,files) requests for consistent routes
if (!string.IsNullOrEmpty(alias.Path)) if (!string.IsNullOrEmpty(alias.Path))
{ {
if (path.StartsWith("/" + alias.Path) && (path.Contains("/api/") || path.Contains("/pages/"))) if (path.StartsWith("/" + alias.Path) && (Constants.ReservedRoutes.Any(item => path.Contains("/" + item + "/"))))
{ {
context.Request.Path = path.Replace("/" + alias.Path, ""); context.Request.Path = path.Replace("/" + alias.Path, "");
} }

View File

@ -45,40 +45,40 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-home", Icon = "oi oi-home",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
PagePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions() , },
PageTemplateModules = new List<PageTemplateModule> { PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> that provides advanced functionality for developing web, mobile, and desktop applications on .NET Core. It leverages the Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.</p>" + Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> that provides advanced functionality for developing web, mobile, and desktop applications on .NET Core. It leverages the Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.</p>" +
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a>&nbsp;&nbsp;<a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" + "<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a>&nbsp;&nbsp;<a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be used with all of the hosting models without any modification.</p>" + "<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be used with all of the hosting models without any modification.</p>" +
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">.NET Core</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>" "<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">.NET Core</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
}, },
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "<p>Copyright (c) 2018-2023 .NET Foundation</p>" + Content = "<p>Copyright (c) 2018-2023 .NET Foundation</p>" +
"<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>" + "<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>" +
"<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>" + "<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>" +
"<p>THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>" "<p>THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>"
}, },
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true), new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "<p>Oqtane allows you to control access to your content using security roles. This module is only visible to Registered Users of the site.</p>" Content = "<p>Oqtane allows you to control access to your content using security roles. This module is only visible to Registered Users of the site.</p>"
} }
} }
@ -92,18 +92,18 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-lock-locked", Icon = "oi oi-lock-locked",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
PagePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true), new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule> { PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true), new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "<p>Oqtane allows you to control access to your content using security roles. This page is only visible to Registered Users of the site.</p>" Content = "<p>Oqtane allows you to control access to your content using security roles. This page is only visible to Registered Users of the site.</p>"
} }
} }
@ -117,18 +117,18 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-target", Icon = "oi oi-target",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = true, IsPersonalizable = true,
PagePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule> { PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "<p>Oqtane offers native support for user personalized pages. If a page is identified as personalizable by the site administrator in the page settings, when an authenticated user visits the page they will see an edit button at the top right corner of the page next to their username. When they click this button the sytem will create a new version of the page and allow them to edit the page content.</p>" Content = "<p>Oqtane offers native support for user personalized pages. If a page is identified as personalizable by the site administrator in the page settings, when an authenticated user visits the page they will see an edit button at the top right corner of the page next to their username. When they click this button the sytem will create a new version of the page and allow them to edit the page content.</p>"
} }
} }
@ -142,23 +142,23 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-wrench", Icon = "oi oi-wrench",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = true, IsPersonalizable = true,
PagePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true), new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true) new Permission(PermissionNames.Edit, RoleNames.Host, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule> { PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Software Development", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Software Development", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true), new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true) new Permission(PermissionNames.Edit, RoleNames.Host, true)
}.EncodePermissions(), },
Content = "<p>Oqtane offers a Module Creator which allows you to create new modules to extend the framework with additional capabilities. Simply provide some basic information and the system will scaffold a completely functional module which includes all of the necessary code files and assets to get you up and running as quickly as possible.</p>" Content = "<p>Oqtane offers a Module Creator which allows you to create new modules to extend the framework with additional capabilities. Simply provide some basic information and the system will scaffold a completely functional module which includes all of the necessary code files and assets to get you up and running as quickly as possible.</p>"
}, },
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleCreator, Oqtane.Client", Title = "Module Creator", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleCreator, Oqtane.Client", Title = "Module Creator", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true), new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true) new Permission(PermissionNames.Edit, RoleNames.Host, true)
}.EncodePermissions() }
} }
} }
}); });

View File

@ -32,11 +32,11 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-home", Icon = "oi oi-home",
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
PagePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule>() PageTemplateModules = new List<PageTemplateModule>()
}); });

View File

@ -37,7 +37,7 @@ namespace Oqtane.Infrastructure
{ {
// legacy support for client api requests which would include the alias as a path prefix ( ie. {alias}/api/[controller] ) // legacy support for client api requests which would include the alias as a path prefix ( ie. {alias}/api/[controller] )
int aliasId; int aliasId;
string[] segments = httpcontext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); string[] segments = httpcontext.Request.Path.Value.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1 && Shared.Constants.ReservedRoutes.Contains(segments[1]) && int.TryParse(segments[0], out aliasId)) if (segments.Length > 1 && Shared.Constants.ReservedRoutes.Contains(segments[1]) && int.TryParse(segments[0], out aliasId))
{ {
alias = _aliasRepository.GetAliases().ToList().FirstOrDefault(item => item.AliasId == aliasId); alias = _aliasRepository.GetAliases().ToList().FirstOrDefault(item => item.AliasId == aliasId);

View File

@ -140,21 +140,21 @@ namespace Oqtane.Infrastructure
Icon = Icons.LinkBroken, Icon = Icons.LinkBroken,
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule> PageTemplateModules = new List<PageTemplateModule>
{ {
new PageTemplateModule new PageTemplateModule
{ {
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UrlMappings.Index).ToModuleDefinitionName(), Title = "Url Mappings", Pane = PaneNames.Default, ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UrlMappings.Index).ToModuleDefinitionName(), Title = "Url Mappings", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "" Content = ""
} }
} }
@ -169,21 +169,21 @@ namespace Oqtane.Infrastructure
Icon = Icons.Eye, Icon = Icons.Eye,
IsNavigation = true, IsNavigation = true,
IsPersonalizable = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule> PageTemplateModules = new List<PageTemplateModule>
{ {
new PageTemplateModule new PageTemplateModule
{ {
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Visitors.Index).ToModuleDefinitionName(), Title = "Visitor Management", Pane = PaneNames.Default, ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Visitors.Index).ToModuleDefinitionName(), Title = "Visitor Management", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "" Content = ""
} }
} }
@ -217,20 +217,20 @@ namespace Oqtane.Infrastructure
Icon = Icons.X, Icon = Icons.X,
IsNavigation = false, IsNavigation = false,
IsPersonalizable = false, IsPersonalizable = false,
PagePermissions = new List<Permission> PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
PageTemplateModules = new List<PageTemplateModule> PageTemplateModules = new List<PageTemplateModule>
{ {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Not Found", Pane = PaneNames.Default, new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Not Found", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> { PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true) new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(), },
Content = "<p>The page you requested does not exist.</p>" Content = "<p>The page you requested does not exist.</p>"
} }
} }

View File

@ -122,6 +122,26 @@ namespace Oqtane.Migrations.EntityBuilders
return table.Column<DateTimeOffset>(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue); return table.Column<DateTimeOffset>(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue);
} }
public void AddByteColumn(string name, bool nullable = false)
{
_migrationBuilder.AddColumn<byte>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema);
}
public void AddByteColumn(string name, bool nullable, int defaultValue)
{
_migrationBuilder.AddColumn<byte>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, defaultValue: defaultValue, schema: Schema);
}
protected OperationBuilder<AddColumnOperation> AddByteColumn(ColumnsBuilder table, string name, bool nullable = false)
{
return table.Column<byte>(name: RewriteName(name), nullable: nullable);
}
protected OperationBuilder<AddColumnOperation> AddByteColumn(ColumnsBuilder table, string name, bool nullable, int defaultValue)
{
return table.Column<byte>(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue);
}
public void AddIntegerColumn(string name, bool nullable = false) public void AddIntegerColumn(string name, bool nullable = false)
{ {
_migrationBuilder.AddColumn<int>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema); _migrationBuilder.AddColumn<int>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema);

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.03.03.02.01")]
public class AddFolderFileIsDeletedColumns : MultiDatabaseMigration
{
public AddFolderFileIsDeletedColumns(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
// IsDeleted columns were removed in 3.2.2 however SQLite does not support column removal so they had to be restored
if (ActiveDatabase.Name != "Sqlite")
{
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
folderEntityBuilder.AddBooleanColumn("IsDeleted", true);
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
fileEntityBuilder.AddBooleanColumn("IsDeleted", true);
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

@ -2,7 +2,6 @@ using Oqtane.Infrastructure;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Modules.HtmlText.Repository; using Oqtane.Modules.HtmlText.Repository;
using System.Net; using System.Net;
using Microsoft.AspNetCore.Http;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Shared; using Oqtane.Shared;
@ -17,15 +16,13 @@ namespace Oqtane.Modules.HtmlText.Manager
public class HtmlTextManager : MigratableModuleBase, IInstallable, IPortable public class HtmlTextManager : MigratableModuleBase, IInstallable, IPortable
{ {
private readonly IHtmlTextRepository _htmlText; private readonly IHtmlTextRepository _htmlText;
private readonly ITenantManager _tenantManager; private readonly IDBContextDependencies _DBContextDependencies;
private readonly IHttpContextAccessor _accessor;
private readonly ISqlRepository _sqlRepository; private readonly ISqlRepository _sqlRepository;
public HtmlTextManager(IHtmlTextRepository htmlText, ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor, ISqlRepository sqlRepository) public HtmlTextManager(IHtmlTextRepository htmlText, IDBContextDependencies DBContextDependencies, ISqlRepository sqlRepository)
{ {
_htmlText = htmlText; _htmlText = htmlText;
_tenantManager = tenantManager; _DBContextDependencies = DBContextDependencies;
_accessor = httpContextAccessor;
_sqlRepository = sqlRepository; _sqlRepository = sqlRepository;
} }
@ -56,12 +53,12 @@ namespace Oqtane.Modules.HtmlText.Manager
// version 1.0.0 used SQL scripts rather than migrations, so we need to seed the migration history table // version 1.0.0 used SQL scripts rather than migrations, so we need to seed the migration history table
_sqlRepository.ExecuteNonQuery(tenant, MigrationUtils.BuildInsertScript("HtmlText.01.00.00.00")); _sqlRepository.ExecuteNonQuery(tenant, MigrationUtils.BuildInsertScript("HtmlText.01.00.00.00"));
} }
return Migrate(new HtmlTextContext(_tenantManager, _accessor), tenant, MigrationType.Up); return Migrate(new HtmlTextContext(_DBContextDependencies), tenant, MigrationType.Up);
} }
public bool Uninstall(Tenant tenant) public bool Uninstall(Tenant tenant)
{ {
return Migrate(new HtmlTextContext(_tenantManager, _accessor), tenant, MigrationType.Down); return Migrate(new HtmlTextContext(_DBContextDependencies), tenant, MigrationType.Down);
} }
} }
} }

View File

@ -13,7 +13,7 @@ namespace Oqtane.Modules.HtmlText.Repository
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")] [PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public class HtmlTextContext : DBContextBase, ITransientService, IMultiDatabase public class HtmlTextContext : DBContextBase, ITransientService, IMultiDatabase
{ {
public HtmlTextContext(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) : base(tenantManager, httpContextAccessor) { } public HtmlTextContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies) { }
public virtual DbSet<Models.HtmlText> HtmlText { get; set; } public virtual DbSet<Models.HtmlText> HtmlText { get; set; }
} }

View File

@ -1,10 +1,10 @@
using Oqtane.Models; using Oqtane.Models;
namespace Oqtane.Modules namespace Oqtane.Modules
{ {
public interface IPortable public interface IPortable
{ {
// You Must Set The "ServerAssemblyName" In Your IModule Interface // You Must Set The "ServerManagerType" In Your IModule Interface
string ExportModule(Module module); string ExportModule(Module module);

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Modules
{
public interface ISitemap
{
// You Must Set The "ServerManagerType" In Your IModule Interface
List<Sitemap> GetUrls(string alias, string path, Module module);
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>3.3.1</Version> <Version>3.4.0</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.0</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
@ -60,4 +60,8 @@
<Copy SourceFiles="@(ModuleTemplateFiles)" DestinationFiles="@(ModuleTemplateFiles->'$(PublishDir)wwwroot\Modules\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" /> <Copy SourceFiles="@(ModuleTemplateFiles)" DestinationFiles="@(ModuleTemplateFiles->'$(PublishDir)wwwroot\Modules\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
<Copy SourceFiles="@(ThemeTemplateFiles)" DestinationFiles="@(ThemeTemplateFiles->'$(PublishDir)wwwroot\Themes\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" /> <Copy SourceFiles="@(ThemeTemplateFiles)" DestinationFiles="@(ThemeTemplateFiles->'$(PublishDir)wwwroot\Themes\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
</Target> </Target>
<ItemGroup>
<!-- extends watching group to include *.dll files and exclude the ones cause an infinite loop -->
<Watch Include="**\*.dll" Exclude="**\Microsoft.EntityFrameworkCore.*.dll;**\Oqtane.Database.*.dll;"/>
</ItemGroup>
</Project> </Project>

View File

@ -73,7 +73,7 @@ namespace Oqtane.Pages
if (file != null) if (file != null)
{ {
if (file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) if (file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{ {
// calculate ETag using last modified date and file size // calculate ETag using last modified date and file size
var etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); var etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16);

View File

@ -1,3 +1,4 @@
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -43,6 +44,10 @@ namespace Oqtane.Pages
{ {
returnurl = ""; returnurl = "";
} }
else
{
returnurl = WebUtility.UrlDecode(returnurl);
}
if (!returnurl.StartsWith("/")) if (!returnurl.StartsWith("/"))
{ {
returnurl = "/" + returnurl; returnurl = "/" + returnurl;

View File

@ -0,0 +1,3 @@
@page "/pages/sitemap.xml"
@namespace Oqtane.Pages
@model Oqtane.Pages.SitemapModel

View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Repository;
using Oqtane.Security;
using Oqtane.Shared;
namespace Oqtane.Pages
{
[AllowAnonymous]
public class SitemapModel : PageModel
{
private readonly IServiceProvider _serviceProvider;
private readonly IPageRepository _pages;
private readonly IPageModuleRepository _pageModules;
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly IUserPermissions _userPermissions;
private readonly ILogManager _logger;
private readonly Alias _alias;
public SitemapModel(IServiceProvider serviceProvider, IPageRepository pages, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, IUrlMappingRepository urlMappings, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_serviceProvider = serviceProvider;
_pages = pages;
_pageModules = pageModules;
_moduleDefinitions = moduleDefinitions;
_userPermissions = userPermissions;
_logger = logger;
_alias = tenantManager.GetAlias();
}
public IActionResult OnGet()
{
var sitemap = new List<Sitemap>();
// build site map
var moduleDefinitions = _moduleDefinitions.GetModuleDefinitions(_alias.SiteId).ToList();
var pageModules = _pageModules.GetPageModules(_alias.SiteId);
foreach (var page in _pages.GetPages(_alias.SiteId))
{
if (_userPermissions.IsAuthorized(null, PermissionNames.View, page.PermissionList) && page.IsNavigation)
{
sitemap.Add(new Sitemap { Url = _alias.Protocol + _alias.Name + Utilities.NavigateUrl(_alias.Path, page.Path, ""), ModifiedOn = page.ModifiedOn });
foreach (var pageModule in pageModules.Where(item => item.PageId == page.PageId))
{
if (_userPermissions.IsAuthorized(null, PermissionNames.View, pageModule.Module.PermissionList))
{
var moduleDefinition = moduleDefinitions.Where(item => item.ModuleDefinitionName == pageModule.Module.ModuleDefinitionName).FirstOrDefault();
if (moduleDefinition != null && moduleDefinition.ServerManagerType != "")
{
Type moduletype = Type.GetType(moduleDefinition.ServerManagerType);
if (moduletype != null && moduletype.GetInterface("ISitemap") != null)
{
try
{
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
var urls = ((ISitemap)moduleobject).GetUrls(_alias.Path, page.Path, pageModule.Module);
foreach (var url in urls)
{
sitemap.Add(new Sitemap { Url = _alias.Protocol + _alias.Name + url.Url, ModifiedOn = url.ModifiedOn });
}
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, this, LogFunction.Other, ex, "Error Retrieving SiteMap For {Name} Module", moduleDefinition.Name);
}
}
}
}
}
}
}
// write XML
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = (" ");
settings.CloseOutput = true;
settings.OmitXmlDeclaration = true;
settings.WriteEndDocumentOnClose = true;
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
foreach (var url in sitemap)
{
writer.WriteStartElement("url");
writer.WriteElementString("loc", url.Url);
writer.WriteElementString("lastmod", url.ModifiedOn.ToString("yyyy-MM-dd"));
writer.WriteEndElement();
}
writer.Close();
}
return Content(builder.ToString());
}
}
}

View File

@ -20,7 +20,6 @@ using Oqtane.Enums;
using Oqtane.Security; using Oqtane.Security;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Themes; using Oqtane.Themes;
using Oqtane.UI;
namespace Oqtane.Pages namespace Oqtane.Pages
{ {
@ -285,7 +284,7 @@ namespace Oqtane.Pages
// check if cookie already exists // check if cookie already exists
Visitor visitor = null; Visitor visitor = null;
bool addcookie = false; bool addcookie = false;
var VisitorCookie = "APP_VISITOR_" + SiteId.ToString(); var VisitorCookie = Constants.VisitorCookiePrefix + SiteId.ToString();
if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId)) if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId))
{ {
// if enabled use IP Address correlation // if enabled use IP Address correlation

View File

@ -67,7 +67,7 @@ namespace Oqtane.Repository
Alias alias = null; Alias alias = null;
List<Alias> aliases = GetAliases().ToList(); List<Alias> aliases = GetAliases().ToList();
var segments = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); var segments = url.Split('/', StringSplitOptions.RemoveEmptyEntries);
// iterate segments to find keywords // iterate segments to find keywords
int start = segments.Length; int start = segments.Length;

View File

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -6,11 +7,13 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration;
using Oqtane.Databases.Interfaces; using Oqtane.Databases.Interfaces;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Migrations.Framework; using Oqtane.Migrations.Framework;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess
@ -18,20 +21,20 @@ namespace Oqtane.Repository
{ {
public class DBContextBase : IdentityUserContext<IdentityUser> public class DBContextBase : IdentityUserContext<IdentityUser>
{ {
private readonly ITenantResolver _tenantResolver;
private readonly ITenantManager _tenantManager; private readonly ITenantManager _tenantManager;
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private string _connectionString; private readonly IConfigurationRoot _config;
private string _databaseType; private string _connectionString = "";
private string _databaseType = "";
public DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) public DBContextBase(IDBContextDependencies DBContextDependencies)
{ {
_connectionString = String.Empty; _tenantManager = DBContextDependencies.TenantManager;
_tenantManager = tenantManager; _accessor = DBContextDependencies.Accessor;
_accessor = httpContextAccessor; _config = DBContextDependencies.Config;
} }
public IDatabase ActiveDatabase { get; private set; } public IDatabase ActiveDatabase { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {
@ -39,21 +42,11 @@ namespace Oqtane.Repository
if (string.IsNullOrEmpty(_connectionString)) if (string.IsNullOrEmpty(_connectionString))
{ {
Tenant tenant = _tenantManager.GetTenant();
Tenant tenant;
if (_tenantResolver != null)
{
tenant = _tenantResolver.GetTenant();
}
else
{
tenant = _tenantManager.GetTenant();
}
if (tenant != null) if (tenant != null)
{ {
_connectionString = tenant.DBConnectionString _connectionString = _config.GetConnectionString(tenant.DBConnectionString)
.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()); .Replace($"|{Constants.DataDirectory}|", AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString());
_databaseType = tenant.DBType; _databaseType = tenant.DBType;
} }
} }
@ -93,12 +86,17 @@ namespace Oqtane.Repository
return base.SaveChangesAsync(cancellationToken); return base.SaveChangesAsync(cancellationToken);
} }
[Obsolete("This constructor is obsolete. Use DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) instead.", false)] [Obsolete("This constructor is obsolete. Use DBContextBase(IDBContextDependencies DBContextDependencies) instead.", false)]
public DBContextBase(ITenantResolver tenantResolver, IHttpContextAccessor httpContextAccessor) public DBContextBase(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor)
{ {
_connectionString = String.Empty; _tenantManager = tenantManager;
_tenantResolver = tenantResolver;
_accessor = httpContextAccessor; _accessor = httpContextAccessor;
// anti-pattern used to reference config service in base class without causing breaking change
_config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, false)
.Build();
} }
} }
} }

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Oqtane.Infrastructure;
namespace Oqtane.Repository
{
public class DBContextDependencies : IDBContextDependencies
{
public DBContextDependencies(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor, IConfigurationRoot config)
{
TenantManager = tenantManager;
Accessor = httpContextAccessor;
Config = config;
}
public ITenantManager TenantManager { get; }
public IHttpContextAccessor Accessor { get; }
public IConfigurationRoot Config { get; }
}
}

View File

@ -1,8 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Oqtane.Databases.Interfaces;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Interfaces;
using Oqtane.Models; using Oqtane.Models;
using IDatabase = Oqtane.Databases.Interfaces.IDatabase; using IDatabase = Oqtane.Databases.Interfaces.IDatabase;

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