Merge branch 'oqtane-dev' into dev

This commit is contained in:
Marc Drexel 2021-09-15 15:23:28 +02:00
commit b496dc488c
40 changed files with 865 additions and 420 deletions

View File

@ -150,7 +150,8 @@
if (firstRender)
{
var interop = new Interop(JSRuntime);
await interop.IncludeLink("app-stylesheet", "stylesheet", "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css", "text/css", "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T", "anonymous", "");
await interop.IncludeLink("", "stylesheet", "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", "text/css", "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC", "anonymous", "");
await interop.IncludeScript("", "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM", "anonymous", "", "head", "");
}
}

View File

@ -27,6 +27,12 @@
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="description" HelpText="A description of the file. This can be used as a caption for image files." ResourceKey="Description">Description: </Label>
<div class="col-sm-9">
<input id="description" class="form-control" @bind="@_description" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="size" HelpText="The size of the file (in bytes)" ResourceKey="Size">Size: </Label>
<div class="col-sm-9">
@ -49,6 +55,7 @@
private string _name;
private List<Folder> _folders;
private int _folderId = -1;
private string _description = string.Empty;
private int _size;
private string _createdBy;
private DateTime _createdOn;
@ -70,6 +77,7 @@
{
_name = file.Name;
_folderId = file.FolderId;
_description = file.Description;
_size = file.Size;
_createdBy = file.CreatedBy;
_createdOn = file.CreatedOn;
@ -97,6 +105,7 @@
File file = await FileService.GetFileAsync(_fileId);
file.Name = _name;
file.FolderId = _folderId;
file.Description = _description;
file = await FileService.UpdateFileAsync(file);
await logger.LogInformation("File Saved {File}", file);
NavigationManager.NavigateTo(NavigateUrl());

View File

@ -28,7 +28,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" maxlength="50" required />
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -47,6 +47,18 @@
}
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="imagesizes" HelpText="Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)" ResourceKey="ImageSizes">Image Sizes: </Label>
<div class="col-sm-9">
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="capacity" HelpText="Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited." ResourceKey="Capacity">Capacity: </Label>
<div class="col-sm-9">
<input id="capacity" class="form-control" @bind="@_capacity" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<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>
@ -84,6 +96,8 @@
private int _parentId = -1;
private string _name;
private string _type = FolderTypes.Private;
private string _imagesizes = string.Empty;
private string _capacity = "0";
private bool _isSystem;
private string _permissions = string.Empty;
private string _createdBy;
@ -114,6 +128,8 @@
_parentId = folder.ParentId ?? -1;
_name = folder.Name;
_type = folder.Type;
_imagesizes = folder.ImageSizes;
_capacity = folder.Capacity.ToString();
_isSystem = folder.IsSystem;
_permissions = folder.Permissions;
_createdBy = folder.CreatedBy;
@ -125,7 +141,6 @@
else
{
_parentId = _folders[0].FolderId;
_permissions = string.Empty;
}
}
catch (Exception ex)
@ -178,6 +193,8 @@
folder.Name = _name;
folder.Type = _type;
folder.ImageSizes = _imagesizes;
folder.Capacity = int.Parse(_capacity);
folder.IsSystem = _isSystem;
folder.Permissions = _permissionGrid.GetPermissions();

View File

@ -80,17 +80,18 @@ else
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price > 0 && !string.IsNullOrEmpty(context.PackageUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price > 0 && !string.IsNullOrEmpty(context.PaymentUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.ToString("$#,##0.00")</a>
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
}
else
{

View File

@ -29,9 +29,9 @@ else
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
<td>
@if (UpgradeAvailable(context.Code))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
}
}
</td>
</Row>
</Pager>

View File

@ -36,18 +36,19 @@
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price > 0 && !string.IsNullOrEmpty(context.PackageUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price > 0 && !string.IsNullOrEmpty(context.PaymentUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.ToString("$#,##0.00")</a>
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
}
else
{

View File

@ -22,6 +22,7 @@ else
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th>
<th>@SharedLocalizer["Version"]</th>
<th>@SharedLocalizer["Expires"]</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
@ -34,11 +35,14 @@ else
</td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@((MarkupString)PurchaseLink(context.PackageName))
</td>
<td>
@if (UpgradeAvailable(context.PackageName, context.Version))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
}
}
</td>
</Row>
</Pager>
@ -67,6 +71,27 @@ else
}
}
private string PurchaseLink(string packagename)
{
string link = "";
if (!string.IsNullOrEmpty(packagename) && _packages != null)
{
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null)
{
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
if (!string.IsNullOrEmpty(package.PaymentUrl))
{
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
}
}
}
}
return link;
}
private bool UpgradeAvailable(string packagename, string version)
{
var upgradeavailable = false;

View File

@ -322,6 +322,12 @@
}
}
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
{
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
return;
}
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
@ -412,4 +418,9 @@
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
}
private static bool PagePathIsDeleted(string pagePath, int siteId, List<Page> existingPages)
{
return existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.IsDeleted == true);
}
}

View File

@ -22,7 +22,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label>
<div class="col-sm-9">
<select id="isautoassigned" class="form-select" @bind="@_isautoassigned">
<select id="isautoassigned" class="form-select" @bind="@_isautoassigned" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
@ -72,7 +72,7 @@
}
else
{
AddModuleMessage(Localizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}

View File

@ -22,7 +22,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label>
<div class="col-sm-9">
<select id="isautoassigned" class="form-select" @bind="@_isautoassigned">
<select id="isautoassigned" class="form-select" @bind="@_isautoassigned" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
@ -101,7 +101,7 @@
}
else
{
AddModuleMessage(Localizer["Message.InfoRequired"], MessageType.Warning);
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
}

View File

