Compare commits
149 Commits
Author | SHA1 | Date | |
---|---|---|---|
802ee8a1ff | |||
e312970212 | |||
c0f4069a9b | |||
654352827e | |||
7dd210976d | |||
5302be8bc1 | |||
9ed7181e28 | |||
23ae4b01cb | |||
59764d3378 | |||
b8e2c729c1 | |||
530d80a011 | |||
2d306e8fda | |||
b3d9a70fd1 | |||
b880207f61 | |||
ee76d02999 | |||
804c33a375 | |||
de784714d9 | |||
2404e26b61 | |||
b191fdda2c | |||
e8adfd45d2 | |||
7158595801 | |||
ba97f63338 | |||
62eca2aedc | |||
b15f6b1fa7 | |||
5b22de589c | |||
d1f50f12af | |||
d40c1d9b31 | |||
b69041d4af | |||
64c5d9a09f | |||
dd170bb41a | |||
1e6e4033f8 | |||
01fabc8d9e | |||
2ca2539b53 | |||
51e2e2966f | |||
55d02d2db5 | |||
282a0b0c44 | |||
8432779b23 | |||
13b9982461 | |||
d76b8cebdc | |||
80315ae6d4 | |||
95e7344286 | |||
28f73727b5 | |||
68f5bf5759 | |||
075748d697 | |||
e8d86f94f2 | |||
d6bb802892 | |||
52680e9002 | |||
d6385d82ae | |||
d058de067c | |||
32d6d143dd | |||
b49432802b | |||
99d4d75d8e | |||
1f584d57ac | |||
2c1543aa82 | |||
4390cbbfae | |||
bbf9e5717e | |||
c7edc28bd9 | |||
6e0de6f7bf | |||
3659422165 | |||
1af30da44e | |||
56c082cb26 | |||
e8eca582de | |||
4084b352de | |||
633e4acf0e | |||
468df15d80 | |||
f4537b4fcb | |||
8bca345b45 | |||
ee80712c77 | |||
3cf7153f44 | |||
8e2fc75e48 | |||
4aa51c8583 | |||
3c6ebd7742 | |||
b85539dc17 | |||
4cae3f02ed | |||
469b436f10 | |||
66e3e6729b | |||
fc6a794714 | |||
d75ed3d5ac | |||
bd0a218214 | |||
f96129fa37 | |||
920418618a | |||
29247481a6 | |||
773710aeef | |||
d0c8ee57e6 | |||
cf2adc7f6a | |||
b621f24540 | |||
99be638525 | |||
d35c204e07 | |||
d8b4267668 | |||
3c2f3be451 | |||
e846cf8672 | |||
8804bce6c0 | |||
83acda6d05 | |||
063719532f | |||
7b1b061355 | |||
ed4540887e | |||
6968476ed0 | |||
5d2c7c3058 | |||
e6cb90e545 | |||
ec73f4dbea | |||
4f41a52ee7 | |||
c097956fcb | |||
8cbc17ed98 | |||
50d89d0f13 | |||
7b4d13b73e | |||
2909aa1656 | |||
64131b6764 | |||
0b78d75a21 | |||
b35c342960 | |||
24c858d379 | |||
b8a31a8be9 | |||
02c30d6454 | |||
985f003e6d | |||
98045e1e2e | |||
5762ce58a4 | |||
2787ee71fc | |||
e61a6df4d7 | |||
0a5c2ecbf5 | |||
6bfab696ad | |||
928f2dd496 | |||
bcf75892f7 | |||
594761385f | |||
d05fba06ec | |||
25155b1b38 | |||
ded6c9c199 | |||
c62e6c0045 | |||
b3feda9fd1 | |||
7ef8e2c8b8 | |||
d5ff211871 | |||
51c23e3842 | |||
557b30815e | |||
145459bfc3 | |||
f97a6a2bee | |||
1134422891 | |||
6012275c7b | |||
2f07063375 | |||
310d1ed485 | |||
a48edbb16e | |||
d6258409fc | |||
1b94b1247b | |||
9ef63ae60e | |||
f99de4be48 | |||
80fd1820c2 | |||
0a4a983d20 | |||
c2cc830691 | |||
4d0490d1c6 | |||
02d1838547 | |||
5c6edff778 | |||
7cbca32ddd |
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,6 +10,8 @@ msbuild.binlog
|
|||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
*.idea
|
*.idea
|
||||||
|
_ReSharper.Caches
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
Oqtane.Server/appsettings.json
|
Oqtane.Server/appsettings.json
|
||||||
Oqtane.Server/Data
|
Oqtane.Server/Data
|
||||||
|
@ -16,7 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
public static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<SiteState>();
|
services.AddScoped<SiteState>();
|
||||||
services.AddScoped<IInstallationService, InstallationService>();
|
services.AddScoped<IInstallationService, InstallationService>();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
|
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) " ResourceKey="Server">Server:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="server" type="text" class="form-control" @bind="@_server" />
|
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
@if (_encryption == "true")
|
@if (_encryption == "true")
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption" ResourceKey="TrustServerCertificate">Certificate:</Label>
|
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True." ResourceKey="TrustServerCertificate">Certificate:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
||||||
<option value="true">@Localizer["Self Signed"]</option>
|
<option value="true">@Localizer["Self Signed"]</option>
|
||||||
|
@ -26,22 +26,42 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@if (_databases != null)
|
@if (_databases != null)
|
||||||
{
|
{
|
||||||
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
|
<div class="input-group">
|
||||||
@foreach (var database in _databases)
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
{
|
@foreach (var database in _databases)
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
{
|
||||||
}
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
</select>
|
}
|
||||||
|
</select>
|
||||||
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@{
|
@if (!_showConnectionString)
|
||||||
if (_databaseConfigType != null)
|
{
|
||||||
{
|
if (_databaseConfigType != null)
|
||||||
@DatabaseConfigComponent;
|
{
|
||||||
}
|
@DatabaseConfigComponent
|
||||||
}
|
}
|
||||||
</div>
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<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>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
<h2>@Localizer["ApplicationAdmin"]</h2><br />
|
||||||
@ -100,13 +120,15 @@
|
|||||||
private Type _databaseConfigType;
|
private Type _databaseConfigType;
|
||||||
private object _databaseConfig;
|
private object _databaseConfig;
|
||||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||||
|
private bool _showConnectionString = false;
|
||||||
|
private string _connectionString = string.Empty;
|
||||||
|
|
||||||
private string _hostUsername = string.Empty;
|
private string _hostUsername = string.Empty;
|
||||||
private string _hostPassword = string.Empty;
|
private string _hostPassword = string.Empty;
|
||||||
private string _passwordType = "password";
|
private string _passwordType = "password";
|
||||||
private string _confirmPasswordType = "password";
|
private string _confirmPasswordType = "password";
|
||||||
private string _togglePassword = string.Empty;
|
private string _togglePassword = string.Empty;
|
||||||
private string _toggleConfirmPassword = string.Empty;
|
private string _toggleConfirmPassword = string.Empty;
|
||||||
private string _confirmPassword = string.Empty;
|
private string _confirmPassword = string.Empty;
|
||||||
private string _hostEmail = string.Empty;
|
private string _hostEmail = string.Empty;
|
||||||
private bool _register = true;
|
private bool _register = true;
|
||||||
@ -116,7 +138,7 @@
|
|||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
_databases = await DatabaseService.GetDatabasesAsync();
|
_databases = await DatabaseService.GetDatabasesAsync();
|
||||||
if (_databases.Exists(item => item.IsDefault))
|
if (_databases.Exists(item => item.IsDefault))
|
||||||
@ -135,7 +157,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -164,17 +186,24 @@
|
|||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous", "");
|
await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/css/bootstrap.min.css", "text/css", "sha512-XWTTruHZEYJsxV3W/lSXG1n3Q39YIWOstqvmFsdNEEQfHoZ6vm6E9GK2OrF6DSJSpIbRbi+Nn0WDPID9O7xB2Q==", "anonymous", "");
|
||||||
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head");
|
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", "anonymous", "", "head");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Install()
|
private async Task Install()
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
var connectionString = String.Empty;
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
if (_showConnectionString)
|
||||||
{
|
{
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
connectionString = _connectionString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
|
||||||
@ -218,12 +247,12 @@
|
|||||||
{
|
{
|
||||||
_message = Localizer["Message.Password.Invalid"];
|
_message = Localizer["Message.Password.Invalid"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Require.DbInfo"];
|
_message = Localizer["Message.Require.DbInfo"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TogglePassword()
|
private void TogglePassword()
|
||||||
{
|
{
|
||||||
@ -239,7 +268,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleConfirmPassword()
|
private void ToggleConfirmPassword()
|
||||||
{
|
{
|
||||||
if (_confirmPasswordType == "password")
|
if (_confirmPasswordType == "password")
|
||||||
{
|
{
|
||||||
@ -252,4 +281,14 @@
|
|||||||
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleConnectionString()
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
_connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
_showConnectionString = !_showConnectionString;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||||
{
|
{
|
||||||
string url = NavigateUrl(p.Path);
|
string url = NavigateUrl(p.Path);
|
||||||
<div class="col-md-2 mx-auto text-center">
|
<div class="col-md-2 mx-auto text-center mb-3">
|
||||||
<NavLink class="nav-link" href="@url" Match="NavLinkMatch.All">
|
<NavLink class="nav-link text-primary" href="@url" Match="NavLinkMatch.All">
|
||||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@SharedLocalizer[p.Name]
|
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@SharedLocalizer[p.Name]
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
string message = string.Format(Localizer["Error.Module.Load"], module.ModuleDefinitionName);
|
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], module.ModuleDefinitionName), MessageType.Error);
|
||||||
AddModuleMessage(message, MessageType.Error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await logger.LogCritical("Error Loading Module {Module}", module);
|
await logger.LogCritical("Error Loading Module {Module}", module);
|
||||||
|
@ -132,22 +132,10 @@
|
|||||||
_isEnabled = job.IsEnabled.ToString();
|
_isEnabled = job.IsEnabled.ToString();
|
||||||
_interval = job.Interval.ToString();
|
_interval = job.Interval.ToString();
|
||||||
_frequency = job.Frequency;
|
_frequency = job.Frequency;
|
||||||
_startDate = job.StartDate;
|
(_startDate, _startTime) = Utilities.UtcAsLocalDateAndTime(job.StartDate);
|
||||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
(_endDate, _endTime) = Utilities.UtcAsLocalDateAndTime(job.EndDate);
|
||||||
{
|
|
||||||
_startTime = job.StartDate.Value.ToString("HH:mm");
|
|
||||||
}
|
|
||||||
_endDate = job.EndDate;
|
|
||||||
if (job.EndDate != null && job.EndDate.Value.TimeOfDay.TotalSeconds != 0)
|
|
||||||
{
|
|
||||||
_endTime = job.EndDate.Value.ToString("HH:mm");
|
|
||||||
}
|
|
||||||
_retentionHistory = job.RetentionHistory.ToString();
|
_retentionHistory = job.RetentionHistory.ToString();
|
||||||
_nextDate = job.NextExecution;
|
(_nextDate, _nextTime) = Utilities.UtcAsLocalDateAndTime(job.NextExecution);
|
||||||
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
|
|
||||||
{
|
|
||||||
_nextTime = job.NextExecution.Value.ToString("HH:mm");
|
|
||||||
}
|
|
||||||
createdby = job.CreatedBy;
|
createdby = job.CreatedBy;
|
||||||
createdon = job.CreatedOn;
|
createdon = job.CreatedOn;
|
||||||
modifiedby = job.ModifiedBy;
|
modifiedby = job.ModifiedBy;
|
||||||
@ -180,50 +168,27 @@
|
|||||||
{
|
{
|
||||||
job.Interval = int.Parse(_interval);
|
job.Interval = int.Parse(_interval);
|
||||||
}
|
}
|
||||||
job.StartDate = _startDate;
|
job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime);
|
||||||
if (job.StartDate != null)
|
job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime);
|
||||||
{
|
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||||
job.StartDate = job.StartDate.Value.Date;
|
job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _nextTime);
|
||||||
if (!string.IsNullOrEmpty(_startTime))
|
|
||||||
{
|
try
|
||||||
job.StartDate = DateTime.Parse(job.StartDate.Value.ToShortDateString() + " " + _startTime);
|
{
|
||||||
}
|
job = await JobService.UpdateJobAsync(job);
|
||||||
}
|
await logger.LogInformation("Job Updated {Job}", job);
|
||||||
job.EndDate = _endDate;
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
if (job.EndDate != null)
|
}
|
||||||
{
|
catch (Exception ex)
|
||||||
job.EndDate = job.EndDate.Value.Date;
|
{
|
||||||
if (!string.IsNullOrEmpty(_endTime))
|
await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message);
|
||||||
{
|
AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error);
|
||||||
job.EndDate = DateTime.Parse(job.EndDate.Value.ToShortDateString() + " " + _endTime);
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
{
|
||||||
job.NextExecution = _nextDate;
|
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
||||||
if (job.NextExecution != null)
|
}
|
||||||
{
|
}
|
||||||
job.NextExecution = job.NextExecution.Value.Date;
|
|
||||||
if (!string.IsNullOrEmpty(_nextTime))
|
|
||||||
{
|
|
||||||
job.NextExecution = DateTime.Parse(job.NextExecution.Value.ToShortDateString() + " " + _nextTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
job = await JobService.UpdateJobAsync(job);
|
|
||||||
await logger.LogInformation("Job Updated {Job}", job);
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ else
|
|||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||||
<td>@context.NextExecution</td>
|
<td>@context.NextExecution?.ToLocalTime()</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.IsStarted)
|
@if (context.IsStarted)
|
||||||
{
|
{
|
||||||
@ -56,6 +56,10 @@ else
|
|||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
_jobs = await JobService.GetJobsAsync();
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
if (_jobs.Count == 0)
|
||||||
|
{
|
||||||
|
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
private string DisplayStatus(bool isEnabled, bool isExecuting)
|
||||||
|
@ -32,7 +32,7 @@ else
|
|||||||
<select id="_code" class="form-select" @bind="@_code" required>
|
<select id="_code" class="form-select" @bind="@_code" required>
|
||||||
@foreach (var culture in _availableCultures)
|
@foreach (var culture in _availableCultures)
|
||||||
{
|
{
|
||||||
<option value="@culture.Name">@culture.DisplayName</option>
|
<option value="@culture.Name">@(culture.DisplayName + " (" + culture.Name + ")")</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -52,7 +52,7 @@ else
|
|||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Download" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
<TabPanel Name="Translations" Heading="Translations" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
||||||
<div class="row justify-content-center mb-3">
|
<div class="row justify-content-center mb-3">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -110,12 +110,16 @@ else
|
|||||||
}
|
}
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
||||||
<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" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Language: </Label>
|
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Translation: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
|
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
|
||||||
</div>
|
</div>
|
||||||
@ -161,33 +165,32 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
private string _code = string.Empty;
|
private string _code = string.Empty;
|
||||||
private string _isDefault = "False";
|
private string _isDefault = "False";
|
||||||
private string _message;
|
private string _message;
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private IEnumerable<Culture> _supportedCultures;
|
||||||
private IEnumerable<Culture> _availableCultures;
|
private IEnumerable<Culture> _availableCultures;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private string _price = "free";
|
private string _price = "free";
|
||||||
private string _search = "";
|
private string _search = "";
|
||||||
private string _productname = "";
|
private string _productname = "";
|
||||||
private string _license = "";
|
private string _license = "";
|
||||||
private string _packageid = "";
|
private string _packageid = "";
|
||||||
private string _version = "";
|
private string _version = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||||
var languagesCodes = languages.Select(l => l.Code).ToList();
|
var languagesCodes = languages.Select(l => l.Code).ToList();
|
||||||
|
|
||||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
||||||
_availableCultures = _supportedCultures
|
_availableCultures = _supportedCultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
||||||
.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
await LoadTranslations();
|
||||||
await LoadTranslations();
|
|
||||||
|
|
||||||
if (_supportedCultures.Count() == 1)
|
if (_supportedCultures.Count() == 1)
|
||||||
{
|
{
|
||||||
|
@ -19,16 +19,18 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th>@Localizer["Translation"]</th>
|
||||||
<th>@Localizer["Default"]</th>
|
<th>@Localizer["Default"]</th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Code</td>
|
<td>@context.Code</td>
|
||||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||||
@if (UpgradeAvailable(context.Code))
|
<td>
|
||||||
|
@if (UpgradeAvailable(context.Code, context.Version))
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
}
|
}
|
||||||
@ -38,24 +40,21 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Language> _languages;
|
private List<Language> _languages;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.PackageId);
|
||||||
|
|
||||||
var cultures = await LocalizationService.GetCulturesAsync();
|
var cultures = await LocalizationService.GetCulturesAsync();
|
||||||
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
||||||
|
|
||||||
// Adds English as default language
|
|
||||||
_languages.Insert(0, new Language { Name = culture.DisplayName, Code = culture.Name, IsDefault = !_languages.Any(l => l.IsDefault) });
|
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
_packages = await PackageService.GetPackagesAsync("translation");
|
_packages = await PackageService.GetPackagesAsync("translation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,15 +75,16 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string code)
|
private bool UpgradeAvailable(string code, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
var upgradeavailable = false;
|
||||||
if (_packages != null)
|
if (_packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + ".Client." + code)).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0);
|
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0) &&
|
||||||
|
(Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ else
|
|||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, Constants.PackagesFolder);
|
await PackageService.DownloadPackageAsync(Constants.PackageId + "." + code, Constants.Version, Constants.PackagesFolder);
|
||||||
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
|
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
|
||||||
await PackageService.InstallPackagesAsync();
|
await PackageService.InstallPackagesAsync();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
@inject IServiceProvider ServiceProvider
|
@inject IServiceProvider ServiceProvider
|
||||||
@inject SiteState SiteState
|
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -184,11 +183,12 @@
|
|||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(login))
|
if (await interop.FormValid(login))
|
||||||
{
|
{
|
||||||
|
var hybrid = (PageState.Runtime == Shared.Runtime.Hybrid);
|
||||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||||
|
|
||||||
if (!twofactor)
|
if (!twofactor)
|
||||||
{
|
{
|
||||||
user = await UserService.LoginUserAsync(user);
|
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -199,10 +199,21 @@
|
|||||||
{
|
{
|
||||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||||
|
|
||||||
// post back to the Login page so that the cookies are set correctly
|
if (hybrid)
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
{
|
||||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
// hybrid apps utilize an interactive login
|
||||||
await interop.SubmitForm(url, fields);
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider
|
||||||
|
.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||||
|
authstateprovider.NotifyAuthenticationChanged();
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// post back to the Login page so that the cookies are set correctly
|
||||||
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
|
||||||
|
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
|
||||||
|
await interop.SubmitForm(url, fields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -130,13 +130,14 @@
|
|||||||
private string _properties = string.Empty;
|
private string _properties = string.Empty;
|
||||||
private string _server = string.Empty;
|
private string _server = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override string UrlParametersTemplate => "/{id}";
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
_logId = Int32.Parse(UrlParameters["id"]);
|
||||||
var log = await LogService.GetLogAsync(_logId);
|
var log = await LogService.GetLogAsync(_logId);
|
||||||
if (log != null)
|
if (log != null)
|
||||||
{
|
{
|
||||||
@ -191,13 +192,6 @@
|
|||||||
|
|
||||||
private string CloseUrl()
|
private string CloseUrl()
|
||||||
{
|
{
|
||||||
if (!PageState.QueryString.ContainsKey("level"))
|
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
{
|
|
||||||
return NavigateUrl();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ else
|
|||||||
<th>@Localizer["Function"]</th>
|
<th>@Localizer["Function"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString() + "&level=" + _level + "&function=" + _function + "&rows=" + _rows + "&page=" + _page.ToString())" ResourceKey="LogDetails" /></td>
|
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
||||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||||
@ -99,29 +99,32 @@ else
|
|||||||
private List<Log> _logs;
|
private List<Log> _logs;
|
||||||
private string _retention = "";
|
private string _retention = "";
|
||||||
|
|
||||||
|
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// external link to log item will display Details component
|
||||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}"));
|
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}"));
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("level"))
|
|
||||||
|
if (UrlParameters.ContainsKey("level"))
|
||||||
{
|
{
|
||||||
_level = PageState.QueryString["level"];
|
_level = UrlParameters["level"];
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("function"))
|
if (UrlParameters.ContainsKey("function"))
|
||||||
{
|
{
|
||||||
_function = PageState.QueryString["function"];
|
_function = UrlParameters["function"];
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("rows"))
|
if (UrlParameters.ContainsKey("rows"))
|
||||||
{
|
{
|
||||||
_rows = PageState.QueryString["rows"];
|
_rows = UrlParameters["rows"];
|
||||||
}
|
}
|
||||||
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
|
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
|
||||||
{
|
{
|
||||||
_page = page;
|
_page = page;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ else
|
|||||||
private List<Template> _templates;
|
private List<Template> _templates;
|
||||||
private string _template = "-";
|
private string _template = "-";
|
||||||
private string[] _versions;
|
private string[] _versions;
|
||||||
private string _reference = Constants.Version;
|
private string _reference = "local";
|
||||||
private string _minversion = "2.0.0";
|
private string _minversion = "2.0.0";
|
||||||
private string _location = string.Empty;
|
private string _location = string.Empty;
|
||||||
|
|
||||||
|
@ -91,9 +91,9 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p style="height: 200px; overflow-y: scroll;">
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
<h3>@_productname</h3>
|
<h3>@_productname</h3>
|
||||||
@if (!string.IsNullOrEmpty(_license))
|
@if (!string.IsNullOrEmpty(_packagelicense))
|
||||||
{
|
{
|
||||||
@((MarkupString)_license)
|
@((MarkupString)_packagelicense)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -114,14 +114,18 @@
|
|||||||
<button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private string _price = "free";
|
private string _price = "free";
|
||||||
private string _search = "";
|
private string _search = "";
|
||||||
private string _productname = "";
|
private string _productname = "";
|
||||||
private string _license = "";
|
|
||||||
private string _packageid = "";
|
private string _packageid = "";
|
||||||
private string _version = "";
|
private string _packagelicense = "";
|
||||||
|
private string _packageversion = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
@ -198,7 +202,7 @@
|
|||||||
private void HideModal()
|
private void HideModal()
|
||||||
{
|
{
|
||||||
_productname = "";
|
_productname = "";
|
||||||
_license = "";
|
_packagelicense = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,12 +214,12 @@
|
|||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
_productname = package.Name;
|
_productname = package.Name;
|
||||||
|
_packageid = package.PackageId;
|
||||||
if (!string.IsNullOrEmpty(package.License))
|
if (!string.IsNullOrEmpty(package.License))
|
||||||
{
|
{
|
||||||
_license = package.License.Replace("\n", "<br />");
|
_packagelicense = package.License.Replace("\n", "<br />");
|
||||||
}
|
}
|
||||||
_packageid = package.PackageId;
|
_packageversion = package.Version;
|
||||||
_version = package.Version;
|
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -230,16 +234,16 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
|
await PackageService.DownloadPackageAsync(_packageid, _packageversion, Constants.PackagesFolder);
|
||||||
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
|
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _packageversion);
|
||||||
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
|
||||||
_productname = "";
|
_productname = "";
|
||||||
_license = "";
|
_packagelicense = "";
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version);
|
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _packageversion);
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +257,7 @@
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Installing Module");
|
await logger.LogError(ex, "Error Installing Modules");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@
|
|||||||
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
var template = _templates.FirstOrDefault(item => item.Name == _template);
|
||||||
_minversion = template.Version;
|
_minversion = template.Version;
|
||||||
}
|
}
|
||||||
GetLocation();
|
GetLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetLocation()
|
private void GetLocation()
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
|
@using System.Globalization
|
||||||
|
@using Microsoft.AspNetCore.Localization
|
||||||
@inject IModuleDefinitionService ModuleDefinitionService
|
@inject IModuleDefinitionService ModuleDefinitionService
|
||||||
|
@inject IPackageService PackageService
|
||||||
|
@inject ILanguageService LanguageService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@ -43,7 +47,13 @@
|
|||||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||||
</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="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>
|
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||||
@ -74,7 +84,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</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>
|
||||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -82,107 +98,278 @@
|
|||||||
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
|
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
<TabPanel Name="Translations" ResourceKey="Translations">
|
||||||
|
@if (_languages != null)
|
||||||
|
{
|
||||||
|
@if (_languages.Count > 0)
|
||||||
|
{
|
||||||
|
<Pager Items="@_languages">
|
||||||
|
<Header>
|
||||||
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th>@Localizer["Version"]</th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
<td>@context.Code</td>
|
||||||
|
<td>@context.Version</td>
|
||||||
|
<td>
|
||||||
|
@if (context.IsDefault)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (UpgradeAvailable(_packagename + "." + context.Code, context.Version))
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<br />
|
||||||
|
<div class="mx-auto text-center">
|
||||||
|
@Localizer["Search.NoResults"]
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
@if (_productname != "")
|
||||||
<br />
|
{
|
||||||
<br />
|
<div class="app-actiondialog">
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
|
<h3>@_productname</h3>
|
||||||
|
@if (!string.IsNullOrEmpty(_packagelicense))
|
||||||
|
{
|
||||||
|
@((MarkupString)_packagelicense)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@SharedLocalizer["License Not Specified"]
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadPackage(_packageid))>@SharedLocalizer["Accept"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
private int _moduleDefinitionId;
|
private int _moduleDefinitionId;
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _version;
|
private string _description = "";
|
||||||
private string _categories;
|
private string _categories;
|
||||||
private string _moduledefinitionname = "";
|
private string _moduledefinitionname = "";
|
||||||
private string _description = "";
|
private string _version;
|
||||||
private string _owner = "";
|
private string _packagename = "";
|
||||||
private string _url = "";
|
private string _owner = "";
|
||||||
private string _contact = "";
|
private string _url = "";
|
||||||
private string _license = "";
|
private string _contact = "";
|
||||||
private string _runtimes = "";
|
private string _license = "";
|
||||||
private string _permissions;
|
private string _runtimes = "";
|
||||||
private string _createdby;
|
private string _permissions;
|
||||||
private DateTime _createdon;
|
private string _createdby;
|
||||||
private string _modifiedby;
|
private DateTime _createdon;
|
||||||
private DateTime _modifiedon;
|
private string _modifiedby;
|
||||||
|
private DateTime _modifiedon;
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
private List<Package> _packages;
|
||||||
|
private List<Language> _languages;
|
||||||
|
private string _productname = "";
|
||||||
|
private string _packagelicense = "";
|
||||||
|
private string _packageid = "";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]);
|
|
||||||
var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
|
||||||
if (moduleDefinition != null)
|
|
||||||
{
|
|
||||||
_name = moduleDefinition.Name;
|
|
||||||
_version = moduleDefinition.Version;
|
|
||||||
_categories = moduleDefinition.Categories;
|
|
||||||
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
|
||||||
_description = moduleDefinition.Description;
|
|
||||||
_owner = moduleDefinition.Owner;
|
|
||||||
_url = moduleDefinition.Url;
|
|
||||||
_contact = moduleDefinition.Contact;
|
|
||||||
_license = moduleDefinition.License;
|
|
||||||
_runtimes = moduleDefinition.Runtimes;
|
|
||||||
_permissions = moduleDefinition.Permissions;
|
|
||||||
_createdby = moduleDefinition.CreatedBy;
|
|
||||||
_createdon = moduleDefinition.CreatedOn;
|
|
||||||
_modifiedby = moduleDefinition.ModifiedBy;
|
|
||||||
_modifiedon = moduleDefinition.ModifiedOn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Module.Load"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveModuleDefinition()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
validated = true;
|
try
|
||||||
var interop = new Interop(JSRuntime);
|
{
|
||||||
if (await interop.FormValid(form))
|
_moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
{
|
var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
||||||
try
|
if (moduleDefinition != null)
|
||||||
{
|
{
|
||||||
var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
_name = moduleDefinition.Name;
|
||||||
if (moduledefinition.Name != _name)
|
_description = moduleDefinition.Description;
|
||||||
{
|
_categories = moduleDefinition.Categories;
|
||||||
moduledefinition.Name = _name;
|
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
||||||
}
|
_version = moduleDefinition.Version;
|
||||||
if (moduledefinition.Description != _description)
|
_packagename = moduleDefinition.PackageName;
|
||||||
{
|
_owner = moduleDefinition.Owner;
|
||||||
moduledefinition.Description = _description;
|
_url = moduleDefinition.Url;
|
||||||
}
|
_contact = moduleDefinition.Contact;
|
||||||
if (moduledefinition.Categories != _categories)
|
_license = moduleDefinition.License;
|
||||||
{
|
_runtimes = moduleDefinition.Runtimes;
|
||||||
moduledefinition.Categories = _categories;
|
_permissions = moduleDefinition.Permissions;
|
||||||
}
|
_createdby = moduleDefinition.CreatedBy;
|
||||||
moduledefinition.Permissions = _permissionGrid.GetPermissions();
|
_createdon = moduleDefinition.CreatedOn;
|
||||||
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
|
_modifiedby = moduleDefinition.ModifiedBy;
|
||||||
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
|
_modifiedon = moduleDefinition.ModifiedOn;
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
_packages = await PackageService.GetPackagesAsync("translation", "", "", _packagename);
|
||||||
catch (Exception ex)
|
_languages = await LanguageService.GetLanguagesAsync(-1, _packagename);
|
||||||
{
|
foreach (var package in _packages)
|
||||||
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
{
|
||||||
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
|
var code = package.PackageId.Split('.').Last();
|
||||||
}
|
if (!_languages.Any(item => item.Code == code))
|
||||||
}
|
{
|
||||||
else
|
_languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = package.Version, IsDefault = true });
|
||||||
{
|
}
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
}
|
||||||
}
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveModuleDefinition()
|
||||||
|
{
|
||||||
|
validated = true;
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var moduledefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
||||||
|
if (moduledefinition.Name != _name)
|
||||||
|
{
|
||||||
|
moduledefinition.Name = _name;
|
||||||
|
}
|
||||||
|
if (moduledefinition.Description != _description)
|
||||||
|
{
|
||||||
|
moduledefinition.Description = _description;
|
||||||
|
}
|
||||||
|
if (moduledefinition.Categories != _categories)
|
||||||
|
{
|
||||||
|
moduledefinition.Categories = _categories;
|
||||||
|
}
|
||||||
|
moduledefinition.Permissions = _permissionGrid.GetPermissions();
|
||||||
|
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
|
||||||
|
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideModal()
|
||||||
|
{
|
||||||
|
_productname = "";
|
||||||
|
_packagelicense = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UpgradeAvailable(string packagename, string version)
|
||||||
|
{
|
||||||
|
var upgradeavailable = false;
|
||||||
|
if (_packages != null)
|
||||||
|
{
|
||||||
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
|
if (package != null)
|
||||||
|
{
|
||||||
|
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return upgradeavailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetPackage(string packagename)
|
||||||
|
{
|
||||||
|
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var package = await PackageService.GetPackageAsync(packagename, version);
|
||||||
|
if (package != null)
|
||||||
|
{
|
||||||
|
_productname = package.Name;
|
||||||
|
if (!string.IsNullOrEmpty(package.License))
|
||||||
|
{
|
||||||
|
_packagelicense = package.License.Replace("\n", "<br />");
|
||||||
|
}
|
||||||
|
_packageid = package.PackageId;
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packagename, version);
|
||||||
|
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadPackage(string packagename)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||||
|
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
||||||
|
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", packagename, version);
|
||||||
|
AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success);
|
||||||
|
_productname = "";
|
||||||
|
_packagelicense = "";
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
||||||
|
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InstallTranslations()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PackageService.InstallPackagesAsync();
|
||||||
|
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Installing Translations");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _content = string.Empty;
|
private string _content = string.Empty;
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Export Content";
|
public override string Title => "Export Content";
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +27,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId);
|
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId);
|
||||||
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
public override string Title => "Import Content";
|
public override string Title => "Import Content";
|
||||||
|
|
||||||
private async Task ImportModule()
|
private async Task ImportModule()
|
||||||
@ -38,7 +38,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
|
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId, _content);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success);
|
||||||
|
@ -124,37 +124,45 @@
|
|||||||
_containerType = ModuleState.ContainerType;
|
_containerType = ModuleState.ContainerType;
|
||||||
_allPages = ModuleState.AllPages.ToString();
|
_allPages = ModuleState.AllPages.ToString();
|
||||||
_permissions = ModuleState.Permissions;
|
_permissions = ModuleState.Permissions;
|
||||||
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
|
|
||||||
_pageId = ModuleState.PageId.ToString();
|
_pageId = ModuleState.PageId.ToString();
|
||||||
createdby = ModuleState.CreatedBy;
|
createdby = ModuleState.CreatedBy;
|
||||||
createdon = ModuleState.CreatedOn;
|
createdon = ModuleState.CreatedOn;
|
||||||
modifiedby = ModuleState.ModifiedBy;
|
modifiedby = ModuleState.ModifiedBy;
|
||||||
modifiedon = ModuleState.ModifiedOn;
|
modifiedon = ModuleState.ModifiedOn;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
if (ModuleState.ModuleDefinition != null)
|
||||||
{
|
{
|
||||||
// module settings type explicitly declared in IModule interface
|
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
|
||||||
|
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||||
|
{
|
||||||
|
// module settings type explicitly declared in IModule interface
|
||||||
|
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
||||||
|
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||||
|
}
|
||||||
|
if (_moduleSettingsType != null)
|
||||||
|
{
|
||||||
|
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
||||||
|
if (!string.IsNullOrEmpty(moduleobject.Title))
|
||||||
|
{
|
||||||
|
_moduleSettingsTitle = moduleobject.Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleSettingsComponent = builder =>
|
||||||
|
{
|
||||||
|
builder.OpenComponent(0, _moduleSettingsType);
|
||||||
|
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||||
|
builder.CloseComponent();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
|
||||||
}
|
|
||||||
if (_moduleSettingsType != null)
|
|
||||||
{
|
|
||||||
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
|
||||||
if (!string.IsNullOrEmpty(moduleobject.Title))
|
|
||||||
{
|
|
||||||
_moduleSettingsTitle = moduleobject.Title;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleSettingsComponent = builder =>
|
|
||||||
{
|
|
||||||
builder.OpenComponent(0, _moduleSettingsType);
|
|
||||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
|
||||||
builder.CloseComponent();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
|
<select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
|
||||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||||
@foreach (Page page in _pageList)
|
@foreach (Page page in PageState.Pages)
|
||||||
{
|
{
|
||||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||||
}
|
}
|
||||||
@ -167,7 +167,6 @@
|
|||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private List<Page> _pageList;
|
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _title;
|
private string _title;
|
||||||
private string _meta;
|
private string _meta;
|
||||||
@ -201,7 +200,6 @@
|
|||||||
_themetype = PageState.Site.DefaultThemeType;
|
_themetype = PageState.Site.DefaultThemeType;
|
||||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||||
_containertype = PageState.Site.DefaultContainerType;
|
_containertype = PageState.Site.DefaultContainerType;
|
||||||
_pageList = PageState.Pages;
|
|
||||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||||
_permissions = string.Empty;
|
_permissions = string.Empty;
|
||||||
ThemeSettings();
|
ThemeSettings();
|
||||||
@ -307,6 +305,10 @@
|
|||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
|
{
|
||||||
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
|
}
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,15 +331,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(PagePathIsDeleted(page.Path, page.SiteId, _pageList))
|
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
if (_pages.Any(item => item.Path == page.Path))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Deleted"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,14 +424,4 @@
|
|||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||||
@foreach (Page page in _pageList)
|
@foreach (Page page in PageState.Pages)
|
||||||
{
|
{
|
||||||
if (page.PageId != _pageId)
|
if (page.PageId != _pageId)
|
||||||
{
|
{
|
||||||
@ -201,7 +201,6 @@
|
|||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||||
private List<Page> _pageList;
|
|
||||||
private List<Module> _pageModules;
|
private List<Module> _pageModules;
|
||||||
private int _pageId;
|
private int _pageId;
|
||||||
private string _name;
|
private string _name;
|
||||||
@ -238,7 +237,6 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pageList = PageState.Pages;
|
|
||||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||||
_themeList = await ThemeService.GetThemesAsync();
|
_themeList = await ThemeService.GetThemesAsync();
|
||||||
_themes = ThemeService.GetThemeControls(_themeList);
|
_themes = ThemeService.GetThemeControls(_themeList);
|
||||||
@ -435,6 +433,10 @@
|
|||||||
}
|
}
|
||||||
if (_path.Contains("/"))
|
if (_path.Contains("/"))
|
||||||
{
|
{
|
||||||
|
if (_path.EndsWith("/") && _path != "/")
|
||||||
|
{
|
||||||
|
_path = _path.Substring(0, _path.Length - 1);
|
||||||
|
}
|
||||||
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
_path = _path.Substring(_path.LastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,12 +459,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
|
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
|
if (_pages.Any(item => item.Path == page.Path && item.PageId != page.PageId))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
|
{
|
||||||
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_insert != "=")
|
if (_insert != "=")
|
||||||
{
|
{
|
||||||
Page child;
|
Page child;
|
||||||
@ -567,9 +576,4 @@
|
|||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
|
|
||||||
{
|
|
||||||
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
<Row>
|
<Row>
|
||||||
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
||||||
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
|
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||||
<td>@context.Title</td>
|
<td>@context.Title</td>
|
||||||
<td>@context.DeletedBy</td>
|
<td>@context.DeletedBy</td>
|
||||||
<td>@context.DeletedOn</td>
|
<td>@context.DeletedOn</td>
|
||||||
@ -140,6 +140,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ModuleInstance.ShowProgressIndicator();
|
||||||
foreach (Page page in _pages)
|
foreach (Page page in _pages)
|
||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
@ -148,6 +149,7 @@
|
|||||||
|
|
||||||
await logger.LogInformation("Pages Permanently Deleted");
|
await logger.LogInformation("Pages Permanently Deleted");
|
||||||
await Load();
|
await Load();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
@ -155,6 +157,7 @@
|
|||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +207,7 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ModuleInstance.ShowProgressIndicator();
|
||||||
foreach (Module module in _modules)
|
foreach (Module module in _modules)
|
||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
@ -218,12 +222,14 @@
|
|||||||
|
|
||||||
await logger.LogInformation("Modules Permanently Deleted");
|
await logger.LogInformation("Modules Permanently Deleted");
|
||||||
await Load();
|
await Load();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,21 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="homepage" HelpText="Select the home page for the site (to be used if there is no page with a path of '/')" ResourceKey="HomePage">Home Page: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="homepage" class="form-select" @bind="@_homepageid" required>
|
||||||
|
<option value="-"><@Localizer["Not Specified"]></option>
|
||||||
|
@foreach (Page page in PageState.Pages)
|
||||||
|
{
|
||||||
|
if (UserSecurity.ContainsRole(page.Permissions, PermissionNames.View, RoleNames.Everyone))
|
||||||
|
{
|
||||||
|
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</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="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
||||||
@ -228,6 +243,7 @@
|
|||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||||
|
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -295,6 +311,7 @@
|
|||||||
private string _themetype = "-";
|
private string _themetype = "-";
|
||||||
private string _containertype = "-";
|
private string _containertype = "-";
|
||||||
private string _admincontainertype = "-";
|
private string _admincontainertype = "-";
|
||||||
|
private string _homepageid = "-";
|
||||||
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";
|
||||||
@ -353,6 +370,11 @@
|
|||||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||||
|
|
||||||
|
if (site.HomePageId != null)
|
||||||
|
{
|
||||||
|
_homepageid = site.HomePageId.Value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
_pwaisenabled = site.PwaIsEnabled.ToString();
|
_pwaisenabled = site.PwaIsEnabled.ToString();
|
||||||
if (site.PwaAppIconFileId != null)
|
if (site.PwaAppIconFileId != null)
|
||||||
{
|
{
|
||||||
@ -479,6 +501,7 @@
|
|||||||
refresh = true; // needs to be refreshed on client
|
refresh = true; // needs to be refreshed on client
|
||||||
}
|
}
|
||||||
site.AdminContainerType = _admincontainertype;
|
site.AdminContainerType = _admincontainertype;
|
||||||
|
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
|
||||||
|
|
||||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||||
{
|
{
|
||||||
@ -557,8 +580,8 @@
|
|||||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||||
}
|
}
|
||||||
|
|
||||||
aliases = await AliasService.GetAliasesAsync();
|
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
|
||||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + aliases.First().Name, true);
|
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,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="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they can be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
<Label Class="col-sm-3" For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder)." ResourceKey="Aliases">Aliases: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -89,7 +89,8 @@ else
|
|||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||||
</select>
|
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -128,18 +129,43 @@ else
|
|||||||
<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 for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
@if (_databases != null)
|
||||||
@foreach (var database in _databases)
|
{
|
||||||
{
|
<div class="input-group">
|
||||||
<option value="@database.Name">@Localizer[@database.Name]</option>
|
<select id="databaseType" class="form-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))" required>
|
||||||
}
|
@foreach (var database in _databases)
|
||||||
</select>
|
{
|
||||||
</div>
|
<option value="@database.Name">@Localizer[@database.Name]</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
@if (!_showConnectionString)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionString"]</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="ToggleConnectionString">@Localizer["EnterConnectionParameters"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
if (_databaseConfigType != null)
|
@if (!_showConnectionString)
|
||||||
{
|
{
|
||||||
@DatabaseConfigComponent;
|
if (_databaseConfigType != null)
|
||||||
}
|
{
|
||||||
|
@DatabaseConfigComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<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>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -169,6 +195,8 @@ else
|
|||||||
private Type _databaseConfigType;
|
private Type _databaseConfigType;
|
||||||
private object _databaseConfig;
|
private object _databaseConfig;
|
||||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||||
|
private bool _showConnectionString = false;
|
||||||
|
private string _connectionString = string.Empty;
|
||||||
|
|
||||||
private List<Theme> _themeList;
|
private List<Theme> _themeList;
|
||||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||||
@ -218,7 +246,7 @@ else
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_databaseName = (string)eventArgs.Value;
|
_databaseName = (string)eventArgs.Value;
|
||||||
|
_showConnectionString = false;
|
||||||
LoadDatabaseConfigComponent();
|
LoadDatabaseConfigComponent();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -309,15 +337,22 @@ else
|
|||||||
user.Username = _hostusername;
|
user.Username = _hostusername;
|
||||||
user.Password = _hostpassword;
|
user.Password = _hostpassword;
|
||||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||||
user = await UserService.LoginUserAsync(user);
|
user = await UserService.LoginUserAsync(user, false, false);
|
||||||
if (user.IsAuthenticated)
|
if (user.IsAuthenticated)
|
||||||
{
|
{
|
||||||
var connectionString = String.Empty;
|
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||||
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
var connectionString = String.Empty;
|
||||||
{
|
if (_showConnectionString)
|
||||||
connectionString = databaseConfigControl.GetConnectionString();
|
{
|
||||||
}
|
connectionString = _connectionString;
|
||||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (connectionString != "")
|
if (connectionString != "")
|
||||||
{
|
{
|
||||||
@ -398,4 +433,13 @@ else
|
|||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ToggleConnectionString()
|
||||||
|
{
|
||||||
|
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
|
||||||
|
{
|
||||||
|
_connectionString = databaseConfigControl.GetConnectionString();
|
||||||
|
}
|
||||||
|
_showConnectionString = !_showConnectionString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,13 @@
|
|||||||
<input id="ipaddress" class="form-control" @bind="@_ipaddress" readonly />
|
<input id="ipaddress" class="form-control" @bind="@_ipaddress" 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="environment" HelpText="Environment name" ResourceKey="Environment">Environment: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="environment" class="form-control" @bind="@_environment" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
<Label Class="col-sm-3" For="contentrootpath" HelpText="Root Path" ResourceKey="ContentRootPath">Root Path: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="contentrootpath" class="form-control" @bind="@_contentrootpath" readonly />
|
<input id="contentrootpath" class="form-control" @bind="@_contentrootpath" readonly />
|
||||||
@ -152,6 +158,7 @@
|
|||||||
private string _osversion = string.Empty;
|
private string _osversion = string.Empty;
|
||||||
private string _machinename = string.Empty;
|
private string _machinename = string.Empty;
|
||||||
private string _ipaddress = string.Empty;
|
private string _ipaddress = string.Empty;
|
||||||
|
private string _environment = string.Empty;
|
||||||
private string _contentrootpath = string.Empty;
|
private string _contentrootpath = string.Empty;
|
||||||
private string _webrootpath = string.Empty;
|
private string _webrootpath = string.Empty;
|
||||||
private string _servertime = string.Empty;
|
private string _servertime = string.Empty;
|
||||||
@ -176,6 +183,7 @@
|
|||||||
_osversion = systeminfo["OSVersion"].ToString();
|
_osversion = systeminfo["OSVersion"].ToString();
|
||||||
_machinename = systeminfo["MachineName"].ToString();
|
_machinename = systeminfo["MachineName"].ToString();
|
||||||
_ipaddress = systeminfo["IPAddress"].ToString();
|
_ipaddress = systeminfo["IPAddress"].ToString();
|
||||||
|
_environment = systeminfo["Environment"].ToString();
|
||||||
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
_contentrootpath = systeminfo["ContentRootPath"].ToString();
|
||||||
_webrootpath = systeminfo["WebRootPath"].ToString();
|
_webrootpath = systeminfo["WebRootPath"].ToString();
|
||||||
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
_servertime = systeminfo["ServerTime"].ToString() + " UTC";
|
||||||
|
@ -114,6 +114,10 @@
|
|||||||
<button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private string _price = "free";
|
private string _price = "free";
|
||||||
|
@ -25,7 +25,13 @@
|
|||||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||||
</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="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 theme" ResourceKey="Owner">Owner: </Label>
|
<Label Class="col-sm-3" For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||||
@ -53,28 +59,30 @@
|
|||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _themeName = "";
|
private string _themeName = "";
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _version;
|
private string _version;
|
||||||
private string _owner = "";
|
private string _packagename;
|
||||||
private string _url = "";
|
private string _owner = "";
|
||||||
private string _contact = "";
|
private string _url = "";
|
||||||
private string _license = "";
|
private string _contact = "";
|
||||||
|
private string _license = "";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
||||||
var themes = await ThemeService.GetThemesAsync();
|
var themes = await ThemeService.GetThemesAsync();
|
||||||
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
||||||
if (theme != null)
|
if (theme != null)
|
||||||
{
|
{
|
||||||
_name = theme.Name;
|
_name = theme.Name;
|
||||||
_version = theme.Version;
|
_version = theme.Version;
|
||||||
_owner = theme.Owner;
|
_packagename = theme.PackageName;
|
||||||
|
_owner = theme.Owner;
|
||||||
_url = theme.Url;
|
_url = theme.Url;
|
||||||
_contact = theme.Contact;
|
_contact = theme.Contact;
|
||||||
_license = theme.License;
|
_license = theme.License;
|
||||||
|
@ -226,8 +226,8 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
private string _passwordtype = "password";
|
private string _passwordtype = "password";
|
||||||
private string _togglepassword = string.Empty;
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
private bool allowtwofactor = false;
|
private bool allowtwofactor = false;
|
||||||
private string twofactor = "False";
|
private string twofactor = "False";
|
||||||
@ -429,6 +429,7 @@ else
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ModuleInstance.ShowProgressIndicator();
|
||||||
foreach(var Notification in notifications)
|
foreach(var Notification in notifications)
|
||||||
{
|
{
|
||||||
if (!Notification.IsDeleted)
|
if (!Notification.IsDeleted)
|
||||||
@ -444,12 +445,15 @@ else
|
|||||||
}
|
}
|
||||||
await logger.LogInformation("Notifications Permanently Deleted");
|
await logger.LogInformation("Notifications Permanently Deleted");
|
||||||
await LoadNotificationsAsync();
|
await LoadNotificationsAsync();
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||||
AddModuleMessage(ex.Message, MessageType.Error);
|
AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@inject SiteState SiteState
|
|
||||||
|
|
||||||
@if (users == null)
|
@if (users == null)
|
||||||
{
|
{
|
||||||
|
@ -128,13 +128,6 @@
|
|||||||
|
|
||||||
private string CloseUrl()
|
private string CloseUrl()
|
||||||
{
|
{
|
||||||
if (!PageState.QueryString.ContainsKey("type"))
|
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||||
{
|
|
||||||
return NavigateUrl();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ else
|
|||||||
<th>@Localizer["Created"]</th>
|
<th>@Localizer["Created"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString() + "&type=" + _type.ToString() + "&days=" + _days.ToString() + "&page=" + _page.ToString())" ResourceKey="Details" /></td>
|
<td><ActionLink Action="Detail" Parameters="@($"id={context.VisitorId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, $"type={_type}&days={_days}&page={_page}"))" ResourceKey="Details" /></td>
|
||||||
<td>@context.IPAddress</td>
|
<td>@context.IPAddress</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.UserId != null)
|
@if (context.UserId != null)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Net
|
||||||
@inherits LocalizableComponent
|
@inherits LocalizableComponent
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
|
||||||
@ -71,6 +72,9 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool IconOnly { get; set; } // optional - specifies only icon in link
|
public bool IconOnly { get; set; } // optional - specifies only icon in link
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ReturnUrl { get; set; } // optional - used to set a url to redirect to
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -116,9 +120,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
||||||
_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);
|
||||||
_authorized = IsAuthorized();
|
if (!string.IsNullOrEmpty(ReturnUrl))
|
||||||
|
{
|
||||||
|
_url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";
|
||||||
|
}
|
||||||
|
_authorized = IsAuthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsAuthorized()
|
private bool IsAuthorized()
|
||||||
|
@ -376,42 +376,62 @@
|
|||||||
_messagetype = MessageType.Warning;
|
_messagetype = MessageType.Warning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.File.NotSelected"];
|
_message = Localizer["Message.File.NotSelected"];
|
||||||
_messagetype = MessageType.Warning;
|
_messagetype = MessageType.Warning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteFile()
|
private async Task DeleteFile()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await FileService.DeleteFileAsync(FileId);
|
await FileService.DeleteFileAsync(FileId);
|
||||||
await logger.LogInformation("File Deleted {File}", FileId);
|
await logger.LogInformation("File Deleted {File}", FileId);
|
||||||
await OnDelete.InvokeAsync(FileId);
|
await OnDelete.InvokeAsync(FileId);
|
||||||
|
|
||||||
_message = Localizer["Success.File.Delete"];
|
_message = Localizer["Success.File.Delete"];
|
||||||
_messagetype = MessageType.Success;
|
_messagetype = MessageType.Success;
|
||||||
|
|
||||||
await GetFiles();
|
await GetFiles();
|
||||||
FileId = -1;
|
FileId = -1;
|
||||||
await SetImage();
|
await SetImage();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
||||||
|
|
||||||
_message = Localizer["Error.File.Delete"];
|
_message = Localizer["Error.File.Delete"];
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetFileId() => FileId;
|
public int GetFileId() => FileId;
|
||||||
|
|
||||||
public int GetFolderId() => FolderId;
|
public int GetFolderId() => FolderId;
|
||||||
|
|
||||||
public File GetFile() => _file;
|
public File GetFile() => _file;
|
||||||
|
|
||||||
|
public async Task Refresh()
|
||||||
|
{
|
||||||
|
await Refresh(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Refresh(int fileId)
|
||||||
|
{
|
||||||
|
await GetFiles();
|
||||||
|
if (fileId != -1)
|
||||||
|
{
|
||||||
|
var file = _files.Where(item => item.FileId == fileId).FirstOrDefault();
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
FileId = file.FileId;
|
||||||
|
await SetImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,79 +5,100 @@
|
|||||||
<div class="row" style="margin-bottom: 50px;">
|
<div class="row" style="margin-bottom: 50px;">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
||||||
@if (AllowFileManagement)
|
@if (_richfilemanager)
|
||||||
{
|
{
|
||||||
@if (_filemanagervisible)
|
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||||
{
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
<br />
|
||||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
}
|
||||||
<br />
|
<div class="d-flex justify-content-center mb-2">
|
||||||
}
|
@if (AllowRawHtml)
|
||||||
<div class="d-flex justify-content-center mb-2">
|
{
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
||||||
<button type="button" class="btn btn-primary" @onclick="InsertImage">@Localizer["InsertImage"]</button>
|
}
|
||||||
@if (_filemanagervisible)
|
@if (AllowFileManagement)
|
||||||
{
|
{
|
||||||
@((MarkupString)" ")
|
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
|
}
|
||||||
}
|
@if (_richfilemanager)
|
||||||
</div>
|
{
|
||||||
}
|
@((MarkupString)" ")
|
||||||
<div class="row">
|
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||||
<div class="col">
|
}
|
||||||
<div @ref="@_toolBar">
|
</div>
|
||||||
@if (ToolbarContent != null)
|
<div class="row">
|
||||||
{
|
<div class="col">
|
||||||
@ToolbarContent
|
<div @ref="@_toolBar">
|
||||||
}
|
@if (ToolbarContent != null)
|
||||||
else
|
{
|
||||||
{
|
@ToolbarContent
|
||||||
<select class="ql-header">
|
}
|
||||||
<option selected=""></option>
|
else
|
||||||
<option value="1"></option>
|
{
|
||||||
<option value="2"></option>
|
<select class="ql-header">
|
||||||
<option value="3"></option>
|
<option selected=""></option>
|
||||||
<option value="4"></option>
|
<option value="1"></option>
|
||||||
<option value="5"></option>
|
<option value="2"></option>
|
||||||
</select>
|
<option value="3"></option>
|
||||||
<span class="ql-formats">
|
<option value="4"></option>
|
||||||
<button class="ql-bold"></button>
|
<option value="5"></option>
|
||||||
<button class="ql-italic"></button>
|
</select>
|
||||||
<button class="ql-underline"></button>
|
<span class="ql-formats">
|
||||||
<button class="ql-strike"></button>
|
<button class="ql-bold"></button>
|
||||||
</span>
|
<button class="ql-italic"></button>
|
||||||
<span class="ql-formats">
|
<button class="ql-underline"></button>
|
||||||
<select class="ql-color"></select>
|
<button class="ql-strike"></button>
|
||||||
<select class="ql-background"></select>
|
</span>
|
||||||
</span>
|
<span class="ql-formats">
|
||||||
<span class="ql-formats">
|
<select class="ql-color"></select>
|
||||||
<button class="ql-list" value="ordered"></button>
|
<select class="ql-background"></select>
|
||||||
<button class="ql-list" value="bullet"></button>
|
</span>
|
||||||
</span>
|
<span class="ql-formats">
|
||||||
<span class="ql-formats">
|
<button class="ql-list" value="ordered"></button>
|
||||||
<button class="ql-link"></button>
|
<button class="ql-list" value="bullet"></button>
|
||||||
</span>
|
</span>
|
||||||
}
|
<span class="ql-formats">
|
||||||
</div>
|
<button class="ql-link"></button>
|
||||||
<div @ref="@_editorElement">
|
</span>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div @ref="@_editorElement">
|
||||||
</TabPanel>
|
</div>
|
||||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
</div>
|
||||||
<div class="d-flex justify-content-center mb-2">
|
</div>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
</TabPanel>
|
||||||
</div>
|
@if (AllowRawHtml)
|
||||||
@if (ReadOnly)
|
{
|
||||||
{
|
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
@if (_rawfilemanager)
|
||||||
}
|
{
|
||||||
else
|
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||||
{
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
<br />
|
||||||
}
|
}
|
||||||
</TabPanel>
|
<div class="d-flex justify-content-center mb-2">
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
||||||
|
@if (AllowFileManagement)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
|
||||||
|
}
|
||||||
|
@if (_rawfilemanager)
|
||||||
|
{
|
||||||
|
@((MarkupString)" ")
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="CloseRawFileManager">@Localizer["Close"]</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (ReadOnly)
|
||||||
|
{
|
||||||
|
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<textarea id="rawhtmleditor" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||||
|
}
|
||||||
|
</TabPanel>
|
||||||
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -85,10 +106,11 @@
|
|||||||
@code {
|
@code {
|
||||||
private ElementReference _editorElement;
|
private ElementReference _editorElement;
|
||||||
private ElementReference _toolBar;
|
private ElementReference _toolBar;
|
||||||
private bool _filemanagervisible = false;
|
private bool _richfilemanager = false;
|
||||||
private FileManager _fileManager;
|
private FileManager _fileManager;
|
||||||
private string _richhtml = string.Empty;
|
private string _richhtml = string.Empty;
|
||||||
private string _originalrichhtml = string.Empty;
|
private string _originalrichhtml = string.Empty;
|
||||||
|
private bool _rawfilemanager = false;
|
||||||
private string _rawhtml = string.Empty;
|
private string _rawhtml = string.Empty;
|
||||||
private string _originalrawhtml = string.Empty;
|
private string _originalrawhtml = string.Empty;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
@ -102,6 +124,12 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool AllowFileManagement { get; set; } = true;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool AllowRawHtml { get; set; } = true;
|
||||||
|
|
||||||
// parameters only applicable to rich text editor
|
// parameters only applicable to rich text editor
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment ToolbarContent { get; set; }
|
public RenderFragment ToolbarContent { get; set; }
|
||||||
@ -112,9 +140,6 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string DebugLevel { get; set; } = "info";
|
public string DebugLevel { get; set; } = "info";
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool AllowFileManagement { get; set; } = true;
|
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||||
@ -152,9 +177,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseFileManager()
|
public void CloseRichFileManager()
|
||||||
{
|
{
|
||||||
_filemanagervisible = false;
|
_richfilemanager = false;
|
||||||
|
_message = string.Empty;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseRawFileManager()
|
||||||
|
{
|
||||||
|
_rawfilemanager = false;
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
@ -194,29 +226,55 @@
|
|||||||
return _originalrawhtml;
|
return _originalrawhtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InsertImage()
|
public async Task InsertRichImage()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
if (_filemanagervisible)
|
if (_richfilemanager)
|
||||||
{
|
{
|
||||||
var file = _fileManager.GetFile();
|
var file = _fileManager.GetFile();
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
await interop.InsertImage(_editorElement, file.Url, file.Name);
|
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name));
|
||||||
_filemanagervisible = false;
|
_richfilemanager = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_message = Localizer["Message.Require.Image"];
|
_message = Localizer["Message.Require.Image"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_filemanagervisible = true;
|
_richfilemanager = true;
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task InsertRawImage()
|
||||||
|
{
|
||||||
|
_message = string.Empty;
|
||||||
|
if (_rawfilemanager)
|
||||||
|
{
|
||||||
|
var file = _fileManager.GetFile();
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
int pos = await interop.GetCaretPosition("rawhtmleditor");
|
||||||
|
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\">";
|
||||||
|
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
|
||||||
|
_rawfilemanager = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_message = Localizer["Message.Require.Image"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_rawfilemanager = true;
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
|
||||||
@if (_content != null)
|
@if (_content != null)
|
||||||
{
|
{
|
||||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||||
<br />
|
<br />
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
private RichTextEditor RichTextEditorHtml;
|
private RichTextEditor RichTextEditorHtml;
|
||||||
private bool _allowfilemanagement;
|
private bool _allowfilemanagement;
|
||||||
|
private bool _allowrawhtml;
|
||||||
private string _content = null;
|
private string _content = null;
|
||||||
private string _createdby;
|
private string _createdby;
|
||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
@ -73,6 +74,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||||
|
_allowrawhtml = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true"));
|
||||||
await LoadContent();
|
await LoadContent();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -15,18 +15,29 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="files" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify If Editors Can Enter Raw HTML">Allow Raw HTML: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="files" class="form-select" @bind="@_allowrawhtml">
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||||
private string _allowfilemanagement;
|
private string _allowfilemanagement;
|
||||||
|
private string _allowrawhtml;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
|
||||||
}
|
_allowrawhtml = SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true");
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||||
@ -39,7 +50,8 @@
|
|||||||
{
|
{
|
||||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
||||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
settings = SettingService.SetSetting(settings, "AllowRawHtml", _allowrawhtml);
|
||||||
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,8 @@ namespace Oqtane.Modules
|
|||||||
public abstract class ModuleBase : ComponentBase, IModuleControl
|
public abstract class ModuleBase : ComponentBase, IModuleControl
|
||||||
{
|
{
|
||||||
private Logger _logger;
|
private Logger _logger;
|
||||||
|
private string _urlparametersstate;
|
||||||
|
private Dictionary<string, string> _urlparameters;
|
||||||
|
|
||||||
protected Logger logger => _logger ?? (_logger = new Logger(this));
|
protected Logger logger => _logger ?? (_logger = new Logger(this));
|
||||||
|
|
||||||
@ -24,6 +26,9 @@ namespace Oqtane.Modules
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected IJSRuntime JSRuntime { get; set; }
|
protected IJSRuntime JSRuntime { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected SiteState SiteState { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected PageState PageState { get; set; }
|
protected PageState PageState { get; set; }
|
||||||
|
|
||||||
@ -44,6 +49,21 @@ namespace Oqtane.Modules
|
|||||||
|
|
||||||
public virtual List<Resource> Resources { get; set; }
|
public virtual List<Resource> Resources { get; set; }
|
||||||
|
|
||||||
|
// url parameters
|
||||||
|
public virtual string UrlParametersTemplate { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string> UrlParameters {
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_urlparametersstate == null || _urlparametersstate != PageState.UrlParameters)
|
||||||
|
{
|
||||||
|
_urlparametersstate = PageState.UrlParameters;
|
||||||
|
_urlparameters = GetUrlParameters(UrlParametersTemplate);
|
||||||
|
}
|
||||||
|
return _urlparameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// base lifecycle method for handling JSInterop script registration
|
// base lifecycle method for handling JSInterop script registration
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
@ -55,7 +75,8 @@ namespace Oqtane.Modules
|
|||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + "/" + resource.Url;
|
||||||
|
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
@ -149,15 +170,26 @@ namespace Oqtane.Modules
|
|||||||
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
|
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
|
public string AddUrlParameters(params object[] parameters)
|
||||||
|
{
|
||||||
|
var url = "";
|
||||||
|
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
|
||||||
|
// if url parameters belong to a specific module you should embed a unique key into the route (ie. /!/blog/1) and validate the url parameter key in the module
|
||||||
|
public virtual Dictionary<string, string> GetUrlParameters(string template = "")
|
||||||
{
|
{
|
||||||
var urlParameters = new Dictionary<string, string>();
|
var urlParameters = new Dictionary<string, string>();
|
||||||
string[] templateSegments;
|
var parameters = _urlparametersstate.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
var parameterId = 0;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(parametersTemplate))
|
if (string.IsNullOrEmpty(template))
|
||||||
{
|
{
|
||||||
|
// no template will populate dictionary with generic "parameter#" keys
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
urlParameters.TryAdd("parameter" + i, parameters[i]);
|
urlParameters.TryAdd("parameter" + i, parameters[i]);
|
||||||
@ -165,39 +197,37 @@ namespace Oqtane.Modules
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
templateSegments = parametersTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
var segments = template.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
string key;
|
||||||
|
|
||||||
if (parameters.Length == templateSegments.Length)
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
if (i < segments.Length)
|
||||||
{
|
{
|
||||||
if (parameters.Length > i)
|
key = segments[i];
|
||||||
|
if (key.StartsWith("{") && key.EndsWith("}"))
|
||||||
{
|
{
|
||||||
if (templateSegments[i] == parameters[i])
|
// dynamic segment
|
||||||
{
|
key = key.Substring(1, key.Length - 2);
|
||||||
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
|
}
|
||||||
parameterId++;
|
else
|
||||||
}
|
{
|
||||||
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
|
// static segments use generic "parameter#" keys
|
||||||
{
|
key = "parameter" + i.ToString();
|
||||||
var key = templateSegments[i].Replace("{", "");
|
|
||||||
key = key.Replace("}", "");
|
|
||||||
urlParameters.TryAdd(key, parameters[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
i = parameters.Length;
|
|
||||||
urlParameters.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else // unspecified segments use generic "parameter#" keys
|
||||||
|
{
|
||||||
|
key = "parameter" + i.ToString();
|
||||||
|
}
|
||||||
|
urlParameters.TryAdd(key, parameters[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return urlParameters;
|
return urlParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// user feedback methods
|
// UI methods
|
||||||
public void AddModuleMessage(string message, MessageType type)
|
public void AddModuleMessage(string message, MessageType type)
|
||||||
{
|
{
|
||||||
ModuleInstance.AddModuleMessage(message, type);
|
ModuleInstance.AddModuleMessage(message, type);
|
||||||
@ -218,6 +248,18 @@ namespace Oqtane.Modules
|
|||||||
ModuleInstance.HideProgressIndicator();
|
ModuleInstance.HideProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetModuleTitle(string title)
|
||||||
|
{
|
||||||
|
var obj = new { PageModuleId = ModuleState.PageModuleId, Title = title };
|
||||||
|
SiteState.Properties.ModuleTitle = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetModuleVisibility(bool visible)
|
||||||
|
{
|
||||||
|
var obj = new { PageModuleId = ModuleState.PageModuleId, Visible = visible };
|
||||||
|
SiteState.Properties.ModuleVisibility = obj;
|
||||||
|
}
|
||||||
|
|
||||||
// logging methods
|
// logging methods
|
||||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||||
{
|
{
|
||||||
|
@ -5,15 +5,15 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RazorLangVersion>3.0</RazorLangVersion>
|
<RazorLangVersion>3.0</RazorLangVersion>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.1.3</Version>
|
<Version>3.2.0</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
<Description>Modular Application Framework for Blazor</Description>
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
<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.1.3</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.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>
|
||||||
@ -35,13 +35,8 @@
|
|||||||
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<TrimmerRootAssembly Include="System.Runtime" />
|
|
||||||
<TrimmerRootAssembly Include="System.Linq.Parallel" />
|
|
||||||
<TrimmerRootAssembly Include="System.Runtime.CompilerServices.VisualC" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<PublishTrimmed>false</PublishTrimmed>
|
||||||
<BlazorEnableCompression>false</BlazorEnableCompression>
|
<BlazorEnableCompression>false</BlazorEnableCompression>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
using Microsoft.AspNetCore.Localization;
|
using Microsoft.AspNetCore.Localization;
|
||||||
@ -15,7 +16,6 @@ using Microsoft.JSInterop;
|
|||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Modules;
|
using Oqtane.Modules;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
|
||||||
using Oqtane.UI;
|
using Oqtane.UI;
|
||||||
|
|
||||||
namespace Oqtane.Client
|
namespace Oqtane.Client
|
||||||
@ -33,7 +33,7 @@ namespace Oqtane.Client
|
|||||||
|
|
||||||
builder.Services.AddOptions();
|
builder.Services.AddOptions();
|
||||||
|
|
||||||
// Register localization services
|
// register localization services
|
||||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
// register auth services
|
// register auth services
|
||||||
@ -42,7 +42,9 @@ namespace Oqtane.Client
|
|||||||
// register scoped core services
|
// register scoped core services
|
||||||
builder.Services.AddOqtaneScopedServices();
|
builder.Services.AddOqtaneScopedServices();
|
||||||
|
|
||||||
await LoadClientAssemblies(httpClient);
|
var serviceProvider = builder.Services.BuildServiceProvider();
|
||||||
|
|
||||||
|
await LoadClientAssemblies(httpClient, serviceProvider);
|
||||||
|
|
||||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||||
foreach (var assembly in assemblies)
|
foreach (var assembly in assemblies)
|
||||||
@ -54,37 +56,105 @@ namespace Oqtane.Client
|
|||||||
RegisterClientStartups(assembly, builder.Services);
|
RegisterClientStartups(assembly, builder.Services);
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = builder.Build();
|
await builder.Build().RunAsync();
|
||||||
|
|
||||||
await SetCultureFromLocalizationCookie(host.Services);
|
|
||||||
|
|
||||||
ServiceActivator.Configure(host.Services);
|
|
||||||
|
|
||||||
await host.RunAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LoadClientAssemblies(HttpClient http)
|
private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
// get list of loaded assemblies on the client
|
var dlls = new Dictionary<string, byte[]>();
|
||||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
|
var pdbs = new Dictionary<string, byte[]>();
|
||||||
|
var filter = new List<string>();
|
||||||
|
|
||||||
// get assemblies from server and load into client app domain
|
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||||
var zip = await http.GetByteArrayAsync($"/api/Installation/load");
|
var interop = new Interop(jsRuntime);
|
||||||
|
var files = await interop.GetIndexedDBKeys(".dll");
|
||||||
|
|
||||||
// asemblies and debug symbols are packaged in a zip file
|
if (files.Count() != 0)
|
||||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
|
||||||
{
|
{
|
||||||
var dlls = new Dictionary<string, byte[]>();
|
// get list of assemblies from server
|
||||||
var pdbs = new Dictionary<string, byte[]>();
|
var json = await http.GetStringAsync("/api/Installation/list");
|
||||||
|
var assemblies = JsonSerializer.Deserialize<List<string>>(json);
|
||||||
|
|
||||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
// determine which assemblies need to be downloaded
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.FullName)))
|
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
filter.Add(assembly);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check if newer version available
|
||||||
|
if (GetFileDate(assembly) > GetFileDate(file))
|
||||||
|
{
|
||||||
|
filter.Add(assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get assemblies already downloaded
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (assemblies.Contains(file) && !filter.Contains(file))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dlls.Add(file, await interop.GetIndexedDBItem<byte[]>(file));
|
||||||
|
var pdb = file.Replace(".dll", ".pdb");
|
||||||
|
if (files.Contains(pdb))
|
||||||
|
{
|
||||||
|
pdbs.Add(pdb, await interop.GetIndexedDBItem<byte[]>(pdb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // file is deprecated
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await interop.RemoveIndexedDBItem(file);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filter.Add("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.Count != 0)
|
||||||
|
{
|
||||||
|
// get assemblies from server and load into client app domain
|
||||||
|
var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", filter));
|
||||||
|
|
||||||
|
// asemblies and debug symbols are packaged in a zip file
|
||||||
|
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||||
|
{
|
||||||
|
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||||
{
|
{
|
||||||
using (var memoryStream = new MemoryStream())
|
using (var memoryStream = new MemoryStream())
|
||||||
{
|
{
|
||||||
entry.Open().CopyTo(memoryStream);
|
entry.Open().CopyTo(memoryStream);
|
||||||
byte[] file = memoryStream.ToArray();
|
byte[] file = memoryStream.ToArray();
|
||||||
|
|
||||||
|
// save assembly to indexeddb
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await interop.SetIndexedDBItem(entry.FullName, file);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
switch (Path.GetExtension(entry.FullName))
|
switch (Path.GetExtension(entry.FullName))
|
||||||
{
|
{
|
||||||
case ".dll":
|
case ".dll":
|
||||||
@ -97,23 +167,31 @@ namespace Oqtane.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var item in dlls)
|
// load assemblies into app domain
|
||||||
|
foreach (var item in dlls)
|
||||||
|
{
|
||||||
|
if (pdbs.ContainsKey(item.Key.Replace(".dll", ".pdb")))
|
||||||
{
|
{
|
||||||
if (pdbs.ContainsKey(item.Key))
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key.Replace(".dll", ".pdb")]));
|
||||||
{
|
}
|
||||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key]));
|
else
|
||||||
}
|
{
|
||||||
else
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
|
||||||
{
|
|
||||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DateTime GetFileDate(string filepath)
|
||||||
|
{
|
||||||
|
var segments = filepath.Split('.');
|
||||||
|
return DateTime.ParseExact(segments[segments.Length - 2], "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
|
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
// dynamically register module scoped services
|
||||||
var implementationTypes = assembly.GetInterfaces<IService>();
|
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||||
foreach (var implementationType in implementationTypes)
|
foreach (var implementationType in implementationTypes)
|
||||||
{
|
{
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
<value>Server:</value>
|
<value>Server:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Server.HelpText" xml:space="preserve">
|
<data name="Server.HelpText" xml:space="preserve">
|
||||||
<value>Enter the database server</value>
|
<value>Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Database.Text" xml:space="preserve">
|
<data name="Database.Text" xml:space="preserve">
|
||||||
<value>Database:</value>
|
<value>Database:</value>
|
||||||
@ -133,7 +133,7 @@
|
|||||||
<value>Integrated Security:</value>
|
<value>Integrated Security:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
|
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
|
||||||
<value>Select if you want integrated security or not</value>
|
<value>Select if you are using integrated security</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Uid.Text" xml:space="preserve">
|
<data name="Uid.Text" xml:space="preserve">
|
||||||
<value>User Id:</value>
|
<value>User Id:</value>
|
||||||
@ -163,7 +163,7 @@
|
|||||||
<value>Self Signed</value>
|
<value>Self Signed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TrustServerCertificate.HelpText" xml:space="preserve">
|
<data name="TrustServerCertificate.HelpText" xml:space="preserve">
|
||||||
<value>Specify the type of certificate you are using for encryption</value>
|
<value>Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TrustServerCertificate.Text" xml:space="preserve">
|
<data name="TrustServerCertificate.Text" xml:space="preserve">
|
||||||
<value>Trust Server Certificate:</value>
|
<value>Trust Server Certificate:</value>
|
||||||
|
@ -135,10 +135,10 @@
|
|||||||
<data name="Message.Require.DbInfo" xml:space="preserve">
|
<data name="Message.Require.DbInfo" xml:space="preserve">
|
||||||
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||||
<value>The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements.</value>
|
<value>The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Register" xml:space="preserve">
|
<data name="Register" xml:space="preserve">
|
||||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.HelpText" xml:space="preserve">
|
<data name="Confirm.HelpText" xml:space="preserve">
|
||||||
@ -168,4 +168,16 @@
|
|||||||
<data name="Username.Text" xml:space="preserve">
|
<data name="Username.Text" xml:space="preserve">
|
||||||
<value>Username:</value>
|
<value>Username:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConnectionString.Text" xml:space="preserve">
|
||||||
|
<value>String:</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>
|
||||||
</root>
|
</root>
|
@ -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
|
||||||
@ -118,6 +118,6 @@
|
|||||||
<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="Error.Module.Load" xml:space="preserve">
|
<data name="Error.Module.Load" xml:space="preserve">
|
||||||
<value>A Problem Was Encountered Loading Module {0}</value>
|
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
@ -192,4 +192,7 @@
|
|||||||
<data name="Once" xml:space="preserve">
|
<data name="Once" xml:space="preserve">
|
||||||
<value>Execute Once</value>
|
<value>Execute Once</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.NoJobs" xml:space="preserve">
|
||||||
|
<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>
|
||||||
</root>
|
</root>
|
@ -154,13 +154,13 @@
|
|||||||
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Download.Heading" xml:space="preserve">
|
<data name="Download.Heading" xml:space="preserve">
|
||||||
<value>Download</value>
|
<value>Translations</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||||
<value>Upload one or more translations. Once they are uploaded click Install to complete the installation.</value>
|
<value>Upload one or more translation packages. Once they are uploaded click Install to complete the installation.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LanguageUpload.Text" xml:space="preserve">
|
<data name="LanguageUpload.Text" xml:space="preserve">
|
||||||
<value>Upload Language</value>
|
<value>Translation</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Manage.Heading" xml:space="preserve">
|
<data name="Manage.Heading" xml:space="preserve">
|
||||||
<value>Manage</value>
|
<value>Manage</value>
|
||||||
|
@ -144,4 +144,7 @@
|
|||||||
<data name="DeleteLanguage.Text" xml:space="preserve">
|
<data name="DeleteLanguage.Text" xml:space="preserve">
|
||||||
<value>Delete</value>
|
<value>Delete</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Translation" xml:space="preserve">
|
||||||
|
<value>Translation</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -195,4 +195,25 @@
|
|||||||
<data name="Information.Text" xml:space="preserve">
|
<data name="Information.Text" xml:space="preserve">
|
||||||
<value>Information</value>
|
<value>Information</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PackageName.HelpText" xml:space="preserve">
|
||||||
|
<value>The unique name of the package from which this module was installed</value>
|
||||||
|
</data>
|
||||||
|
<data name="PackageName.Text" xml:space="preserve">
|
||||||
|
<value>Package Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Translation.Download" xml:space="preserve">
|
||||||
|
<value>Error Downloading Translation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Search.NoResults" xml:space="preserve">
|
||||||
|
<value>No Translations Exist For This Module Or Package Service Is Disabled</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.Translation.Download" xml:space="preserve">
|
||||||
|
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.Translation.Install" xml:space="preserve">
|
||||||
|
<value>Translation Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Translations.Heading" xml:space="preserve">
|
||||||
|
<value>Translations</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -147,4 +147,7 @@
|
|||||||
<data name="Message.Required.Title" xml:space="preserve">
|
<data name="Message.Required.Title" xml:space="preserve">
|
||||||
<value>You Must Provide A Title For The Module</value>
|
<value>You Must Provide A Title For The Module</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Module.Load" xml:space="preserve">
|
||||||
|
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -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
|
||||||
@ -160,7 +160,7 @@
|
|||||||
<value>Error Loading Pane Layouts For Theme</value>
|
<value>Error Loading Pane Layouts For Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Page.Exists" xml:space="preserve">
|
<data name="Message.Page.Exists" xml:space="preserve">
|
||||||
<value>A page with path {0} already exists for the selected parent page. The page path needs to be unique for the selected parent.</value>
|
<value>A page with path '{0}' already exists for this site. Page paths must be unique. You may need to check if a page with this path exists in the Recycle Bin.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.PageInfo" xml:space="preserve">
|
<data name="Message.Required.PageInfo" xml:space="preserve">
|
||||||
<value>You Must Provide Page Name, Theme, and Container</value>
|
<value>You Must Provide Page Name, Theme, and Container</value>
|
||||||
@ -228,13 +228,13 @@
|
|||||||
<data name="Appearance.Name" xml:space="preserve">
|
<data name="Appearance.Name" xml:space="preserve">
|
||||||
<value>Appearance</value>
|
<value>Appearance</value>
|
||||||
</data>
|
</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>
|
|
||||||
<data name="Meta.HelpText" xml:space="preserve">
|
<data name="Meta.HelpText" xml:space="preserve">
|
||||||
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
<value>Optionally enter meta tags (in exactly the form you want them to be included in the page output).</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Meta.Text" xml:space="preserve">
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
<value>Meta:</value>
|
<value>Meta:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Page.Reserved" xml:space="preserve">
|
||||||
|
<value>The page name {0} is reserved. Please enter a different name for your page.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -151,7 +151,7 @@
|
|||||||
<value>Error Loading Pane Layouts For Theme</value>
|
<value>Error Loading Pane Layouts For Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Mesage.Page.PathExists" xml:space="preserve">
|
<data name="Mesage.Page.PathExists" xml:space="preserve">
|
||||||
<value>A page with path {0} already exists for the selected parent page. The page path needs to be unique for the selected parent.</value>
|
<value>A page with path '{0}' already exists for this site. Page paths must be unique. You may need to check if a page with this path exists in the Recycle Bin.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Required.PageInfo" xml:space="preserve">
|
<data name="Message.Required.PageInfo" xml:space="preserve">
|
||||||
<value>You Must Provide Page Name, Theme, and Container</value>
|
<value>You Must Provide Page Name, Theme, and Container</value>
|
||||||
@ -270,4 +270,7 @@
|
|||||||
<data name="Meta.Text" xml:space="preserve">
|
<data name="Meta.Text" xml:space="preserve">
|
||||||
<value>Meta:</value>
|
<value>Meta:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Page.Reserved" xml:space="preserve">
|
||||||
|
<value>The page name {0} is reserved. Please enter a different name for your page.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -166,7 +166,7 @@
|
|||||||
<value>Enter the tenant for the site</value>
|
<value>Enter the tenant 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). If a site has multiple aliases they should be separated by commas.</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>
|
||||||
</data>
|
</data>
|
||||||
<data name="IsDeleted.HelpText" xml:space="preserve">
|
<data name="IsDeleted.HelpText" xml:space="preserve">
|
||||||
<value>Is this site deleted?</value>
|
<value>Is this site deleted?</value>
|
||||||
@ -333,4 +333,10 @@
|
|||||||
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Delete {0}?</value>
|
<value>Are You Sure You Wish To Delete {0}?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HomePage.HelpText" xml:space="preserve">
|
||||||
|
<value>Select the home page for the site (to be used if there is no page with a path of '/')</value>
|
||||||
|
</data>
|
||||||
|
<data name="HomePage.Text" xml:space="preserve">
|
||||||
|
<value>Home Page:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -270,4 +270,16 @@
|
|||||||
<data name="Runtime.Text" xml:space="preserve">
|
<data name="Runtime.Text" xml:space="preserve">
|
||||||
<value>Runtime: </value>
|
<value>Runtime: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ConnectionString.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a complete connection string including all parameters and delimiters</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConnectionString.Text" xml:space="preserve">
|
||||||
|
<value>String:</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>
|
||||||
</root>
|
</root>
|
@ -270,4 +270,10 @@
|
|||||||
<data name="WorkingSet.Text" xml:space="preserve">
|
<data name="WorkingSet.Text" xml:space="preserve">
|
||||||
<value>Memory Allocation:</value>
|
<value>Memory Allocation:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Environment.HelpText" xml:space="preserve">
|
||||||
|
<value>Environment Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Environment.Text" xml:space="preserve">
|
||||||
|
<value>Environment:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -162,4 +162,10 @@
|
|||||||
<data name="License.HelpText" xml:space="preserve">
|
<data name="License.HelpText" xml:space="preserve">
|
||||||
<value>The license of the theme</value>
|
<value>The license of the theme</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PackageName.HelpText" xml:space="preserve">
|
||||||
|
<value>The unique name of the package from which this module was installed</value>
|
||||||
|
</data>
|
||||||
|
<data name="PackageName.Text" xml:space="preserve">
|
||||||
|
<value>Package Name:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -123,4 +123,10 @@
|
|||||||
<data name="AllowFileManagement.Text" xml:space="preserve">
|
<data name="AllowFileManagement.Text" xml:space="preserve">
|
||||||
<value>Allow File Management: </value>
|
<value>Allow File Management: </value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AllowRawHtml.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify If Editors Can Enter Raw HTML</value>
|
||||||
|
</data>
|
||||||
|
<data name="AllowRawHtml.Text" xml:space="preserve">
|
||||||
|
<value>Allow Raw HTML:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -318,6 +318,9 @@
|
|||||||
<data name="BlazorWebAssembly" xml:space="preserve">
|
<data name="BlazorWebAssembly" xml:space="preserve">
|
||||||
<value>Blazor WebAssembly</value>
|
<value>Blazor WebAssembly</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="BlazorHybrid" xml:space="preserve">
|
||||||
|
<value>Blazor Hybrid</value>
|
||||||
|
</data>
|
||||||
<data name="Settings" xml:space="preserve">
|
<data name="Settings" xml:space="preserve">
|
||||||
<value>Settings</value>
|
<value>Settings</value>
|
||||||
</data>
|
</data>
|
||||||
@ -336,4 +339,7 @@
|
|||||||
<data name="Visitor Management" xml:space="preserve">
|
<data name="Visitor Management" xml:space="preserve">
|
||||||
<value>Visitor Management</value>
|
<value>Visitor Management</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Oqtane.Marketplace" xml:space="preserve">
|
||||||
|
<value>Please note that the third party extensions displayed above have been registered in the <a href="https://www.oqtane.net" target="_new">Oqtane Marketplace</a> which enables them to be seamlessly downloaded and installed into the framework.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -138,6 +138,12 @@
|
|||||||
<data name="Register.Text" xml:space="preserve">
|
<data name="Register.Text" xml:space="preserve">
|
||||||
<value>Show Register?</value>
|
<value>Show Register?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Scope.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify if the settings are applicable to this page or the entire site.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Scope.Text" xml:space="preserve">
|
||||||
|
<value>Setting Scope:</value>
|
||||||
|
</data>
|
||||||
<data name="Site" xml:space="preserve">
|
<data name="Site" xml:space="preserve">
|
||||||
<value>Site</value>
|
<value>Site</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -47,7 +47,7 @@ namespace Oqtane.Services
|
|||||||
var path = WebUtility.UrlEncode(folderPath);
|
var path = WebUtility.UrlEncode(folderPath);
|
||||||
|
|
||||||
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
List<File> files = await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
|
||||||
return files.OrderBy(item => item.Name).ToList();
|
return files?.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<File> GetFileAsync(int fileId)
|
public async Task<File> GetFileAsync(int fileId)
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
@ -20,9 +18,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
||||||
{
|
{
|
||||||
List<Folder> folders = await GetJsonAsync<List<Folder>>($"{ApiUrl}?siteid={siteId}");
|
return await GetJsonAsync<List<Folder>>($"{ApiUrl}?siteid={siteId}");
|
||||||
folders = GetFoldersHierarchy(folders);
|
|
||||||
return folders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Folder> GetFolderAsync(int folderId)
|
public async Task<Folder> GetFolderAsync(int folderId)
|
||||||
@ -58,48 +54,5 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
await DeleteAsync($"{ApiUrl}/{folderId}");
|
await DeleteAsync($"{ApiUrl}/{folderId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Folder> GetFoldersHierarchy(List<Folder> folders)
|
|
||||||
{
|
|
||||||
List<Folder> hierarchy = new List<Folder>();
|
|
||||||
Action<List<Folder>, Folder> getPath = null;
|
|
||||||
var folders1 = folders;
|
|
||||||
getPath = (folderList, folder) =>
|
|
||||||
{
|
|
||||||
IEnumerable<Folder> children;
|
|
||||||
int level;
|
|
||||||
if (folder == null)
|
|
||||||
{
|
|
||||||
level = -1;
|
|
||||||
children = folders1.Where(item => item.ParentId == null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
level = folder.Level;
|
|
||||||
children = folders1.Where(item => item.ParentId == folder.FolderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Folder child in children)
|
|
||||||
{
|
|
||||||
child.Level = level + 1;
|
|
||||||
child.HasChildren = folders1.Any(item => item.ParentId == child.FolderId);
|
|
||||||
hierarchy.Add(child);
|
|
||||||
if (getPath != null) getPath(folderList, child);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
folders = folders.OrderBy(item => item.Order).ToList();
|
|
||||||
getPath(folders, null);
|
|
||||||
|
|
||||||
// add any non-hierarchical items to the end of the list
|
|
||||||
foreach (Folder folder in folders)
|
|
||||||
{
|
|
||||||
if (hierarchy.Find(item => item.FolderId == folder.FolderId) == null)
|
|
||||||
{
|
|
||||||
hierarchy.Add(folder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hierarchy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,14 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<Language>> GetLanguagesAsync(int siteId);
|
Task<List<Language>> GetLanguagesAsync(int siteId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of all available languages for the given <see cref="Site" /> and package
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="siteId"></param>
|
||||||
|
/// <param name="packageName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<Language>> GetLanguagesAsync(int siteId, string packageName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the given language
|
/// Returns the given language
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -50,13 +50,13 @@ namespace Oqtane.Services
|
|||||||
/// <param name="moduleId"></param>
|
/// <param name="moduleId"></param>
|
||||||
/// <param name="content">module in JSON format</param>
|
/// <param name="content">module in JSON format</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<bool> ImportModuleAsync(int moduleId, string content);
|
Task<bool> ImportModuleAsync(int moduleId, int pageId, string content);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exports a given module
|
/// Exports a given module
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="moduleId"></param>
|
/// <param name="moduleId"></param>
|
||||||
/// <returns>module in JSON</returns>
|
/// <returns>module in JSON</returns>
|
||||||
Task<string> ExportModuleAsync(int moduleId);
|
Task<string> ExportModuleAsync(int moduleId, int pageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,10 @@ namespace Oqtane.Services
|
|||||||
/// Note that this will probably not be a real User, but a user object where the `Username` and `Password` have been filled.
|
/// Note that this will probably not be a real User, but a user object where the `Username` and `Password` have been filled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">A <see cref="User"/> object which should have at least the <see cref="User.Username"/> and <see cref="User.Password"/> set.</param>
|
/// <param name="user">A <see cref="User"/> object which should have at least the <see cref="User.Username"/> and <see cref="User.Password"/> set.</param>
|
||||||
|
/// <param name="setCookie">Determines if the login cookie should be set (only relevant for Hybrid scenarios)</param>
|
||||||
|
/// <param name="isPersistent">Determines if the login cookie should be persisted for a long time.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<User> LoginUserAsync(User user);
|
Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logout a <see cref="User"/>
|
/// Logout a <see cref="User"/>
|
||||||
|
@ -17,18 +17,27 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
public async Task<List<Language>> GetLanguagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
var languages = await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}");
|
return await GetLanguagesAsync(siteId, "");
|
||||||
|
}
|
||||||
|
|
||||||
return languages?.OrderBy(l => l.Name).ToList() ?? Enumerable.Empty<Language>().ToList();
|
public async Task<List<Language>> GetLanguagesAsync(int siteId, string packageName)
|
||||||
|
{
|
||||||
|
return await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}&packagename={packageName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Language> GetLanguageAsync(int languageId)
|
public async Task<Language> GetLanguageAsync(int languageId)
|
||||||
=> await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
{
|
||||||
|
return await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Language> AddLanguageAsync(Language language)
|
public async Task<Language> AddLanguageAsync(Language language)
|
||||||
=> await PostJsonAsync<Language>(Apiurl, language);
|
{
|
||||||
|
return await PostJsonAsync<Language>(Apiurl, language);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteLanguageAsync(int languageId)
|
public async Task DeleteLanguageAsync(int languageId)
|
||||||
=> await DeleteAsync($"{Apiurl}/{languageId}");
|
{
|
||||||
|
await DeleteAsync($"{Apiurl}/{languageId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.Net.Http;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
using Oqtane.Modules.Controls;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
@ -44,14 +45,14 @@ namespace Oqtane.Services
|
|||||||
await DeleteAsync($"{Apiurl}/{moduleId.ToString()}");
|
await DeleteAsync($"{Apiurl}/{moduleId.ToString()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ImportModuleAsync(int moduleId, string content)
|
public async Task<bool> ImportModuleAsync(int moduleId, int pageId, string content)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<string,bool>($"{Apiurl}/import?moduleid={moduleId}", content);
|
return await PostJsonAsync<string,bool>($"{Apiurl}/import?moduleid={moduleId}&pageid={pageId}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> ExportModuleAsync(int moduleId)
|
public async Task<string> ExportModuleAsync(int moduleId, int pageId)
|
||||||
{
|
{
|
||||||
return await GetStringAsync($"{Apiurl}/export?moduleid={moduleId}");
|
return await GetStringAsync($"{Apiurl}/export?moduleid={moduleId}&pageid={pageId}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
|
|
||||||
@ -19,9 +17,7 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
public async Task<List<Page>> GetPagesAsync(int siteId)
|
public async Task<List<Page>> GetPagesAsync(int siteId)
|
||||||
{
|
{
|
||||||
List<Page> pages = await GetJsonAsync<List<Page>>($"{Apiurl}?siteid={siteId}");
|
return await GetJsonAsync<List<Page>>($"{Apiurl}?siteid={siteId}");
|
||||||
pages = GetPagesHierarchy(pages);
|
|
||||||
return pages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Page> GetPageAsync(int pageId)
|
public async Task<Page> GetPageAsync(int pageId)
|
||||||
@ -73,45 +69,5 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
await DeleteAsync($"{Apiurl}/{pageId}");
|
await DeleteAsync($"{Apiurl}/{pageId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Page> GetPagesHierarchy(List<Page> pages)
|
|
||||||
{
|
|
||||||
List<Page> hierarchy = new List<Page>();
|
|
||||||
Action<List<Page>, Page> getPath = null;
|
|
||||||
getPath = (pageList, page) =>
|
|
||||||
{
|
|
||||||
IEnumerable<Page> children;
|
|
||||||
int level;
|
|
||||||
if (page == null)
|
|
||||||
{
|
|
||||||
level = -1;
|
|
||||||
children = pages.Where(item => item.ParentId == null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
level = page.Level;
|
|
||||||
children = pages.Where(item => item.ParentId == page.PageId);
|
|
||||||
}
|
|
||||||
foreach (Page child in children)
|
|
||||||
{
|
|
||||||
child.Level = level + 1;
|
|
||||||
child.HasChildren = pages.Any(item => item.ParentId == child.PageId);
|
|
||||||
hierarchy.Add(child);
|
|
||||||
getPath(pageList, child);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pages = pages.OrderBy(item => item.Order).ToList();
|
|
||||||
getPath(pages, null);
|
|
||||||
|
|
||||||
// add any non-hierarchical items to the end of the list
|
|
||||||
foreach (Page page in pages)
|
|
||||||
{
|
|
||||||
if (hierarchy.Find(item => item.PageId == page.PageId) == null)
|
|
||||||
{
|
|
||||||
hierarchy.Add(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hierarchy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@ namespace Oqtane.Services
|
|||||||
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
|
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> LoginUserAsync(User user)
|
public async Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent)
|
||||||
{
|
{
|
||||||
return await PostJsonAsync<User>($"{Apiurl}/login", user);
|
return await PostJsonAsync<User>($"{Apiurl}/login?setcookie={setCookie}&persistent={isPersistent}", user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogoutUserAsync(User user)
|
public async Task LogoutUserAsync(User user)
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private void CloseModal()
|
private void CloseModal()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo((!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
// obtained from https://cdnjs.com/libraries
|
// obtained from https://cdnjs.com/libraries
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", Integrity = "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", CrossOrigin = "anonymous" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/css/bootstrap.min.css", Integrity = "sha512-XWTTruHZEYJsxV3W/lSXG1n3Q39YIWOstqvmFsdNEEQfHoZ6vm6E9GK2OrF6DSJSpIbRbi+Nn0WDPID9O7xB2Q==", CrossOrigin = "anonymous" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", Integrity = "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", CrossOrigin = "anonymous" }
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -2,44 +2,44 @@
|
|||||||
@inherits ModuleActionsBase
|
@inherits ModuleActionsBase
|
||||||
@attribute [OqtaneIgnore]
|
@attribute [OqtaneIgnore]
|
||||||
|
|
||||||
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions) && PageState.Action == Constants.DefaultAction)
|
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
|
||||||
{
|
{
|
||||||
<div class="app-moduleactions">
|
<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>
|
||||||
<ul class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
|
<ul class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
|
||||||
@foreach (var action in Actions.Where(item => !item.Name.Contains("Pane")))
|
@foreach (var action in Actions.Where(item => !item.Name.Contains("Pane")))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(action.Name))
|
if (string.IsNullOrEmpty(action.Name))
|
||||||
{
|
{
|
||||||
<li class="dropdown-divider"></li>
|
<li class="dropdown-divider"></li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if (Actions.Where(item => item.Name.Contains("Pane")).Any())
|
@if (Actions.Where(item => item.Name.Contains("Pane")).Any())
|
||||||
{
|
{
|
||||||
<li class="dropdown-submenu">
|
<li class="dropdown-submenu">
|
||||||
<a class="dropdown-item" onclick="return subMenu(this)">
|
<a class="dropdown-item" onclick="return subMenu(this)">
|
||||||
<span class="@Icons.AccountLogin" aria-hidden="true"></span> Move To >
|
<span class="@Icons.AccountLogin" aria-hidden="true"></span> Move To >
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
@foreach (var action in Actions.Where(item => item.Name.Contains("Pane")))
|
@foreach (var action in Actions.Where(item => item.Name.Contains("Pane")))
|
||||||
{
|
{
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -29,54 +29,55 @@ namespace Oqtane.Themes.Controls
|
|||||||
protected virtual List<ActionViewModel> GetActions()
|
protected virtual List<ActionViewModel> GetActions()
|
||||||
{
|
{
|
||||||
var actionList = new List<ActionViewModel>();
|
var actionList = new List<ActionViewModel>();
|
||||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
|
|
||||||
|
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||||
{
|
{
|
||||||
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.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
|
if (UserSecurity.ContainsRole(ModuleState.Permissions, 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) });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel {Icon=Icons.CircleCheck, Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
|
actionList.Add(new ActionViewModel { Icon = Icons.CircleCheck, Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
|
||||||
}
|
}
|
||||||
actionList.Add(new ActionViewModel {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.ServerManagerType != "")
|
||||||
{
|
{
|
||||||
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") });
|
||||||
actionList.Add(new ActionViewModel {Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
|
actionList.Add(new ActionViewModel { Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export") });
|
||||||
}
|
}
|
||||||
|
|
||||||
actionList.Add(new ActionViewModel {Name = ""});
|
actionList.Add(new ActionViewModel { Name = "" });
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex > 0)
|
if (ModuleState.PaneModuleIndex > 0)
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel {Icon = Icons.DataTransferUpload ,Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
|
actionList.Add(new ActionViewModel { Icon = Icons.DataTransferUpload, Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex > 0)
|
if (ModuleState.PaneModuleIndex > 0)
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickTop, Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
|
actionList.Add(new ActionViewModel { Icon = Icons.ArrowThickTop, Name = "Move Up", Action = async (s, m) => await MoveUp(s, m) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickBottom, Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
|
actionList.Add(new ActionViewModel { Icon = Icons.ArrowThickBottom, Name = "Move Down", Action = async (s, m) => await MoveDown(s, m) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel {Icon = Icons.DataTransferDownload, Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
|
actionList.Add(new ActionViewModel { Icon = Icons.DataTransferDownload, Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m) });
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string pane in PageState.Page.Panes)
|
foreach (string pane in PageState.Page.Panes)
|
||||||
{
|
{
|
||||||
if (pane != ModuleState.Pane)
|
if (pane != ModuleState.Pane)
|
||||||
{
|
{
|
||||||
actionList.Add(new ActionViewModel {Icon = Icons.AccountLogin, Name = pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
|
actionList.Add(new ActionViewModel { Icon = Icons.AccountLogin, Name = pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
@using System.ComponentModel
|
||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
@inherits ContainerBase
|
@inherits ContainerBase
|
||||||
@attribute [OqtaneIgnore]
|
@attribute [OqtaneIgnore]
|
||||||
|
@inject SiteState SiteState
|
||||||
|
|
||||||
<span class="app-moduletitle">
|
<span class="app-moduletitle">
|
||||||
<a id="@ModuleState.PageModuleId.ToString()">
|
<a id="@ModuleState.PageModuleId.ToString()">
|
||||||
@ -9,17 +11,39 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string title = "";
|
private string title = "";
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
|
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged;
|
||||||
{
|
}
|
||||||
title = ModuleState.ControlTitle;
|
|
||||||
}
|
protected override void OnParametersSet()
|
||||||
else
|
{
|
||||||
{
|
if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
|
||||||
title = ModuleState.Title;
|
{
|
||||||
}
|
title = ModuleState.ControlTitle;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
title = ModuleState.Title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == "ModuleTitle")
|
||||||
|
{
|
||||||
|
if (SiteState.Properties.ModuleTitle.PageModuleId == ModuleState.PageModuleId)
|
||||||
|
{
|
||||||
|
title = SiteState.Properties.ModuleTitle.Title;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged -= PropertyChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||||
{
|
{
|
||||||
if (PageState.EditMode)
|
if (PageState.EditMode)
|
||||||
{
|
{
|
||||||
@ -70,7 +70,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row d-flex">
|
<div class="row d-flex">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
|
@if (UserSecurity.ContainsRole(PageState.Page.Permissions, 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>
|
||||||
}
|
}
|
||||||
@ -218,6 +218,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code{
|
@code{
|
||||||
|
private bool _showEditMode = false;
|
||||||
private bool _deleteConfirmation = false;
|
private bool _deleteConfirmation = false;
|
||||||
private List<string> _categories = new List<string>();
|
private List<string> _categories = new List<string>();
|
||||||
private List<ModuleDefinition> _allModuleDefinitions;
|
private List<ModuleDefinition> _allModuleDefinitions;
|
||||||
@ -285,8 +286,10 @@
|
|||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
_showEditMode = false;
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||||
{
|
{
|
||||||
|
_showEditMode = true;
|
||||||
_pages?.Clear();
|
_pages?.Clear();
|
||||||
|
|
||||||
foreach (Page p in PageState.Pages)
|
foreach (Page p in PageState.Pages)
|
||||||
@ -305,6 +308,17 @@
|
|||||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
|
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
|
||||||
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions))
|
||||||
|
{
|
||||||
|
_showEditMode = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CategoryChanged(ChangeEventArgs e)
|
private void CategoryChanged(ChangeEventArgs e)
|
||||||
@ -405,165 +419,178 @@
|
|||||||
Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
||||||
Title = "";
|
Title = "";
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Message = $"<div class=\"alert alert-warning mt-2 text-center\" 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
|
else
|
||||||
{
|
{
|
||||||
Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
|
Message = $"<div class=\"alert alert-error mt-2 text-center\" role=\"alert\">{Localizer["Error.Authorize.No"]}</div>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ToggleEditMode(bool EditMode)
|
private async Task ToggleEditMode(bool EditMode)
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
if (_showEditMode)
|
||||||
{
|
{
|
||||||
if (EditMode)
|
if (EditMode)
|
||||||
{
|
{
|
||||||
PageState.EditMode = false;
|
PageState.EditMode = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
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) ? "1" : "0")));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (PageState.Page.IsPersonalizable && PageState.User != null)
|
if (PageState.Page.IsPersonalizable && PageState.User != null)
|
||||||
{
|
{
|
||||||
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) ? "1" : "0")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Navigate(string location)
|
private void Navigate(string location)
|
||||||
{
|
{
|
||||||
//HideControlPanel();
|
Module module;
|
||||||
Module module;
|
switch (location)
|
||||||
switch (location)
|
{
|
||||||
{
|
case "Admin":
|
||||||
case "Admin":
|
// get admin dashboard moduleid
|
||||||
// get admin dashboard moduleid
|
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
|
||||||
|
|
||||||
if (module != null)
|
if (module != null)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "Add":
|
case "Add":
|
||||||
case "Edit":
|
case "Edit":
|
||||||
string url = "";
|
string url = "";
|
||||||
// get page management moduleid
|
// get page management moduleid
|
||||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
|
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
|
||||||
|
|
||||||
if (module != null)
|
if (module != null)
|
||||||
{
|
{
|
||||||
switch (location)
|
switch (location)
|
||||||
{
|
{
|
||||||
case "Add":
|
case "Add":
|
||||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "cp=" + PageState.Page.PageId);
|
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "cp=" + PageState.Page.PageId);
|
||||||
break;
|
break;
|
||||||
case "Edit":
|
case "Edit":
|
||||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString() + "&cp=" + PageState.Page.PageId);
|
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString() + "&cp=" + PageState.Page.PageId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url != "")
|
if (url != "")
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(url);
|
NavigationManager.NavigateTo(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.Permissions))
|
||||||
{
|
{
|
||||||
List<PermissionString> permissions;
|
List<PermissionString> permissions;
|
||||||
|
|
||||||
// publish/unpublish page
|
// publish/unpublish page
|
||||||
var page = PageState.Page;
|
var page = PageState.Page;
|
||||||
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
|
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
|
||||||
foreach (var permissionstring in permissions)
|
foreach (var permissionstring in permissions)
|
||||||
{
|
{
|
||||||
if (permissionstring.PermissionName == PermissionNames.View)
|
if (permissionstring.PermissionName == PermissionNames.View)
|
||||||
{
|
{
|
||||||
List<string> ids = permissionstring.Permissions.Split(';').ToList();
|
List<string> ids = permissionstring.Permissions.Split(';').ToList();
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case "publish":
|
case "publish":
|
||||||
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
|
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
|
||||||
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
|
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
|
||||||
break;
|
break;
|
||||||
case "unpublish":
|
case "unpublish":
|
||||||
ids.Remove(RoleNames.Everyone);
|
ids.Remove(RoleNames.Everyone);
|
||||||
ids.Remove(RoleNames.Registered);
|
ids.Remove(RoleNames.Registered);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
permissionstring.Permissions = string.Join(";", ids.ToArray());
|
permissionstring.Permissions = string.Join(";", ids.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
||||||
await PageService.UpdatePageAsync(page);
|
await PageService.UpdatePageAsync(page);
|
||||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfirmDelete()
|
private void ConfirmDelete()
|
||||||
{
|
{
|
||||||
_deleteConfirmation = !_deleteConfirmation;
|
_deleteConfirmation = !_deleteConfirmation;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeletePage()
|
private async Task DeletePage()
|
||||||
{
|
{
|
||||||
ConfirmDelete();
|
ConfirmDelete();
|
||||||
|
|
||||||
var page = PageState.Page;
|
var page = PageState.Page;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (page.UserId == null)
|
if (page.UserId == null)
|
||||||
{
|
{
|
||||||
page.IsDeleted = true;
|
page.IsDeleted = true;
|
||||||
await PageService.UpdatePageAsync(page);
|
await PageService.UpdatePageAsync(page);
|
||||||
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||||
NavigationManager.NavigateTo(NavigateUrl(""));
|
NavigationManager.NavigateTo(NavigateUrl(""));
|
||||||
}
|
}
|
||||||
else // personalized page
|
else // personalized page
|
||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message);
|
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string settingCategory = "CP-category";
|
private string settingCategory = "CP-category";
|
||||||
private string settingPane = "CP-pane";
|
private string settingPane = "CP-pane";
|
||||||
private string _pane = "";
|
private string _pane = "";
|
||||||
|
|
||||||
private async Task LoadSettingsAsync()
|
private async Task LoadSettingsAsync()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
_category = SettingService.GetSetting(settings, settingCategory, "Common");
|
_category = SettingService.GetSetting(settings, settingCategory, "Common");
|
||||||
var pane = SettingService.GetSetting(settings, settingPane, "");
|
var pane = SettingService.GetSetting(settings, settingPane, "");
|
||||||
_pane = PageState.Page.Panes.Contains(pane) ? pane : PaneNames.Admin;
|
if (PageState.Page.Panes.Contains(pane))
|
||||||
|
{
|
||||||
|
_pane = pane;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PageState.Page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||||
|
{
|
||||||
|
_pane = PaneNames.Default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pane = PaneNames.Admin;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateSettingsAsync()
|
private async Task UpdateSettingsAsync()
|
||||||
|
@ -24,13 +24,9 @@
|
|||||||
@code{
|
@code{
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private IEnumerable<Culture> _supportedCultures;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
var languages = PageState.Languages;
|
||||||
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
|
|
||||||
|
|
||||||
languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName });
|
|
||||||
|
|
||||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ 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.Providers;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
@ -38,12 +39,23 @@ namespace Oqtane.Themes.Controls
|
|||||||
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
|
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
|
||||||
{
|
{
|
||||||
url = PageState.Alias.Path;
|
url = PageState.Alias.Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// post to the Logout page to complete the logout process
|
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
{
|
||||||
var interop = new Interop(jsRuntime);
|
// hybrid apps utilize an interactive logout
|
||||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
await UserService.LogoutUserAsync(PageState.User);
|
||||||
|
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||||
|
authstateprovider.NotifyAuthenticationChanged();
|
||||||
|
NavigationManager.NavigateTo(url, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// post to the Logout page to complete the logout process
|
||||||
|
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||||
|
var interop = new Interop(jsRuntime);
|
||||||
|
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,11 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<Pane Name="@PaneNames.Admin" />
|
<Pane Name="@PaneNames.Default" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Pane Name="Top Full Width" />
|
<Pane Name="Top Full Width" />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@ -102,20 +102,20 @@
|
|||||||
{
|
{
|
||||||
<Pane Name="Footer" />
|
<Pane Name="Footer" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
public override string Name => "Default Theme";
|
public override string Name => "Default Theme";
|
||||||
|
|
||||||
public override string Panes => PaneNames.Admin + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
// obtained from https://cdnjs.com/libraries
|
// obtained from https://cdnjs.com/libraries
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.1.3/cyborg/bootstrap.min.css", Integrity = "sha512-/in5IWTUhb7wOUd6iHotlyrLrZ7+2utJJR8ySzSxeeOMJ9fanjCr4fmyWzDW/ziw56shUNTVClBMWZaA677VhA==", CrossOrigin = "anonymous" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.0/cyborg/bootstrap.min.css", Integrity = "sha512-d6pZJl/sNcj0GFkp4kTjXtPE14deuUsOqFQtxkj0KyBJQl+4e0qsEyuIDcNqrYuGoauAW3sWyDCQp49mhF4Syw==", CrossOrigin = "anonymous" },
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", Integrity = "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", CrossOrigin = "anonymous" }
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool _login = true;
|
private bool _login = true;
|
||||||
|
@ -1,20 +1,26 @@
|
|||||||
|
@using System.ComponentModel
|
||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
|
@inject SiteState SiteState
|
||||||
|
|
||||||
<CascadingValue Value="@ModuleState">
|
@if (_visible)
|
||||||
@if (_useadminborder)
|
{
|
||||||
{
|
<CascadingValue Value="@ModuleState">
|
||||||
<div class="app-pane-admin-border">
|
@if (_useadminborder)
|
||||||
@DynamicComponent
|
{
|
||||||
</div>
|
<div class="app-pane-admin-border">
|
||||||
}
|
@DynamicComponent
|
||||||
else
|
</div>
|
||||||
{
|
}
|
||||||
@DynamicComponent
|
else
|
||||||
}
|
{
|
||||||
</CascadingValue>
|
@DynamicComponent
|
||||||
|
}
|
||||||
|
</CascadingValue>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _useadminborder = false;
|
private bool _visible = true;
|
||||||
|
private bool _useadminborder = false;
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected PageState PageState { get; set; }
|
protected PageState PageState { get; set; }
|
||||||
@ -24,7 +30,12 @@
|
|||||||
|
|
||||||
RenderFragment DynamicComponent { get; set; }
|
RenderFragment DynamicComponent { get; set; }
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
string container = ModuleState.ContainerType;
|
string container = ModuleState.ContainerType;
|
||||||
if (PageState.ModuleId != -1 && ModuleState.UseAdminContainer)
|
if (PageState.ModuleId != -1 && ModuleState.UseAdminContainer)
|
||||||
@ -53,4 +64,21 @@
|
|||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == "ModuleVisibility")
|
||||||
|
{
|
||||||
|
if (SiteState.Properties.ModuleVisibility.PageModuleId == ModuleState.PageModuleId)
|
||||||
|
{
|
||||||
|
_visible = SiteState.Properties.ModuleVisibility.Visible;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged -= PropertyChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Oqtane.UI
|
namespace Oqtane.UI
|
||||||
{
|
{
|
||||||
@ -293,5 +296,91 @@ namespace Oqtane.UI
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValueTask<int> GetCaretPosition(string id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _jsRuntime.InvokeAsync<int>(
|
||||||
|
"Oqtane.Interop.getCaretPosition",
|
||||||
|
id);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new ValueTask<int>(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SetIndexedDBItem(string key, object value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_jsRuntime.InvokeVoidAsync(
|
||||||
|
"Oqtane.Interop.manageIndexedDBItems",
|
||||||
|
"put", key, value);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> GetIndexedDBItem<T>(string key)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _jsRuntime.InvokeAsync<T>(
|
||||||
|
"Oqtane.Interop.manageIndexedDBItems",
|
||||||
|
"get", key, null);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<string>> GetIndexedDBKeys()
|
||||||
|
{
|
||||||
|
return await GetIndexedDBKeys("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<string>> GetIndexedDBKeys(string contains)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var items = await _jsRuntime.InvokeAsync<JsonDocument>(
|
||||||
|
"Oqtane.Interop.manageIndexedDBItems",
|
||||||
|
"getallkeys", null, null);
|
||||||
|
if (!string.IsNullOrEmpty(contains))
|
||||||
|
{
|
||||||
|
return items.Deserialize<List<string>>()
|
||||||
|
.Where(item => item.Contains(contains)).ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return items.Deserialize<List<string>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task RemoveIndexedDBItem(string key)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_jsRuntime.InvokeVoidAsync(
|
||||||
|
"Oqtane.Interop.manageIndexedDBItems",
|
||||||
|
"delete", key, null);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
|
|
||||||
namespace Oqtane.UI
|
namespace Oqtane.UI
|
||||||
@ -8,10 +10,8 @@ namespace Oqtane.UI
|
|||||||
{
|
{
|
||||||
public Alias Alias { get; set; }
|
public Alias Alias { get; set; }
|
||||||
public Site Site { get; set; }
|
public Site Site { get; set; }
|
||||||
public List<Page> Pages { get; set; }
|
|
||||||
public Page Page { get; set; }
|
public Page Page { get; set; }
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
public List<Module> Modules { get; set; }
|
|
||||||
public Uri Uri { get; set; }
|
public Uri Uri { get; set; }
|
||||||
public Dictionary<string, string> QueryString { get; set; }
|
public Dictionary<string, string> QueryString { get; set; }
|
||||||
public string UrlParameters { get; set; }
|
public string UrlParameters { get; set; }
|
||||||
@ -19,8 +19,22 @@ namespace Oqtane.UI
|
|||||||
public string Action { get; set; }
|
public string Action { get; set; }
|
||||||
public bool EditMode { get; set; }
|
public bool EditMode { get; set; }
|
||||||
public DateTime LastSyncDate { get; set; }
|
public DateTime LastSyncDate { get; set; }
|
||||||
public Oqtane.Shared.Runtime Runtime { get; set; }
|
public Shared.Runtime Runtime { get; set; }
|
||||||
public int VisitorId { get; set; }
|
public int VisitorId { get; set; }
|
||||||
public string RemoteIPAddress { get; set; }
|
public string RemoteIPAddress { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
public List<Page> Pages
|
||||||
|
{
|
||||||
|
get { return Site.Pages.Where(item => !item.IsDeleted).ToList(); }
|
||||||
|
}
|
||||||
|
public List<Module> Modules
|
||||||
|
{
|
||||||
|
get { return Site.Modules.Where(item => !item.IsDeleted).ToList(); }
|
||||||
|
}
|
||||||
|
public List<Language> Languages
|
||||||
|
{
|
||||||
|
get { return Site.Languages; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,46 +17,57 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool _useadminborder = false;
|
private bool _useadminborder = false;
|
||||||
private string _panetitle = "";
|
private string _panetitle = "";
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected PageState PageState { get; set; }
|
protected PageState PageState { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
RenderFragment DynamicComponent { get; set; }
|
RenderFragment DynamicComponent { get; set; }
|
||||||
|
|
||||||
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.Permissions) && 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>";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_useadminborder = false;
|
_useadminborder = false;
|
||||||
_panetitle = "";
|
_panetitle = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicComponent = builder =>
|
DynamicComponent = builder =>
|
||||||
{
|
{
|
||||||
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
|
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
|
||||||
{
|
{
|
||||||
if (Name.ToLower() == PaneNames.Admin.ToLower())
|
// action route needs to inject module control into specific pane
|
||||||
{
|
string pane = "";
|
||||||
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
|
if (PageState.Page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||||
if (module != null)
|
{
|
||||||
{
|
pane = PaneNames.Default;
|
||||||
var moduleType = Type.GetType(module.ModuleType);
|
}
|
||||||
if (moduleType != null)
|
else
|
||||||
{
|
{
|
||||||
bool authorized = false;
|
pane = PaneNames.Admin;
|
||||||
if (Constants.DefaultModuleActions.Contains(PageState.Action))
|
|
||||||
{
|
}
|
||||||
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions);
|
if (Name.ToLower() == pane.ToLower())
|
||||||
|
{
|
||||||
|
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
|
||||||
|
if (module != null)
|
||||||
|
{
|
||||||
|
var moduleType = Type.GetType(module.ModuleType);
|
||||||
|
if (moduleType != null)
|
||||||
|
{
|
||||||
|
bool authorized = false;
|
||||||
|
if (Constants.DefaultModuleActions.Contains(PageState.Action))
|
||||||
|
{
|
||||||
|
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,6 @@ namespace Oqtane.UI
|
|||||||
public enum Refresh
|
public enum Refresh
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Page,
|
|
||||||
Site,
|
Site,
|
||||||
Application
|
Application
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@using System.Diagnostics.CodeAnalysis
|
@using System.Diagnostics.CodeAnalysis
|
||||||
|
@using System.Net
|
||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||||
@inject SiteState SiteState
|
@inject SiteState SiteState
|
||||||
@ -8,18 +9,23 @@
|
|||||||
@inject ISiteService SiteService
|
@inject ISiteService SiteService
|
||||||
@inject IPageService PageService
|
@inject IPageService PageService
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
@inject IModuleService ModuleService
|
|
||||||
@inject IUrlMappingService UrlMappingService
|
@inject IUrlMappingService UrlMappingService
|
||||||
@inject ILogService LogService
|
@inject ILogService LogService
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
@implements IHandleAfterRender
|
@implements IHandleAfterRender
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(_error))
|
||||||
|
{
|
||||||
|
<ModuleMessage Message="@_error" Type="@MessageType.Warning" />
|
||||||
|
}
|
||||||
|
|
||||||
@DynamicComponent
|
@DynamicComponent
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _absoluteUri;
|
private string _absoluteUri;
|
||||||
private bool _navigationInterceptionEnabled;
|
private bool _navigationInterceptionEnabled;
|
||||||
private PageState _pagestate;
|
private PageState _pagestate;
|
||||||
|
private string _error = "";
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Runtime { get; set; }
|
public string Runtime { get; set; }
|
||||||
@ -70,19 +76,23 @@
|
|||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
{
|
{
|
||||||
Site site;
|
Site site;
|
||||||
List<Page> pages;
|
|
||||||
Page page;
|
Page page;
|
||||||
User user = null;
|
User user = null;
|
||||||
List<Module> modules;
|
|
||||||
var editmode = false;
|
var editmode = false;
|
||||||
var refresh = UI.Refresh.None;
|
var refresh = UI.Refresh.None;
|
||||||
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
||||||
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
||||||
|
_error = "";
|
||||||
|
|
||||||
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
||||||
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
||||||
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
||||||
var querystring = ParseQueryString(route.Query);
|
var querystring = ParseQueryString(route.Query);
|
||||||
|
var returnurl = "";
|
||||||
|
if (querystring.ContainsKey("returnurl"))
|
||||||
|
{
|
||||||
|
returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
|
||||||
|
}
|
||||||
|
|
||||||
// reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
|
// reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
|
||||||
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
||||||
@ -102,7 +112,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the refresh parameter is used to refresh the client-side PageState
|
// the refresh parameter is used to refresh the client-side PageState
|
||||||
if (querystring.ContainsKey("refresh"))
|
if (querystring.ContainsKey("refresh"))
|
||||||
{
|
{
|
||||||
@ -115,6 +125,24 @@
|
|||||||
lastsyncdate = PageState.LastSyncDate;
|
lastsyncdate = PageState.LastSyncDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get user
|
||||||
|
if (PageState == null || refresh == UI.Refresh.Site || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||||
|
{
|
||||||
|
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||||
|
if (authState.User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = PageState.User;
|
||||||
|
}
|
||||||
|
|
||||||
// process any sync events
|
// process any sync events
|
||||||
var sync = await SyncService.GetSyncAsync(lastsyncdate);
|
var sync = await SyncService.GetSyncAsync(lastsyncdate);
|
||||||
lastsyncdate = sync.SyncDate;
|
lastsyncdate = sync.SyncDate;
|
||||||
@ -126,13 +154,19 @@
|
|||||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// when a site has changed the state needs to be refreshed
|
||||||
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||||
{
|
{
|
||||||
refresh = UI.Refresh.Site;
|
refresh = UI.Refresh.Site;
|
||||||
}
|
}
|
||||||
|
// when a user changed the site needs to be refreshed as the list of pages/modules may have changed
|
||||||
|
if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
|
||||||
|
{
|
||||||
|
refresh = UI.Refresh.Site;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refresh == UI.Refresh.Site || PageState == null || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
if (PageState == null || refresh == UI.Refresh.Site || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||||
{
|
{
|
||||||
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
|
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
|
||||||
refresh = UI.Refresh.Site;
|
refresh = UI.Refresh.Site;
|
||||||
@ -144,96 +178,47 @@
|
|||||||
|
|
||||||
if (site != null)
|
if (site != null)
|
||||||
{
|
{
|
||||||
if (PageState == null || refresh == UI.Refresh.Site)
|
if (PageState == null || refresh == UI.Refresh.Site || PageState.Page.Path != route.PagePath)
|
||||||
{
|
{
|
||||||
// get user
|
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
||||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
editmode = false;
|
||||||
if (authState.User.Identity.IsAuthenticated)
|
|
||||||
{
|
|
||||||
user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId);
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user = PageState.User;
|
|
||||||
}
|
|
||||||
|
|
||||||
// process any sync events for user
|
|
||||||
if (refresh != UI.Refresh.Site && user != null && sync.SyncEvents.Any())
|
|
||||||
{
|
|
||||||
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
|
|
||||||
{
|
|
||||||
refresh = UI.Refresh.Site;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PageState == null || refresh == UI.Refresh.Site)
|
|
||||||
{
|
|
||||||
pages = await PageService.GetPagesAsync(site.SiteId);
|
|
||||||
pages = pages.Where(item => !item.IsDeleted).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pages = PageState.Pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PageState == null || refresh == UI.Refresh.Site)
|
|
||||||
{
|
|
||||||
page = pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page = PageState.Page;
|
page = PageState.Page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the page if the path has changed
|
if (page == null && route.PagePath == "") // naked path refers to site home page
|
||||||
if (page == null || page.Path != route.PagePath)
|
|
||||||
{
|
{
|
||||||
page = pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
if (site.HomePageId != null)
|
||||||
// if the home page path does not exist then use the first page in the collection (a future enhancement would allow the admin to specify the home page)
|
|
||||||
if (page == null && route.PagePath == "")
|
|
||||||
{
|
{
|
||||||
page = pages.FirstOrDefault();
|
page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId);
|
||||||
|
}
|
||||||
|
if (page == null)
|
||||||
|
{
|
||||||
|
// fallback to use the first page in the collection
|
||||||
|
page = site.Pages.FirstOrDefault();
|
||||||
}
|
}
|
||||||
editmode = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page != null)
|
if (page != null)
|
||||||
{
|
{
|
||||||
if (PageState == null)
|
|
||||||
{
|
|
||||||
editmode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.Permissions))
|
||||||
{
|
{
|
||||||
|
// load additional metadata for current page
|
||||||
page = await ProcessPage(page, site, user);
|
page = await ProcessPage(page, site, user);
|
||||||
|
|
||||||
if (PageState == null || refresh == UI.Refresh.Site)
|
// load additional metadata for modules
|
||||||
{
|
(page, site.Modules) = ProcessModules(page, site.Modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
|
||||||
modules = await ModuleService.GetModulesAsync(site.SiteId);
|
|
||||||
modules = modules.Where(item => !item.IsDeleted).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
modules = PageState.Modules;
|
|
||||||
}
|
|
||||||
|
|
||||||
(page, modules) = ProcessModules(page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
|
|
||||||
|
|
||||||
|
// populate page state (which acts as a client-side cache for subsequent requests)
|
||||||
_pagestate = new PageState
|
_pagestate = new PageState
|
||||||
{
|
{
|
||||||
Alias = SiteState.Alias,
|
Alias = SiteState.Alias,
|
||||||
Site = site,
|
Site = site,
|
||||||
Pages = pages,
|
|
||||||
Page = page,
|
Page = page,
|
||||||
User = user,
|
User = user,
|
||||||
Modules = modules,
|
|
||||||
Uri = new Uri(_absoluteUri, UriKind.Absolute),
|
Uri = new Uri(_absoluteUri, UriKind.Absolute),
|
||||||
QueryString = querystring,
|
QueryString = querystring,
|
||||||
UrlParameters = route.UrlParameters,
|
UrlParameters = route.UrlParameters,
|
||||||
@ -243,7 +228,8 @@
|
|||||||
LastSyncDate = lastsyncdate,
|
LastSyncDate = lastsyncdate,
|
||||||
Runtime = runtime,
|
Runtime = runtime,
|
||||||
VisitorId = VisitorId,
|
VisitorId = VisitorId,
|
||||||
RemoteIPAddress = SiteState.RemoteIPAddress
|
RemoteIPAddress = SiteState.RemoteIPAddress,
|
||||||
|
ReturnUrl = returnurl
|
||||||
};
|
};
|
||||||
|
|
||||||
OnStateChange?.Invoke(_pagestate);
|
OnStateChange?.Invoke(_pagestate);
|
||||||
@ -350,7 +336,7 @@
|
|||||||
page.Panes = new List<string>();
|
page.Panes = new List<string>();
|
||||||
page.Resources = new List<Resource>();
|
page.Resources = new List<Resource>();
|
||||||
|
|
||||||
string panes = PaneNames.Admin;
|
string panes = "";
|
||||||
Type themetype = Type.GetType(page.ThemeType);
|
Type themetype = Type.GetType(page.ThemeType);
|
||||||
if (themetype == null)
|
if (themetype == null)
|
||||||
{
|
{
|
||||||
@ -370,7 +356,19 @@
|
|||||||
page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page);
|
page.Resources = ManagePageResources(page.Resources, themeobject.Resources, ResourceLevel.Page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
if (!string.IsNullOrEmpty(panes))
|
||||||
|
{
|
||||||
|
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
if (!page.Panes.Contains(PaneNames.Default) && !page.Panes.Contains(PaneNames.Admin))
|
||||||
|
{
|
||||||
|
_error = "The Current Theme Does Not Contain A Default Or Admin Pane";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
page.Panes.Add(PaneNames.Admin);
|
||||||
|
_error = "The Current Theme Does Not Contain Any Panes";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -462,10 +460,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure module's pane exists in current page and if not, assign it to the Admin pane
|
// validate that module's pane exists in current page
|
||||||
if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
if (page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||||
{
|
{
|
||||||
module.Pane = PaneNames.Admin;
|
// fallback to default pane if it exists
|
||||||
|
if (page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||||
|
{
|
||||||
|
module.Pane = PaneNames.Default;
|
||||||
|
}
|
||||||
|
else // otherwise admin pane (legacy)
|
||||||
|
{
|
||||||
|
module.Pane = PaneNames.Admin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate module position within pane
|
// calculate module position within pane
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||||
{
|
{
|
||||||
var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
|
var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
|
||||||
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + "/" + resource.Url;
|
||||||
|
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
|
||||||
}
|
}
|
||||||
if (links.Any())
|
if (links.Any())
|
||||||
{
|
{
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.3</Version>
|
<Version>3.2.0</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
<Description>Modular Application Framework for Blazor</Description>
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
<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.1.3</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.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>
|
||||||
|
@ -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.1.3</version>
|
<version>3.2.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.1.3</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.3</Version>
|
<Version>3.2.0</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
<Description>Modular Application Framework for Blazor</Description>
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
<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.1.3</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.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>
|
||||||
|
@ -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.1.3</version>
|
<version>3.2.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.1.3</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.3</Version>
|
<Version>3.2.0</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
<Description>Modular Application Framework for Blazor</Description>
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
<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.1.3</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.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>
|
||||||
|
@ -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.1.3</version>
|
<version>3.2.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.1.3</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.1.3</Version>
|
<Version>3.2.0</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
<Description>Modular Application Framework for Blazor</Description>
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
<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.1.3</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.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>
|
||||||
|
@ -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.1.3</version>
|
<version>3.2.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.1.3</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
26
Oqtane.Maui.sln
Normal file
26
Oqtane.Maui.sln
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31611.283
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Maui", "Oqtane.Maui\Oqtane.Maui.csproj", "{5EE64148-2152-4908-A3E7-658EB1D87754}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{5EE64148-2152-4908-A3E7-658EB1D87754}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5EE64148-2152-4908-A3E7-658EB1D87754}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5EE64148-2152-4908-A3E7-658EB1D87754}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||||
|
{5EE64148-2152-4908-A3E7-658EB1D87754}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5EE64148-2152-4908-A3E7-658EB1D87754}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5EE64148-2152-4908-A3E7-658EB1D87754}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
26
Oqtane.Maui/App.xaml
Normal file
26
Oqtane.Maui/App.xaml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:local="clr-namespace:Oqtane.Maui"
|
||||||
|
x:Class="Oqtane.Maui.App">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
|
||||||
|
<Color x:Key="PageBackgroundColor">#512bdf</Color>
|
||||||
|
<Color x:Key="PrimaryTextColor">White</Color>
|
||||||
|
|
||||||
|
<Style TargetType="Label">
|
||||||
|
<Setter Property="TextColor" Value="{DynamicResource PrimaryTextColor}" />
|
||||||
|
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Button">
|
||||||
|
<Setter Property="TextColor" Value="{DynamicResource PrimaryTextColor}" />
|
||||||
|
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||||
|
<Setter Property="BackgroundColor" Value="#2b0b98" />
|
||||||
|
<Setter Property="Padding" Value="14,10" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
11
Oqtane.Maui/App.xaml.cs
Normal file
11
Oqtane.Maui/App.xaml.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Oqtane.Maui;
|
||||||
|
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public App()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
MainPage = new MainPage();
|
||||||
|
}
|
||||||
|
}
|
18
Oqtane.Maui/Main.razor
Normal file
18
Oqtane.Maui/Main.razor
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<DynamicComponent Type="@ComponentType" Parameters="@Parameters"></DynamicComponent>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
Type ComponentType = Type.GetType("Oqtane.App, Oqtane.Client");
|
||||||
|
private IDictionary<string, object> Parameters { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Parameters = new Dictionary<string, object>();
|
||||||
|
Parameters.Add(new KeyValuePair<string, object>("AntiForgeryToken", ""));
|
||||||
|
Parameters.Add(new KeyValuePair<string, object>("Runtime", "Hybrid"));
|
||||||
|
Parameters.Add(new KeyValuePair<string, object>("RenderMode", "Hybrid"));
|
||||||
|
Parameters.Add(new KeyValuePair<string, object>("VisitorId", -1));
|
||||||
|
Parameters.Add(new KeyValuePair<string, object>("RemoteIPAddress", ""));
|
||||||
|
Parameters.Add(new KeyValuePair<string, object>("AuthorizationToken", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
Oqtane.Maui/MainPage.xaml
Normal file
14
Oqtane.Maui/MainPage.xaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:local="clr-namespace:Oqtane.Maui"
|
||||||
|
x:Class="Oqtane.Maui.MainPage"
|
||||||
|
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
||||||
|
|
||||||
|
<BlazorWebView HostPage="wwwroot/index.html">
|
||||||
|
<BlazorWebView.RootComponents>
|
||||||
|
<RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
|
||||||
|
</BlazorWebView.RootComponents>
|
||||||
|
</BlazorWebView>
|
||||||
|
|
||||||
|
</ContentPage>
|
9
Oqtane.Maui/MainPage.xaml.cs
Normal file
9
Oqtane.Maui/MainPage.xaml.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Oqtane.Maui;
|
||||||
|
|
||||||
|
public partial class MainPage : ContentPage
|
||||||
|
{
|
||||||
|
public MainPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
235
Oqtane.Maui/MauiProgram.cs
Normal file
235
Oqtane.Maui/MauiProgram.cs
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
using System.IO.Compression;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Oqtane.Modules;
|
||||||
|
using Oqtane.Services;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Oqtane.Maui;
|
||||||
|
|
||||||
|
public static class MauiProgram
|
||||||
|
{
|
||||||
|
// the API service url
|
||||||
|
static string apiurl = "http://localhost:44357";
|
||||||
|
|
||||||
|
public static MauiApp CreateMauiApp()
|
||||||
|
{
|
||||||
|
var builder = MauiApp.CreateBuilder();
|
||||||
|
builder
|
||||||
|
.UseMauiApp<App>()
|
||||||
|
.ConfigureFonts(fonts =>
|
||||||
|
{
|
||||||
|
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddMauiBlazorWebView();
|
||||||
|
#if DEBUG
|
||||||
|
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var httpClient = new HttpClient { BaseAddress = new Uri(apiurl) };
|
||||||
|
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Shared.Constants.MauiUserAgent);
|
||||||
|
builder.Services.AddSingleton(httpClient);
|
||||||
|
builder.Services.AddHttpClient(); // IHttpClientFactory for calling remote services via RemoteServiceBase
|
||||||
|
|
||||||
|
// dynamically load client assemblies
|
||||||
|
LoadClientAssemblies(httpClient);
|
||||||
|
|
||||||
|
// register localization services
|
||||||
|
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
|
// register auth services
|
||||||
|
builder.Services.AddOqtaneAuthorization();
|
||||||
|
|
||||||
|
// register scoped core services
|
||||||
|
builder.Services.AddOqtaneScopedServices();
|
||||||
|
|
||||||
|
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
// dynamically register module services
|
||||||
|
RegisterModuleServices(assembly, builder.Services);
|
||||||
|
|
||||||
|
// register client startup services
|
||||||
|
RegisterClientStartups(assembly, builder.Services);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadClientAssemblies(HttpClient http)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// ensure local assembly folder exists
|
||||||
|
string folder = Path.Combine(FileSystem.Current.AppDataDirectory, "oqtane");
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dlls = new Dictionary<string, byte[]>();
|
||||||
|
var pdbs = new Dictionary<string, byte[]>();
|
||||||
|
var filter = new List<string>();
|
||||||
|
|
||||||
|
var files = new List<string>();
|
||||||
|
foreach (var file in Directory.EnumerateFiles(folder, "*.dll", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
files.Add(file.Substring(folder.Length + 1).Replace("\\", "/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.Count() != 0)
|
||||||
|
{
|
||||||
|
// get list of assemblies from server
|
||||||
|
var json = Task.Run(() => http.GetStringAsync("/api/Installation/list")).GetAwaiter().GetResult();
|
||||||
|
var assemblies = JsonSerializer.Deserialize<List<string>>(json);
|
||||||
|
|
||||||
|
// determine which assemblies need to be downloaded
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
filter.Add(assembly);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check if newer version available
|
||||||
|
if (GetFileDate(assembly) > GetFileDate(file))
|
||||||
|
{
|
||||||
|
filter.Add(assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get assemblies already downloaded
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (assemblies.Contains(file) && !filter.Contains(file))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dlls.Add(file, File.ReadAllBytes(Path.Combine(folder, file)));
|
||||||
|
var pdb = file.Replace(".dll", ".pdb");
|
||||||
|
if (File.Exists(Path.Combine(folder, pdb)))
|
||||||
|
{
|
||||||
|
pdbs.Add(pdb, File.ReadAllBytes(Path.Combine(folder, pdb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // file is deprecated
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(Path.Combine(folder, file));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filter.Add("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.Count != 0)
|
||||||
|
{
|
||||||
|
// get assemblies from server
|
||||||
|
var zip = Task.Run(() => http.GetByteArrayAsync("/api/Installation/load?list=" + string.Join(",", filter))).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
// asemblies and debug symbols are packaged in a zip file
|
||||||
|
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||||
|
{
|
||||||
|
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||||
|
{
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
entry.Open().CopyTo(memoryStream);
|
||||||
|
byte[] file = memoryStream.ToArray();
|
||||||
|
|
||||||
|
// save assembly to local folder
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int subfolder = entry.FullName.IndexOf('/');
|
||||||
|
if (subfolder != -1 && !Directory.Exists(Path.Combine(folder, entry.FullName.Substring(0, subfolder))))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(folder, entry.FullName.Substring(0, subfolder)));
|
||||||
|
}
|
||||||
|
using var stream = File.Create(Path.Combine(folder, entry.FullName));
|
||||||
|
stream.Write(file, 0, file.Length);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Path.GetExtension(entry.FullName) == ".dll")
|
||||||
|
{
|
||||||
|
dlls.Add(entry.FullName, file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pdbs.Add(entry.FullName, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load assemblies into app domain
|
||||||
|
foreach (var item in dlls)
|
||||||
|
{
|
||||||
|
if (pdbs.ContainsKey(item.Key.Replace(".dll", ".pdb")))
|
||||||
|
{
|
||||||
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key.Replace(".dll", ".pdb")]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Oqtane Error: Loading Client Assemblies {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTime GetFileDate(string filepath)
|
||||||
|
{
|
||||||
|
var segments = filepath.Split('.');
|
||||||
|
return DateTime.ParseExact(segments[segments.Length - 2], "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
|
||||||
|
{
|
||||||
|
// dynamically register module scoped services
|
||||||
|
var implementationTypes = assembly.GetInterfaces<IService>();
|
||||||
|
foreach (var implementationType in implementationTypes)
|
||||||
|
{
|
||||||
|
if (implementationType.AssemblyQualifiedName != null)
|
||||||
|
{
|
||||||
|
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
|
||||||
|
services.AddScoped(serviceType ?? implementationType, implementationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterClientStartups(Assembly assembly, IServiceCollection services)
|
||||||
|
{
|
||||||
|
var startUps = assembly.GetInstances<IClientStartup>();
|
||||||
|
foreach (var startup in startUps)
|
||||||
|
{
|
||||||
|
startup.ConfigureServices(services);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
Oqtane.Maui/Oqtane.Maui.csproj
Normal file
86
Oqtane.Maui/Oqtane.Maui.csproj
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net6.0-android;net6.0-ios;net6.0-maccatalyst</TargetFrameworks>
|
||||||
|
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net6.0-windows10.0.19041.0</TargetFrameworks>
|
||||||
|
<!-- 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> -->
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<Version>3.2.0</Version>
|
||||||
|
<Product>Oqtane</Product>
|
||||||
|
<Authors>Shaun Walker</Authors>
|
||||||
|
<Company>.NET Foundation</Company>
|
||||||
|
<Description>Modular Application Framework for Blazor and MAUI</Description>
|
||||||
|
<Copyright>.NET Foundation</Copyright>
|
||||||
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
||||||
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
|
<RepositoryType>Git</RepositoryType>
|
||||||
|
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||||
|
<UseMaui>true</UseMaui>
|
||||||
|
<SingleProject>true</SingleProject>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<EnableDefaultCssItems>false</EnableDefaultCssItems>
|
||||||
|
|
||||||
|
<!-- Display name -->
|
||||||
|
<ApplicationTitle>Oqtane.Maui</ApplicationTitle>
|
||||||
|
|
||||||
|
<!-- App Identifier -->
|
||||||
|
<ApplicationId>com.oqtane.maui</ApplicationId>
|
||||||
|
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||||
|
|
||||||
|
<!-- Versions -->
|
||||||
|
<ApplicationDisplayVersion>3.2.0</ApplicationDisplayVersion>
|
||||||
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">14.0</SupportedOSPlatformVersion>
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
||||||
|
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
||||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- App Icon -->
|
||||||
|
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
|
||||||
|
|
||||||
|
<!-- Splash Screen -->
|
||||||
|
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
|
||||||
|
|
||||||
|
<!-- Images -->
|
||||||
|
<MauiImage Include="Resources\Images\*" />
|
||||||
|
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
|
||||||
|
|
||||||
|
<!-- Custom Fonts -->
|
||||||
|
<MauiFont Include="Resources\Fonts\*" />
|
||||||
|
|
||||||
|
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
|
||||||
|
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Platforms\Android\Resources\xml\network_security_config.xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
|
||||||
|
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Oqtane.Client">
|
||||||
|
<HintPath>..\Oqtane.Server\bin\Debug\net6.0\Oqtane.Client.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Oqtane.Shared">
|
||||||
|
<HintPath>..\Oqtane.Server\bin\Debug\net6.0\Oqtane.Shared.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
6
Oqtane.Maui/Platforms/Android/AndroidManifest.xml
Normal file
6
Oqtane.Maui/Platforms/Android/AndroidManifest.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config"></application>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
</manifest>
|
10
Oqtane.Maui/Platforms/Android/MainActivity.cs
Normal file
10
Oqtane.Maui/Platforms/Android/MainActivity.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using Android.App;
|
||||||
|
using Android.Content.PM;
|
||||||
|
using Android.OS;
|
||||||
|
|
||||||
|
namespace Oqtane.Maui;
|
||||||
|
|
||||||
|
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
|
||||||
|
public class MainActivity : MauiAppCompatActivity
|
||||||
|
{
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user