@ -12,65 +12,70 @@
else
{
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label>
<div class="col-sm-9">
<input id="role" class="form-control" @bind="@name" disabled />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label>
<div class="col-sm-9">
<input id="role" class="form-control" @bind="@name" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
<div class="col-sm-9">
<select id="user" class="form-select" @bind="@userid" required>
<option value="-1">&lt;@Localizer["User.Select"]&gt;</option>
@foreach (UserRole userrole in users)
{
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<div class="col-sm-9">
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<div class="col-sm-9">
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" required />
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<hr class="app-rule" />
<div class="row mb-1 align-items-center">
<p align="center">
<Pager Items="@userroles">
<Header>
<th>@Localizer["Users"]</th>
<th>@Localizer["Effective"]</th>
<th>@Localizer["Expiry"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.User.DisplayName</td>
<td>@context.EffectiveDate</td>
<td>@context.ExpiryDate</td>
<td>
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || PageState.User.Username == UserNames.Host)" ResourceKey="DeleteUserRole" />
</td>
</Row>
</Pager>
</p>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
<div class="col-sm-9">
<select id="user" class="form-select" @bind="@userid">
<option value="-1">&lt;@Localizer["User.Select"]&gt;</option>
@foreach (UserRole userrole in users)
{
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<div class="col-sm-9">
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<div class="col-sm-9">
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<hr class="app-rule" />
<div class="row mb-1 align-items-center">
<p align="center">
<Pager Items="@userroles">
<Header>
<th>@Localizer["Users"]</th>
<th>@Localizer["Effective"]</th>
<th>@Localizer["Expiry"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.User.DisplayName</td>
<td>@context.EffectiveDate</td>
<td>@context.ExpiryDate</td>
<td>
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@context.Role.IsAutoAssigned" ResourceKey="DeleteUserRole" />
</td>
</Row>
</Pager>
</p>
</div>
</div>
</form>
}
@code {
private ElementReference form;
private bool validated = false;
private int roleid;
private string name = string.Empty;
private List<UserRole> users;
@ -118,59 +123,78 @@ else
private async Task SaveUserRole()
{
try
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
if (userid != -1)
try
{
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
if (userrole != null)
if (userid != -1)
{
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.UpdateUserRoleAsync(userrole);
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
if (userrole != null)
{
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.UpdateUserRoleAsync(userrole);
}
else
{
userrole = new UserRole();
userrole.UserId = userid;
userrole.RoleId = roleid;
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.AddUserRoleAsync(userrole);
}
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
}
else
{
userrole = new UserRole();
userrole.UserId = userid;
userrole.RoleId = roleid;
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.AddUserRoleAsync(userrole);
AddModuleMessage(Localizer["Message.Required.UserSelect"], MessageType.Warning);
}
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
}
else
catch (Exception ex)
{
AddModuleMessage(Localizer["Message.Required.UserSelect"], MessageType.Warning);
await logger.LogError(ex, "Error Saving User Roles {RoleId} {Error}", roleid, ex.Message);
AddModuleMessage(Localizer["Error.User.SaveRole"], MessageType.Error);
}
}
catch (Exception ex)
else
{
await logger.LogError(ex, "Error Saving User Roles {RoleId} {Error}", roleid, ex.Message);
AddModuleMessage(Localizer["Error.User.SaveRole"], MessageType.Error);
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task DeleteUserRole(int UserRoleId)
{
try
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
try
{
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
}
}
catch (Exception ex)
else
{
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
}

View File

@ -44,12 +44,6 @@
<input id="installationid" class="form-control" @bind="@_installationid" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-3"></div>
<div class="col-sm-9">
<br /><input type="checkbox" @onchange="(e => RegisterChecked(e))" /> @Localizer["Register"]
</div>
</div>
</div>
<br /><br />
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
@ -196,20 +190,4 @@
await logger.LogError(ex, "Error Restarting Application");
}
}
private async Task RegisterChecked(ChangeEventArgs e)
{
try
{
if ((bool)e.Value)
{
await InstallationService.RegisterAsync(PageState.User.Email);
AddModuleMessage(Localizer["Success.Register"], MessageType.Success);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On Register");
}
}
}

View File

@ -37,17 +37,18 @@
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"]&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong>&nbsp;&nbsp;|&nbsp;&nbsp;
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
@((MarkupString)(context.TrialPeriod > 0 ? "&nbsp;&nbsp;|&nbsp;&nbsp;<strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price > 0 && !string.IsNullOrEmpty(context.PackageUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
{
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
}
</td>
<td style="width: 1px; vertical-align: middle;">
@if (context.Price > 0 && !string.IsNullOrEmpty(context.PaymentUrl))
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.ToString("$#,##0.00")</a>
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
}
else
{

View File

@ -21,8 +21,9 @@ else
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th scope="col">@SharedLocalizer["Name"]</th>
<th scope="col">@SharedLocalizer["Version"]</th>
<th>@SharedLocalizer["Name"]</th>
<th>@SharedLocalizer["Version"]</th>
<th>@SharedLocalizer["Expires"]</th>
<th>&nbsp;</th>
</Header>
<Row>
@ -35,11 +36,14 @@ else
</td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@((MarkupString)PurchaseLink(context.PackageName))
</td>
<td>
@if (UpgradeAvailable(context.PackageName, context.Version))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.PackageName, context.Version))>@SharedLocalizer["Upgrade"]</button>
}
}
</td>
<td></td>
</Row>
@ -69,6 +73,27 @@ else
}
}
private string PurchaseLink(string packagename)
{
string link = "";
if (!string.IsNullOrEmpty(packagename) && _packages != null)
{
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null)
{
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
if (!string.IsNullOrEmpty(package.PaymentUrl))
{
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
}
}
}
}
return link;
}
private bool UpgradeAvailable(string packagename, string version)
{
var upgradeavailable = false;

View File

@ -6,12 +6,13 @@
@inject ISettingService SettingService
@inject INotificationService NotificationService
@inject IFileService FileService
@inject IFolderService FolderService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (PageState.User != null && photo != null)
{
<img src="@photo.Url" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
<img src="@ImageUrl(photofileid, "400x400", "crop")" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
}
else
{
@ -19,7 +20,7 @@ else
}
<TabStrip>
<TabPanel Name="Identity" ResourceKey="Identity">
@if (PageState.User != null)
@if (profiles != null && settings != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
@ -55,7 +56,7 @@ else
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
<div class="col-sm-9">
<FileManager FileId="@photofileid" @ref="filemanager" />
<FileManager FileId="@photofileid" Filter="@Constants.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
</div>
</div>
</div>
@ -67,8 +68,6 @@ else
<TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null && settings != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
@foreach (Profile profile in profiles)
@ -132,11 +131,11 @@ else
{
<Pager Items="@notifications">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["From"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Received"]</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["From"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Received"]</th>
</Header>
<Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></td>
@ -165,11 +164,11 @@ else
{
<Pager Items="@notifications">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>@Localizer["To"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Sent"]</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>@Localizer["To"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Sent"]</th>
</Header>
<Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></td>
@ -210,6 +209,7 @@ else
private string email = string.Empty;
private string displayname = string.Empty;
private FileManager filemanager;
private int folderid = -1;
private int photofileid = -1;
private File photo = null;
private List<Profile> profiles;
@ -230,6 +230,13 @@ else
email = PageState.User.Email;
displayname = PageState.User.DisplayName;
// get user folder
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
if (folder != null)
{
folderid = folder.FolderId;
}
if (PageState.User.PhotoFileId != null)
{
photofileid = PageState.User.PhotoFileId.Value;

View File

@ -59,7 +59,7 @@
</div>
}
</div>
@if (_image != string.Empty)
@if (_image != string.Empty && ShowImage)
{
<div class="col-auto">
@((MarkupString) _image)
@ -110,6 +110,9 @@
[Parameter]
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
[Parameter]
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
[Parameter]
public int FileId { get; set; } = -1; // optional - for setting a specific file by default

View File

@ -2,45 +2,57 @@
@inherits ModuleControlBase
@typeparam TableItem
<p>
@if (Toolbar == "Top")
@if (ItemList != null)
{
@if (Toolbar == "Top" && _pages > 0 && Items.Count() > _maxItems)
{
<div class="mx-auto text-center">
@if (_endPage > 1)
<ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{
<button class="btn btn-secondary m-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
</li>
}
@if (_page > _maxPages)
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
</li>
@for (int i = _startPage; i <= _endPage; i++)
{
<button class="btn btn-secondary m-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary m-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++)
var pager = i;
if (pager == _page)
{
var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))>
@pager
</button>
<li class="page-item active">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
else
{
<li class="page-item">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
<button class="btn btn-secondary m-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{
<button class="btn btn-secondary m-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
</li>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary m-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<span class="btn btn-link disabled">Page @_page of @_pages</span>
}
</div>
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">
<a class="page-link">Page @_page of @_pages</a>
</li>
</ul>
}
@if (Format == "Table")
@if (Format == "Table" && Row != null)
{
<table class="@Class">
<thead>
@ -58,90 +70,125 @@
</tbody>
</table>
}
@if (Format == "Grid")
@if (Format == "Grid" && Row != null)
{
int count = 0;
if (ItemList != null)
{
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
}
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
@if (Header != null)
{
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<div class="row">@Detail(item)</div>
}
<div class="row"><div class="col">@Header</div></div>
}
@for (int row = 0; row < (count / _columns); row++)
{
<div class="row">
@for (int col = 0; col < _columns; col++)
{
int index = (row * _columns) + col;
if (index < ItemList.Count())
{
<div class="col">@Row(ItemList.ElementAt(index))</div>
}
else
{
<div class="col">&nbsp;</div>
}
}
</div>
}
</div>
}
@if (Toolbar == "Bottom")
@if (Toolbar == "Bottom" && _pages > 0 && Items.Count() > _maxItems)
{
<div class="mx-auto text-center">
@if (_endPage > 1)
<ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
</li>
}
@if (_page > _maxPages)
<li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
</li>
@for (int i = _startPage; i <= _endPage; i++)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++)
var pager = i;
if (pager == _page)
{
var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))>
@pager
</button>
<li class="page-item active">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
else
{
<li class="page-item">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
</li>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<span class="btn btn-link disabled">Page @_page of @_pages</span>
}
</div>
<li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">
<a class="page-link">Page @_page of @_pages</a>
</li>
</ul>
}
</p>
}
@code {
private int _pages = 0;
private int _page = 1;
private int _maxItems = 10;
private int _maxPages = 5;
private int _displayPages = 5;
private int _startPage = 0;
private int _endPage = 0;
private int _columns = 1;
[Parameter]
public string Format { get; set; }
public string Format { get; set; } // Table or Grid
[Parameter]
public string Toolbar { get; set; }
public string Toolbar { get; set; } // Top or Bottom
[Parameter]
public RenderFragment Header { get; set; }
public RenderFragment Header { get; set; } = null;
[Parameter]
public RenderFragment<TableItem> Row { get; set; }
public RenderFragment<TableItem> Row { get; set; } = null;
[Parameter]
public RenderFragment<TableItem> Detail { get; set; }
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
[Parameter]
public IEnumerable<TableItem> Items { get; set; }
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
[Parameter]
public string PageSize { get; set; }
public string PageSize { get; set; } // number of items to display on a page
[Parameter]
public string DisplayPages { get; set; }
public string Columns { get; set; } // only applicable to Grid layouts
[Parameter]
public string CurrentPage { get; set; } // optional property to set the initial page to display
[Parameter]
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
[Parameter]
public string Class { get; set; }
@ -177,86 +224,89 @@
_maxItems = int.Parse(PageSize);
}
if (!string.IsNullOrEmpty(DisplayPages))
if (!string.IsNullOrEmpty(Columns))
{
_maxPages = int.Parse(DisplayPages);
_columns = int.Parse(Columns);
}
if (!string.IsNullOrEmpty(DisplayPages))
{
_displayPages = int.Parse(DisplayPages);
}
if (!string.IsNullOrEmpty(CurrentPage))
{
_page = int.Parse(CurrentPage);
}
else
{
_page = 1;
}
_page = 1;
_startPage = 0;
_endPage = 0;
if (Items != null)
{
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
_pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems);
if (_page > _pages)
{
_page = _pages;
}
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
SetPagerSize();
}
SetPagerSize("forward");
}
public void UpdateList(int currentPage)
public void SetPagerSize()
{
ItemList = Items.Skip((currentPage - 1) * _maxItems).Take(_maxItems);
_page = currentPage;
_startPage = ((_page - 1) / _displayPages) * _displayPages + 1;
_endPage = _startPage + _displayPages - 1;
if (_endPage > _pages)
{
_endPage = _pages;
}
StateHasChanged();
}
public void SetPagerSize(string direction)
public void UpdateList(int page)
{
if (direction == "forward")
{
if (_endPage + 1 < _pages)
{
_startPage = _endPage + 1;
}
else
{
_startPage = 1;
}
ItemList = Items.Skip((page - 1) * _maxItems).Take(_maxItems);
_page = page;
SetPagerSize();
}
if (_endPage + _maxPages < _pages)
{
_endPage = _startPage + _maxPages - 1;
}
else
{
_endPage = _pages;
}
StateHasChanged();
}
else if (direction == "back")
public void SkipPages(string direction)
{
switch (direction)
{
_endPage = _startPage - 1;
_startPage = _startPage - _maxPages;
case "forward":
_page = _endPage + 1;
break;
case "back":
_page = _startPage - 1;
break;
}
SetPagerSize();
}
public void NavigateToPage(string direction)
{
if (direction == "next")
switch (direction)
{
if (_page < _pages)
{
if (_page == _endPage)
case "next":
if (_page < _pages)
{
SetPagerSize("forward");
_page += 1;
}
_page += 1;
}
}
else if (direction == "previous")
{
if (_page > 1)
{
if (_page == _startPage)
break;
case "previous":
if (_page > 1)
{
SetPagerSize("back");
_page -= 1;
}
_page -= 1;
}
break;
}
UpdateList(_page);

View File

@ -134,6 +134,11 @@ namespace Oqtane.Modules
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
}
public string ImageUrl(int fileid, string size, string mode)
{
return Utilities.ImageUrl(PageState.Alias, fileid, size, mode);
}
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
{
var urlParameters = new Dictionary<string, string>();

View File

@ -144,4 +144,10 @@
<data name="Size.Text" xml:space="preserve">
<value>Size: </value>
</data>
<data name="Description.HelpText" xml:space="preserve">
<value>A description of the file. This can be used as a caption for image files.</value>
</data>
<data name="Description.Text" xml:space="preserve">
<value>Description:</value>
</data>
</root>

View File

@ -171,4 +171,16 @@
<data name="Type.Text" xml:space="preserve">
<value>Type:</value>
</data>
<data name="Capacity.HelpText" xml:space="preserve">
<value>Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited.</value>
</data>
<data name="Capacity.Text" xml:space="preserve">
<value>Capacity:</value>
</data>
<data name="ImageSizes.HelpText" xml:space="preserve">
<value>Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)</value>
</data>
<data name="ImageSizes.Text" xml:space="preserve">
<value>Image Sizes:</value>
</data>
</root>

View File

@ -228,4 +228,7 @@
<data name="Appearance.Name" xml:space="preserve">
<value>Appearance</value>
</data>
<data name="Message.Page.Deleted" xml:space="preserve">
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
</data>
</root>

View File

@ -300,4 +300,13 @@
<data name="Review License Terms" xml:space="preserve">
<value>Review License Terms</value>
</data>
<data name="Trial" xml:space="preserve">
<value>Day Trial</value>
</data>
<data name="Expires" xml:space="preserve">
<value>Expires</value>
</data>
<data name="Extend" xml:space="preserve">
<value>Extend</value>
</data>
</root>

View File

@ -96,7 +96,7 @@ namespace Oqtane.Themes.Controls
{
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
string url = NavigateUrl();
string url = NavigateUrl(true);
if (action.Action != null)
{
@ -115,7 +115,7 @@ namespace Oqtane.Themes.Controls
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
return NavigateUrl(url, true);
return url;
}
private async Task<string> DeleteModule(string url, PageModule pagemodule)
@ -123,7 +123,7 @@ namespace Oqtane.Themes.Controls
pagemodule.IsDeleted = true;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(url, true);
return url;
}
private async Task<string> Settings(string url, PageModule pagemodule)
@ -133,7 +133,7 @@ namespace Oqtane.Themes.Controls
return url;
}
private async Task<string> Publish(string s, PageModule pagemodule)
private async Task<string> Publish(string url, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
@ -148,10 +148,10 @@ namespace Oqtane.Themes.Controls
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, true);
return url;
}
private async Task<string> Unpublish(string s, PageModule pagemodule)
private async Task<string> Unpublish(string url, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
@ -166,39 +166,39 @@ namespace Oqtane.Themes.Controls
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, true);
return url;
}
private async Task<string> MoveTop(string s, PageModule pagemodule)
private async Task<string> MoveTop(string url, PageModule pagemodule)
{
pagemodule.Order = 0;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true);
return url;
}
private async Task<string> MoveBottom(string s, PageModule pagemodule)
private async Task<string> MoveBottom(string url, PageModule pagemodule)
{
pagemodule.Order = int.MaxValue;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true);
return url;
}
private async Task<string> MoveUp(string s, PageModule pagemodule)
private async Task<string> MoveUp(string url, PageModule pagemodule)
{
pagemodule.Order -= 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true);
return url;
}
private async Task<string> MoveDown(string s, PageModule pagemodule)
private async Task<string> MoveDown(string url, PageModule pagemodule)
{
pagemodule.Order += 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, true);
return url;
}
public class ActionViewModel

View File

@ -400,17 +400,17 @@
await PageModuleService.AddPageModuleAsync(pageModule);
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
Message = $"<br /><div class=\"alert alert-success\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
Message = $"<br /><div class=\"alert alert-warning\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
Message = $"<div class=\"alert alert-warning mt-2 text-center\" role=\"alert\">{Localizer["Message.Require.ModuleSelect"]}</div>";
}
}
else
{
Message = $"<br /><div class=\"alert alert-error\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
}
}

View File

@ -103,5 +103,10 @@ namespace Oqtane.Themes
{
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
}
public string ImageUrl(int fileid, string size, string mode)
{
return Utilities.ImageUrl(PageState.Alias, fileid, size, mode);
}
}
}

View File

@ -1,6 +1,6 @@
@namespace Oqtane.UI
<CascadingValue Value="@ModuleState" IsFixed="true">
<CascadingValue Value="@ModuleState" IsFixed="@(!PageState.EditMode)">
@if (_useadminborder)
{
<div class="app-pane-admin-border">

View File

@ -138,6 +138,7 @@
if (authState.User.Identity.IsAuthenticated)
{
user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId);
user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
}
}
else

View File

@ -17,6 +17,9 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Extensions;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
// ReSharper disable StringIndexOfIsCultureSpecific.1
@ -71,7 +74,7 @@ namespace Oqtane.Controllers
{
foreach (string file in Directory.GetFiles(folder))
{
files.Add(new Models.File {Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "")});
files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "") });
}
}
}
@ -169,7 +172,11 @@ namespace Oqtane.Controllers
string filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
System.IO.File.Delete(filepath);
// remove file and thumbnails
foreach(var f in Directory.GetFiles(Path.GetDirectoryName(filepath), Path.GetFileNameWithoutExtension(filepath) + ".*"))
{
System.IO.File.Delete(f);
}
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
@ -194,7 +201,7 @@ namespace Oqtane.Controllers
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.Permissions))
{
string folderPath = _folders.GetFolderPath(folder);
CreateDirectory(folderPath);
@ -226,7 +233,11 @@ namespace Oqtane.Controllers
}
client.DownloadFile(url, targetPath);
file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
file = CreateFile(filename, folder.FolderId, targetPath);
if (file != null)
{
file = _files.AddFile(file);
}
}
catch
{
@ -235,7 +246,7 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {FolderId} {Url}", folderid, url);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Download Attempt {FolderId} {Url}", folderid, url);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
@ -244,16 +255,16 @@ namespace Oqtane.Controllers
// POST api/<controller>/upload
[HttpPost("upload")]
public async Task UploadFile(string folder, IFormFile file)
public async Task UploadFile(string folder, IFormFile formfile)
{
if (file.Length <= 0)
if (formfile.Length <= 0)
{
return;
}
if (!file.FileName.IsPathOrFileValid())
if (!formfile.FileName.IsPathOrFileValid())
{
HttpContext.Response.StatusCode = (int) HttpStatusCode.Conflict;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return;
}
@ -280,20 +291,24 @@ namespace Oqtane.Controllers
if (!string.IsNullOrEmpty(folderPath))
{
CreateDirectory(folderPath);
using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create))
using (var stream = new FileStream(Path.Combine(folderPath, formfile.FileName), FileMode.Create))
{
await file.CopyToAsync(stream);
await formfile.CopyToAsync(stream);
}
string upload = await MergeFile(folderPath, file.FileName);
string upload = await MergeFile(folderPath, formfile.FileName);
if (upload != "" && FolderId != -1)
{
_files.AddFile(CreateFile(upload, FolderId, Path.Combine(folderPath, upload)));
var file = CreateFile(upload, FolderId, Path.Combine(folderPath, upload));
if (file != null)
{
_files.AddFile(file);
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, file);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, formfile.FileName);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
@ -479,6 +494,99 @@ namespace Oqtane.Controllers
return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
}
[HttpGet("image/{id}/{size}/{mode?}")]
public IActionResult GetImage(int id, string size, string mode)
{
var file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
{
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
{
var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
size = size.ToLower();
mode = (string.IsNullOrEmpty(mode)) ? "crop" : mode;
if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.Permissions) ||
size.Contains("x") && !string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(size))
&& Enum.TryParse(mode, true, out ResizeMode resizemode))
{
var imagepath = CreateImage(filepath, size, resizemode.ToString());
if (!string.IsNullOrEmpty(imagepath))
{
return PhysicalFile(imagepath, file.GetMimeType());
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Error Creating Image For File {File} {Size}", file, size);
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Invalid Image Size For Folder Or Invalid Mode Specification {Folder} {Size} {Mode}", file.Folder, size, mode);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Is Not An Image {File}", file);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt {FileId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
string errorPath = Path.Combine(GetFolderPath("images"), "error.png");
return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null;
}
private string CreateImage(string filepath, string size, string mode)
{
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + size + "." + mode.ToLower() + ".png");
if (!System.IO.File.Exists(imagepath))
{
try
{
FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
using (Image image = Image.Load(stream))
{
var parts = size.Split('x');
int width = (!string.IsNullOrEmpty(parts[0])) ? int.Parse(parts[0]) : 0;
int height = (!string.IsNullOrEmpty(parts[1])) ? int.Parse(parts[1]) : 0;
Enum.TryParse(mode, true, out ResizeMode resizemode);
image.Mutate(x =>
x.Resize(new ResizeOptions
{
Size = new Size(width, height),
Mode = resizemode
})
.BackgroundColor(new Rgba32(255, 255, 255, 0)));
image.Save(imagepath, new PngEncoder());
}
stream.Close();
}
catch // error creating image
{
imagepath = "";
}
}
return imagepath;
}
private string GetFolderPath(string folder)
{
return Utilities.PathCombine(_environment.ContentRootPath, folder);
@ -489,7 +597,7 @@ namespace Oqtane.Controllers
if (!Directory.Exists(folderpath))
{
string path = "";
var separators = new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
foreach (string folder in folders)
{
@ -504,25 +612,52 @@ namespace Oqtane.Controllers
private Models.File CreateFile(string filename, int folderid, string filepath)
{
Models.File file = new Models.File();
file.Name = filename;
file.FolderId = folderid;
Models.File file = null;
int size = 0;
var folder = _folders.GetFolder(folderid);
if (folder.Capacity != 0)
{
foreach (var f in _files.GetFiles(folderid))
{
size += f.Size;
}
}
FileInfo fileinfo = new FileInfo(filepath);
file.Extension = fileinfo.Extension.ToLower().Replace(".", "");
file.Size = (int) fileinfo.Length;
file.ImageHeight = 0;
file.ImageWidth = 0;
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
if (folder.Capacity == 0 || ((size + fileinfo.Length) / 1000000) < folder.Capacity)
{
FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
using (Image image = Image.Load(stream))
file = new Models.File();
file.Name = filename;
file.FolderId = folderid;
file.Extension = fileinfo.Extension.ToLower().Replace(".", "");
file.Size = (int)fileinfo.Length;
file.ImageHeight = 0;
file.ImageWidth = 0;
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
{
file.ImageHeight = image.Height;
file.ImageWidth = image.Width;
try
{
FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
using (Image image = Image.Load(stream))
{
file.ImageHeight = image.Height;
file.ImageWidth = image.Width;
}
stream.Close();
}
catch
{
// error opening image file
}
}
stream.Close();
}
else
{
System.IO.File.Delete(filepath);
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "File Exceeds Folder Capacity {Folder} {File}", folder, filepath);
}
return file;

View File

@ -187,8 +187,10 @@ namespace Oqtane.Controllers
ParentId = folder.FolderId,
Name = "My Folder",
Type = FolderTypes.Private,
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),Path.DirectorySeparatorChar.ToString()),
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
Order = 1,
ImageSizes = "",
Capacity = Constants.UserFolderCapacity,
IsSystem = true,
Permissions = new List<Permission>
{
@ -196,7 +198,7 @@ namespace Oqtane.Controllers
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.Edit, newUser.UserId, true)
}.EncodePermissions()
});
}) ;
}
}
}

View File

@ -250,6 +250,7 @@ namespace Oqtane.Infrastructure
catch (Exception ex)
{
result.Message = ex.Message;
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
return result;
@ -288,6 +289,7 @@ namespace Oqtane.Infrastructure
catch (Exception ex)
{
result.Message = ex.Message;
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
}
else
@ -328,6 +330,7 @@ namespace Oqtane.Infrastructure
catch (Exception ex)
{
result.Message = ex.Message;
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
}
}
@ -432,6 +435,7 @@ namespace Oqtane.Infrastructure
catch (Exception ex)
{
result.Message = ex.Message;
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
// execute any version specific upgrade logic
@ -539,6 +543,10 @@ namespace Oqtane.Infrastructure
{
result.Success = true;
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
return result;
}
@ -549,110 +557,126 @@ namespace Oqtane.Infrastructure
if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases) && !string.IsNullOrEmpty(install.SiteName))
{
using (var scope = _serviceScopeFactory.CreateScope())
try
{
// set the alias explicitly so the tenant can be resolved
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
var firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
tenantManager.SetAlias(alias);
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName);
if (site == null)
using (var scope = _serviceScopeFactory.CreateScope())
{
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
var users = scope.ServiceProvider.GetRequiredService<IUserRepository>();
var roles = scope.ServiceProvider.GetRequiredService<IRoleRepository>();
var userRoles = scope.ServiceProvider.GetRequiredService<IUserRoleRepository>();
var folders = scope.ServiceProvider.GetRequiredService<IFolderRepository>();
var log = scope.ServiceProvider.GetRequiredService<ILogManager>();
var identityUserManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
// set the alias explicitly so the tenant can be resolved
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
var firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
tenantManager.SetAlias(alias);
var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName);
site = new Site
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName);
if (site == null)
{
TenantId = tenant.TenantId,
Name = install.SiteName,
LogoFileId = null,
DefaultThemeType = (!string.IsNullOrEmpty(install.DefaultTheme)) ? install.DefaultTheme : Constants.DefaultTheme,
DefaultContainerType = (!string.IsNullOrEmpty(install.DefaultContainer)) ? install.DefaultContainer : Constants.DefaultContainer,
AdminContainerType = (!string.IsNullOrEmpty(install.DefaultAdminContainer)) ? install.DefaultAdminContainer : Constants.DefaultAdminContainer,
SiteTemplateType = install.SiteTemplate
};
site = sites.AddSite(site);
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
var users = scope.ServiceProvider.GetRequiredService<IUserRepository>();
var roles = scope.ServiceProvider.GetRequiredService<IRoleRepository>();
var userRoles = scope.ServiceProvider.GetRequiredService<IUserRoleRepository>();
var folders = scope.ServiceProvider.GetRequiredService<IFolderRepository>();
var log = scope.ServiceProvider.GetRequiredService<ILogManager>();
var identityUserManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
if (!string.IsNullOrEmpty(install.HostUsername))
{
var identityUser = identityUserManager.FindByNameAsync(install.HostUsername).GetAwaiter().GetResult();
if (identityUser == null)
var tenant = tenants.GetTenants().FirstOrDefault(item => item.Name == install.TenantName);
site = new Site
{
identityUser = new IdentityUser { UserName = install.HostUsername, Email = install.HostEmail, EmailConfirmed = true };
var create = identityUserManager.CreateAsync(identityUser, install.HostPassword).GetAwaiter().GetResult();
if (create.Succeeded)
TenantId = tenant.TenantId,
Name = install.SiteName,
LogoFileId = null,
DefaultThemeType = (!string.IsNullOrEmpty(install.DefaultTheme)) ? install.DefaultTheme : Constants.DefaultTheme,
DefaultContainerType = (!string.IsNullOrEmpty(install.DefaultContainer)) ? install.DefaultContainer : Constants.DefaultContainer,
AdminContainerType = (!string.IsNullOrEmpty(install.DefaultAdminContainer)) ? install.DefaultAdminContainer : Constants.DefaultAdminContainer,
SiteTemplateType = install.SiteTemplate
};
site = sites.AddSite(site);
if (!string.IsNullOrEmpty(install.HostUsername))
{
var identityUser = identityUserManager.FindByNameAsync(install.HostUsername).GetAwaiter().GetResult();
if (identityUser == null)
{
var user = new User
identityUser = new IdentityUser { UserName = install.HostUsername, Email = install.HostEmail, EmailConfirmed = true };
var create = identityUserManager.CreateAsync(identityUser, install.HostPassword).GetAwaiter().GetResult();
if (create.Succeeded)
{
SiteId = site.SiteId,
Username = install.HostUsername,
Password = install.HostPassword,
Email = install.HostEmail,
DisplayName = install.HostName,
LastIPAddress = "",
LastLoginOn = null
};
user = users.AddUser(user);
var hostRoleId = roles.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == RoleNames.Host)?.RoleId ?? 0;
var userRole = new UserRole { UserId = user.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null };
userRoles.AddUserRole(userRole);
// add user folder
var folder = folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", Path.DirectorySeparatorChar.ToString()));
if (folder != null)
{
folders.AddFolder(new Folder
var user = new User
{
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Type = FolderTypes.Private,
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
Order = 1,
IsSystem = true,
Permissions = new List<Permission>
SiteId = site.SiteId,
Username = install.HostUsername,
Password = install.HostPassword,
Email = install.HostEmail,
DisplayName = install.HostName,
LastIPAddress = "",
LastLoginOn = null
};
user = users.AddUser(user);
var hostRoleId = roles.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == RoleNames.Host)?.RoleId ?? 0;
var userRole = new UserRole { UserId = user.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null };
userRoles.AddUserRole(userRole);
// add user folder
var folder = folders.GetFolder(user.SiteId, Utilities.PathCombine("Users", Path.DirectorySeparatorChar.ToString()));
if (folder != null)
{
new Permission(PermissionNames.Browse, user.UserId, true),
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.Edit, user.UserId, true),
}.EncodePermissions(),
});
folders.AddFolder(new Folder
{
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Type = FolderTypes.Private,
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
Order = 1,
ImageSizes = "",
Capacity = Constants.UserFolderCapacity,
IsSystem = true,
Permissions = new List<Permission>
{
new Permission(PermissionNames.Browse, user.UserId, true),
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.Edit, user.UserId, true),
}.EncodePermissions(),
});
}
}
}
}
}
foreach (var aliasName in install.Aliases.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries))
{
alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasName);
if (alias != null)
foreach (var aliasName in install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
alias.SiteId = site.SiteId;
aliases.UpdateAlias(alias);
alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasName);
if (alias != null)
{
alias.SiteId = site.SiteId;
aliases.UpdateAlias(alias);
}
}
tenant.Version = Constants.Version;
tenants.UpdateTenant(tenant);
if (site != null) log.Log(site.SiteId, Shared.LogLevel.Information, this, LogFunction.Create, "Site Created {Site}", site);
}
tenant.Version = Constants.Version;
tenants.UpdateTenant(tenant);
if (site != null) log.Log(site.SiteId, Shared.LogLevel.Information, this, LogFunction.Create, "Site Created {Site}", site);
}
}
catch (Exception ex)
{
result.Message = "An Error Occurred Creating Site - " + ex.Message;
}
}
result.Success = true;
if (string.IsNullOrEmpty(result.Message))
{
result.Success = true;
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
return result;
}

View File

@ -55,7 +55,7 @@ namespace Oqtane.Infrastructure
var filepath = Path.Combine(folder, "error.log");
// only retain an error log for the current day as it is intended for development purposes
if (File.GetCreationTime(filepath).ToUniversalTime().Date < DateTime.UtcNow.Date && File.Exists(filepath))
if (File.Exists(filepath) && File.GetLastWriteTimeUtc(filepath).Date < DateTime.UtcNow.Date)
{
File.Delete(filepath);
}

View File

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.03.00.01")]
public class AddFolderCapacity : MultiDatabaseMigration
{
public AddFolderCapacity(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
folderEntityBuilder.AddIntegerColumn("Capacity", true);
folderEntityBuilder.UpdateColumn("Capacity", "0");
folderEntityBuilder.UpdateColumn("Capacity", Constants.UserFolderCapacity.ToString(), $"{ActiveDatabase.RewriteName("Name")} = 'My Folder'");
folderEntityBuilder.AddStringColumn("ImageSizes", 512, true, true);
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
fileEntityBuilder.AddStringColumn("Description", 512, true, true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
folderEntityBuilder.DropColumn("ImageSizes");
folderEntityBuilder.DropColumn("Capacity");
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
fileEntityBuilder.DropColumn("Description");
}
}
}

View File

@ -124,7 +124,7 @@ namespace Oqtane.Repository
Folder folder = _folderRepository.AddFolder(new Folder
{
SiteId = site.SiteId, ParentId = null, Name = "Root", Type = FolderTypes.Private, Path = "", Order = 1, IsSystem = true,
SiteId = site.SiteId, ParentId = null, Name = "Root", Type = FolderTypes.Private, Path = "", Order = 1, ImageSizes = "", Capacity = 0, IsSystem = true,
Permissions = new List<Permission>
{
new Permission(PermissionNames.Browse, RoleNames.Admin, true),
@ -132,7 +132,7 @@ namespace Oqtane.Repository
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions()
});
_folderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Public", Type = FolderTypes.Public, Path = Utilities.PathCombine("Public", Path.DirectorySeparatorChar.ToString()), Order = 1, IsSystem = false,
_folderRepository.AddFolder(new Folder { SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Public", Type = FolderTypes.Public, Path = Utilities.PathCombine("Public", Path.DirectorySeparatorChar.ToString()), Order = 1, ImageSizes = "", Capacity = 0, IsSystem = false,
Permissions = new List<Permission>
{
new Permission(PermissionNames.Browse, RoleNames.Admin, true),
@ -142,7 +142,7 @@ namespace Oqtane.Repository
});
_folderRepository.AddFolder(new Folder
{
SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Type = FolderTypes.Private, Path = Utilities.PathCombine("Users",Path.DirectorySeparatorChar.ToString()), Order = 3, IsSystem = true,
SiteId = site.SiteId, ParentId = folder.FolderId, Name = "Users", Type = FolderTypes.Private, Path = Utilities.PathCombine("Users",Path.DirectorySeparatorChar.ToString()), Order = 3, ImageSizes = "", Capacity = 0, IsSystem = true,
Permissions = new List<Permission>
{
new Permission(PermissionNames.Browse, RoleNames.Admin, true),

View File

@ -333,7 +333,7 @@ Oqtane.Interop = {
var data = new FormData();
data.append('folder', folder);
data.append('file', Chunk, FileName);
data.append('formfile', Chunk, FileName);
var request = new XMLHttpRequest();
request.open('POST', posturl, true);
request.upload.onloadstart = function (e) {

View File

@ -50,6 +50,11 @@ namespace Oqtane.Models
/// </summary>
public int ImageWidth { get; set; }
/// <summary>
/// Description of a file
/// </summary>
public string Description { get; set; }
#region IAuditable Properties
/// <inheritdoc />

View File

@ -45,7 +45,17 @@ namespace Oqtane.Models
public int Order { get; set; }
/// <summary>
/// TODO: unclear what this is for
/// List of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)
/// </summary>
public string ImageSizes { get; set; }
/// <summary>
/// Maximum folder capacity (in bytes)
/// </summary>
public int Capacity { get; set; }
/// <summary>
/// Folder is a dependency of the framework and cannot be modified or removed
/// </summary>
public bool IsSystem { get; set; }

View File

@ -67,14 +67,28 @@ namespace Oqtane.Models
/// </summary>
public int Vulnerabilities { get; set; }
#region Commercial Properties
/// <summary>
/// The price of the package
/// </summary>
public decimal Price { get; set; }
public decimal? Price { get; set; }
/// <summary>
/// The Url for purchasing the package ( if commercial )
/// The Url for purchasing the package
/// </summary>
public string PaymentUrl { get; set; }
/// <summary>
/// The trial period in days
/// </summary>
public int TrialPeriod { get; set; }
/// <summary>
/// The expiry date of the package
/// </summary>
public DateTime? ExpiryDate { get; set; }
#endregion
}
}

View File

@ -88,5 +88,14 @@ namespace Oqtane.Models
/// </summary>
[NotMapped]
public bool IsAuthenticated { get; set; }
/// <summary>
/// The path name of the user's personal folder
/// </summary>
[NotMapped]
public string FolderPath
{
get => "Users\\" + UserId.ToString() + "\\";
}
}
}

View File

@ -40,6 +40,8 @@ namespace Oqtane.Shared {
public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server";
public const string ContentUrl = "/api/file/download/";
public const string ImageUrl = "/api/file/image/";
public const int UserFolderCapacity = 20; // megabytes
[Obsolete("Use UserNames.Host instead.")]
public const string HostUser = UserNames.Host;

View File

@ -1,6 +1,7 @@
using Oqtane.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
@ -60,13 +61,16 @@ namespace Oqtane.Shared
string urlparameters;
string querystring;
string anchor;
(urlparameters, querystring, anchor) = ParseParameters(parameters);
// parse parameters
(urlparameters, querystring, anchor) = ParseParameters(parameters);
if (!string.IsNullOrEmpty(urlparameters))
{
if (urlparameters.StartsWith("/")) urlparameters = urlparameters.Remove(0, 1);
path += $"/{Constants.UrlParametersDelimiter}/{urlparameters}";
}
// build url
var uriBuilder = new UriBuilder
{
Path = !string.IsNullOrEmpty(alias)
@ -76,9 +80,9 @@ namespace Oqtane.Shared
: $"{path}",
Query = querystring,
};
anchor = string.IsNullOrEmpty(anchor) ? "" : "#" + anchor;
var navigateUrl = uriBuilder.Uri.PathAndQuery + anchor;
return navigateUrl;
return uriBuilder.Uri.PathAndQuery + anchor;
}
public static string EditUrl(string alias, string path, int moduleid, string action, string parameters)
@ -108,6 +112,12 @@ namespace Oqtane.Shared
return $"{aliasUrl}{Constants.ContentUrl}{fileId}{method}";
}
public static string ImageUrl(Alias alias, int fileId, string size, string mode)
{
var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
return $"{aliasUrl}{Constants.ImageUrl}{fileId}/{size}/{mode}";
}
public static string TenantUrl(Alias alias, string url)
{
url = (!url.StartsWith("/")) ? "/" + url : url;