2
.gitignore
vendored
2
.gitignore
vendored
@ -10,6 +10,8 @@ msbuild.binlog
|
||||
*.zip
|
||||
|
||||
*.idea
|
||||
_ReSharper.Caches
|
||||
.DS_Store
|
||||
|
||||
Oqtane.Server/appsettings.json
|
||||
Oqtane.Server/Data
|
||||
|
@ -16,7 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return services;
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<SiteState>();
|
||||
services.AddScoped<IInstallationService, InstallationService>();
|
||||
|
@ -186,8 +186,8 @@
|
||||
if (firstRender)
|
||||
{
|
||||
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.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head");
|
||||
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.2.0/js/bootstrap.bundle.min.js", "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", "anonymous", "", "head");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
|
||||
{
|
||||
string url = NavigateUrl(p.Path);
|
||||
<div class="col-md-2 mx-auto text-center">
|
||||
<NavLink class="nav-link" href="@url" Match="NavLinkMatch.All">
|
||||
<div class="col-md-2 mx-auto text-center mb-3">
|
||||
<NavLink class="nav-link text-primary" href="@url" Match="NavLinkMatch.All">
|
||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@SharedLocalizer[p.Name]
|
||||
</NavLink>
|
||||
</div>
|
||||
|
@ -132,22 +132,10 @@
|
||||
_isEnabled = job.IsEnabled.ToString();
|
||||
_interval = job.Interval.ToString();
|
||||
_frequency = job.Frequency;
|
||||
_startDate = job.StartDate;
|
||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_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");
|
||||
}
|
||||
(_startDate, _startTime) = Utilities.UtcAsLocalDateAndTime(job.StartDate);
|
||||
(_endDate, _endTime) = Utilities.UtcAsLocalDateAndTime(job.EndDate);
|
||||
_retentionHistory = job.RetentionHistory.ToString();
|
||||
_nextDate = job.NextExecution;
|
||||
if (job.NextExecution != null && job.NextExecution.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
_nextTime = job.NextExecution.Value.ToString("HH:mm");
|
||||
}
|
||||
(_nextDate, _nextTime) = Utilities.UtcAsLocalDateAndTime(job.NextExecution);
|
||||
createdby = job.CreatedBy;
|
||||
createdon = job.CreatedOn;
|
||||
modifiedby = job.ModifiedBy;
|
||||
@ -180,50 +168,27 @@
|
||||
{
|
||||
job.Interval = int.Parse(_interval);
|
||||
}
|
||||
job.StartDate = _startDate;
|
||||
if (job.StartDate != null)
|
||||
{
|
||||
job.StartDate = job.StartDate.Value.Date;
|
||||
if (!string.IsNullOrEmpty(_startTime))
|
||||
{
|
||||
job.StartDate = DateTime.Parse(job.StartDate.Value.ToShortDateString() + " " + _startTime);
|
||||
}
|
||||
}
|
||||
job.EndDate = _endDate;
|
||||
if (job.EndDate != null)
|
||||
{
|
||||
job.EndDate = job.EndDate.Value.Date;
|
||||
if (!string.IsNullOrEmpty(_endTime))
|
||||
{
|
||||
job.EndDate = DateTime.Parse(job.EndDate.Value.ToShortDateString() + " " + _endTime);
|
||||
}
|
||||
}
|
||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||
job.NextExecution = _nextDate;
|
||||
if (job.NextExecution != null)
|
||||
{
|
||||
job.NextExecution = job.NextExecution.Value.Date;
|
||||
if (!string.IsNullOrEmpty(_nextTime))
|
||||
{
|
||||
job.NextExecution = DateTime.Parse(job.NextExecution.Value.ToShortDateString() + " " + _nextTime);
|
||||
}
|
||||
}
|
||||
job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime);
|
||||
job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime);
|
||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||
job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _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);
|
||||
}
|
||||
}
|
||||
|
||||
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>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||
<td>@context.NextExecution</td>
|
||||
<td>@context.NextExecution?.ToLocalTime()</td>
|
||||
<td>
|
||||
@if (context.IsStarted)
|
||||
{
|
||||
|
@ -110,6 +110,10 @@ else
|
||||
}
|
||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
||||
|
@ -30,7 +30,7 @@ else
|
||||
<td>@context.Version</td>
|
||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||
<td>
|
||||
@if (UpgradeAvailable(context.Code))
|
||||
@if (UpgradeAvailable(context.Code, context.Version))
|
||||
{
|
||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||
}
|
||||
@ -40,21 +40,21 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Language> _languages;
|
||||
private List<Package> _packages;
|
||||
private List<Language> _languages;
|
||||
private List<Package> _packages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_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));
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_packages = await PackageService.GetPackagesAsync("translation");
|
||||
_packages = await PackageService.GetPackagesAsync("translation");
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,15 +75,16 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpgradeAvailable(string code)
|
||||
private bool UpgradeAvailable(string code, string version)
|
||||
{
|
||||
var upgradeavailable = false;
|
||||
if (_packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == ("Oqtane.Client." + code)).FirstOrDefault();
|
||||
if (package != null)
|
||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -96,7 +97,7 @@ else
|
||||
{
|
||||
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 PackageService.InstallPackagesAsync();
|
||||
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
|
@ -3,7 +3,6 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject SiteState SiteState
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -184,11 +183,12 @@
|
||||
var interop = new Interop(JSRuntime);
|
||||
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};
|
||||
|
||||
|
||||
if (!twofactor)
|
||||
{
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -199,10 +199,21 @@
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||
|
||||
// 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);
|
||||
if (hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive login
|
||||
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
|
||||
{
|
||||
|
@ -130,13 +130,14 @@
|
||||
private string _properties = 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()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logId = Int32.Parse(PageState.QueryString["id"]);
|
||||
_logId = Int32.Parse(UrlParameters["id"]);
|
||||
var log = await LogService.GetLogAsync(_logId);
|
||||
if (log != null)
|
||||
{
|
||||
@ -191,13 +192,6 @@
|
||||
|
||||
private string CloseUrl()
|
||||
{
|
||||
if (!PageState.QueryString.ContainsKey("level"))
|
||||
{
|
||||
return NavigateUrl();
|
||||
}
|
||||
else
|
||||
{
|
||||
return NavigateUrl(PageState.Page.Path, "level=" + PageState.QueryString["level"] + "&function=" + PageState.QueryString["function"] + "&rows=" + PageState.QueryString["rows"] + "&page=" + PageState.QueryString["page"]);
|
||||
}
|
||||
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ else
|
||||
<th>@Localizer["Function"]</th>
|
||||
</Header>
|
||||
<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.Level</td>
|
||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||
@ -99,29 +99,32 @@ else
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
|
||||
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// external link to log item will display Details component
|
||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={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;
|
||||
}
|
||||
|
@ -114,6 +114,10 @@
|
||||
<button type="button" class="btn btn-success" @onclick="InstallModules">@SharedLocalizer["Install"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||
|
||||
@code {
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
|
@ -1,7 +1,10 @@
|
||||
@namespace Oqtane.Modules.Admin.ModuleDefinitions
|
||||
@inherits ModuleBase
|
||||
@using System.Globalization
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@inject IModuleDefinitionService ModuleDefinitionService
|
||||
@inject IPackageService PackageService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@ -99,35 +102,32 @@
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Translations" ResourceKey="Translations">
|
||||
@if (_packages != null)
|
||||
@if (_languages != null)
|
||||
{
|
||||
if (_packages.Count > 0)
|
||||
@if (_languages.Count > 0)
|
||||
{
|
||||
<Pager Items="@_packages">
|
||||
<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>
|
||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3> by: <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
|
||||
@if (context.IsDefault)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||
}
|
||||
</td>
|
||||
<td style="width: 1px; vertical-align: middle;">
|
||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
||||
{
|
||||
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||
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>
|
||||
@ -140,6 +140,7 @@
|
||||
<div class="mx-auto text-center">
|
||||
@Localizer["Search.NoResults"]
|
||||
</div>
|
||||
<br />
|
||||
}
|
||||
}
|
||||
</TabPanel>
|
||||
@ -169,7 +170,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" @onclick="DownloadTranslation">@SharedLocalizer["Accept"]</button>
|
||||
<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>
|
||||
@ -179,110 +180,120 @@
|
||||
}
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _moduleDefinitionId;
|
||||
private string _name;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _moduleDefinitionId;
|
||||
private string _name;
|
||||
private string _description = "";
|
||||
private string _categories;
|
||||
private string _moduledefinitionname = "";
|
||||
private string _moduledefinitionname = "";
|
||||
private string _version;
|
||||
private string _packagename = "";
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
private string _runtimes = "";
|
||||
private string _permissions;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
private string _runtimes = "";
|
||||
private string _permissions;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
|
||||
#pragma warning disable 649
|
||||
private PermissionGrid _permissionGrid;
|
||||
private PermissionGrid _permissionGrid;
|
||||
#pragma warning restore 649
|
||||
|
||||
private List<Package> _packages;
|
||||
private List<Language> _languages;
|
||||
private string _productname = "";
|
||||
private string _packageid = "";
|
||||
private string _packagelicense = "";
|
||||
private string _packageversion = "";
|
||||
private string _packageid = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
||||
if (moduleDefinition != null)
|
||||
{
|
||||
_name = moduleDefinition.Name;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_moduleDefinitionId = Int32.Parse(PageState.QueryString["id"]);
|
||||
var moduleDefinition = await ModuleDefinitionService.GetModuleDefinitionAsync(_moduleDefinitionId, ModuleState.SiteId);
|
||||
if (moduleDefinition != null)
|
||||
{
|
||||
_name = moduleDefinition.Name;
|
||||
_description = moduleDefinition.Description;
|
||||
_categories = moduleDefinition.Categories;
|
||||
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
||||
_categories = moduleDefinition.Categories;
|
||||
_moduledefinitionname = moduleDefinition.ModuleDefinitionName;
|
||||
_version = moduleDefinition.Version;
|
||||
_packagename = moduleDefinition.PackageName;
|
||||
_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;
|
||||
_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;
|
||||
|
||||
_packages = await PackageService.GetPackagesAsync("translation", "", "", moduleDefinition.PackageName);
|
||||
_packages = await PackageService.GetPackagesAsync("translation", "", "", _packagename);
|
||||
_languages = await LanguageService.GetLanguagesAsync(-1, _packagename);
|
||||
foreach (var package in _packages)
|
||||
{
|
||||
var code = package.PackageId.Split('.').Last();
|
||||
if (!_languages.Any(item => item.Code == code))
|
||||
{
|
||||
_languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = package.Version, IsDefault = true });
|
||||
}
|
||||
}
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 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()
|
||||
{
|
||||
@ -290,37 +301,53 @@
|
||||
_packagelicense = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task GetPackage(string packageid, string version)
|
||||
|
||||
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(packageid, version);
|
||||
var package = await PackageService.GetPackageAsync(packagename, version);
|
||||
if (package != null)
|
||||
{
|
||||
_productname = package.Name;
|
||||
_packageid = package.PackageId;
|
||||
if (!string.IsNullOrEmpty(package.License))
|
||||
{
|
||||
_packagelicense = package.License.Replace("\n", "<br />");
|
||||
}
|
||||
_packageversion = package.Version;
|
||||
_packageid = package.PackageId;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
|
||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packagename, version);
|
||||
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadTranslation()
|
||||
private async Task DownloadPackage(string packagename)
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
|
||||
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
|
||||
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 = "";
|
||||
@ -328,7 +355,7 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packageid, _version);
|
||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
||||
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
@code {
|
||||
private string _content = string.Empty;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Export Content";
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId);
|
||||
_content = await ModuleService.ExportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId);
|
||||
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -25,7 +25,7 @@
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Import Content";
|
||||
|
||||
private async Task ImportModule()
|
||||
@ -38,7 +38,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
|
||||
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId, _content);
|
||||
if (success)
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Content.Import"], MessageType.Success);
|
||||
|
@ -24,7 +24,7 @@
|
||||
<div class="col-sm-9">
|
||||
<select id="parent" class="form-select" @onchange="(e => ParentChanged(e))" required>
|
||||
<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>
|
||||
}
|
||||
@ -167,7 +167,6 @@
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pageList;
|
||||
private string _name;
|
||||
private string _title;
|
||||
private string _meta;
|
||||
@ -201,7 +200,6 @@
|
||||
_themetype = PageState.Site.DefaultThemeType;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = PageState.Site.DefaultContainerType;
|
||||
_pageList = PageState.Pages;
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
_permissions = string.Empty;
|
||||
ThemeSettings();
|
||||
@ -307,6 +305,10 @@
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
{
|
||||
_path = _path.Substring(0, _path.Length - 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -421,14 +424,4 @@
|
||||
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">
|
||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||
@foreach (Page page in _pageList)
|
||||
@foreach (Page page in PageState.Pages)
|
||||
{
|
||||
if (page.PageId != _pageId)
|
||||
{
|
||||
@ -201,7 +201,6 @@
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pageList;
|
||||
private List<Module> _pageModules;
|
||||
private int _pageId;
|
||||
private string _name;
|
||||
@ -238,7 +237,6 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_pageList = PageState.Pages;
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
@ -435,6 +433,10 @@
|
||||
}
|
||||
if (_path.Contains("/"))
|
||||
{
|
||||
if (_path.EndsWith("/") && _path != "/")
|
||||
{
|
||||
_path = _path.Substring(0, _path.Length - 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);
|
||||
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 != "=")
|
||||
{
|
||||
Page child;
|
||||
@ -567,9 +576,4 @@
|
||||
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>
|
||||
<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>@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.DeletedBy</td>
|
||||
<td>@context.DeletedOn</td>
|
||||
@ -140,6 +140,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
foreach (Page page in _pages)
|
||||
{
|
||||
await PageService.DeletePageAsync(page.PageId);
|
||||
@ -148,6 +149,7 @@
|
||||
|
||||
await logger.LogInformation("Pages Permanently Deleted");
|
||||
await Load();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
@ -155,6 +157,7 @@
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,6 +207,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
foreach (Module module in _modules)
|
||||
{
|
||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||
@ -218,12 +222,14 @@
|
||||
|
||||
await logger.LogInformation("Modules Permanently Deleted");
|
||||
await Load();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Modules.Delete"], MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,21 @@
|
||||
}
|
||||
</select>
|
||||
</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 class="row mb-1 align-items-center">
|
||||
<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>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -295,6 +311,7 @@
|
||||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "-";
|
||||
private string _homepageid = "-";
|
||||
private string _smtphost = string.Empty;
|
||||
private string _smtpport = string.Empty;
|
||||
private string _smtpssl = "False";
|
||||
@ -353,6 +370,11 @@
|
||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||
|
||||
if (site.HomePageId != null)
|
||||
{
|
||||
_homepageid = site.HomePageId.Value.ToString();
|
||||
}
|
||||
|
||||
_pwaisenabled = site.PwaIsEnabled.ToString();
|
||||
if (site.PwaAppIconFileId != null)
|
||||
{
|
||||
@ -479,6 +501,7 @@
|
||||
refresh = true; // needs to be refreshed on client
|
||||
}
|
||||
site.AdminContainerType = _admincontainertype;
|
||||
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
|
||||
|
||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||
{
|
||||
|
@ -89,7 +89,8 @@ else
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
</select>
|
||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -336,7 +337,7 @@ else
|
||||
user.Username = _hostusername;
|
||||
user.Password = _hostpassword;
|
||||
user.LastIPAddress = PageState.RemoteIPAddress;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
user = await UserService.LoginUserAsync(user, false, false);
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
|
||||
|
@ -114,6 +114,10 @@
|
||||
<button type="button" class="btn btn-success" @onclick="InstallThemes">@SharedLocalizer["Install"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||
|
||||
@code {
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
|
@ -226,8 +226,8 @@ else
|
||||
@code {
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private bool allowtwofactor = false;
|
||||
private string twofactor = "False";
|
||||
@ -429,6 +429,7 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
ModuleInstance.ShowProgressIndicator();
|
||||
foreach(var Notification in notifications)
|
||||
{
|
||||
if (!Notification.IsDeleted)
|
||||
@ -444,12 +445,15 @@ else
|
||||
}
|
||||
await logger.LogInformation("Notifications Permanently Deleted");
|
||||
await LoadNotificationsAsync();
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Notifications {Error}", ex.Message);
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
ModuleInstance.HideProgressIndicator();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
@inject ISiteService SiteService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject SiteState SiteState
|
||||
|
||||
@if (users == null)
|
||||
{
|
||||
|
@ -128,13 +128,6 @@
|
||||
|
||||
private string CloseUrl()
|
||||
{
|
||||
if (!PageState.QueryString.ContainsKey("type"))
|
||||
{
|
||||
return NavigateUrl();
|
||||
}
|
||||
else
|
||||
{
|
||||
return NavigateUrl(PageState.Page.Path, "type=" + PageState.QueryString["type"] + "&days=" + PageState.QueryString["days"] + "&page=" + PageState.QueryString["page"]);
|
||||
}
|
||||
return (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl();
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ else
|
||||
<th>@Localizer["Created"]</th>
|
||||
</Header>
|
||||
<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>
|
||||
@if (context.UserId != null)
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@using System.Net
|
||||
@inherits LocalizableComponent
|
||||
@inject IUserService UserService
|
||||
|
||||
@ -71,6 +72,9 @@
|
||||
[Parameter]
|
||||
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()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
@ -116,9 +120,13 @@
|
||||
}
|
||||
|
||||
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
|
||||
_text = Localize(nameof(Text), _text);
|
||||
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
|
||||
_authorized = IsAuthorized();
|
||||
_text = Localize(nameof(Text), _text);
|
||||
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
|
||||
if (!string.IsNullOrEmpty(ReturnUrl))
|
||||
{
|
||||
_url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";
|
||||
}
|
||||
_authorized = IsAuthorized();
|
||||
}
|
||||
|
||||
private bool IsAuthorized()
|
||||
|
@ -376,42 +376,62 @@
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.File.NotSelected"];
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.File.NotSelected"];
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteFile()
|
||||
{
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
await FileService.DeleteFileAsync(FileId);
|
||||
await logger.LogInformation("File Deleted {File}", FileId);
|
||||
await OnDelete.InvokeAsync(FileId);
|
||||
private async Task DeleteFile()
|
||||
{
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
await FileService.DeleteFileAsync(FileId);
|
||||
await logger.LogInformation("File Deleted {File}", FileId);
|
||||
await OnDelete.InvokeAsync(FileId);
|
||||
|
||||
_message = Localizer["Success.File.Delete"];
|
||||
_messagetype = MessageType.Success;
|
||||
_message = Localizer["Success.File.Delete"];
|
||||
_messagetype = MessageType.Success;
|
||||
|
||||
await GetFiles();
|
||||
FileId = -1;
|
||||
await SetImage();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
||||
await GetFiles();
|
||||
FileId = -1;
|
||||
await SetImage();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
|
||||
|
||||
_message = Localizer["Error.File.Delete"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
_message = Localizer["Error.File.Delete"];
|
||||
_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="col">
|
||||
<TabStrip>
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
@if (_filemanagervisible)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<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-primary" @onclick="InsertImage">@Localizer["InsertImage"]</button>
|
||||
@if (_filemanagervisible)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["SynchronizeContent"]</button>
|
||||
</div>
|
||||
@if (ReadOnly)
|
||||
{
|
||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor">
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
||||
}
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||
@if (_rawfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -85,10 +106,11 @@
|
||||
@code {
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _filemanagervisible = false;
|
||||
private bool _richfilemanager = false;
|
||||
private FileManager _fileManager;
|
||||
private string _richhtml = string.Empty;
|
||||
private string _originalrichhtml = string.Empty;
|
||||
private bool _rawfilemanager = false;
|
||||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
@ -102,6 +124,12 @@
|
||||
[Parameter]
|
||||
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
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
@ -112,9 +140,6 @@
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
|
||||
[Parameter]
|
||||
public bool AllowFileManagement { get; set; } = true;
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
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;
|
||||
StateHasChanged();
|
||||
}
|
||||
@ -194,29 +226,55 @@
|
||||
return _originalrawhtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertImage()
|
||||
{
|
||||
_message = string.Empty;
|
||||
if (_filemanagervisible)
|
||||
{
|
||||
var file = _fileManager.GetFile();
|
||||
if (file != null)
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
await interop.InsertImage(_editorElement, file.Url, file.Name);
|
||||
_filemanagervisible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.Image"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_filemanagervisible = true;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
public async Task InsertRichImage()
|
||||
{
|
||||
_message = string.Empty;
|
||||
if (_richfilemanager)
|
||||
{
|
||||
var file = _fileManager.GetFile();
|
||||
if (file != null)
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name));
|
||||
_richfilemanager = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.Require.Image"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_richfilemanager = true;
|
||||
}
|
||||
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">
|
||||
@if (_content != null)
|
||||
{
|
||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@ -60,6 +60,7 @@
|
||||
|
||||
private RichTextEditor RichTextEditorHtml;
|
||||
private bool _allowfilemanagement;
|
||||
private bool _allowrawhtml;
|
||||
private string _content = null;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
@ -73,6 +74,7 @@
|
||||
try
|
||||
{
|
||||
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
|
||||
_allowrawhtml = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true"));
|
||||
await LoadContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -15,18 +15,29 @@
|
||||
</select>
|
||||
</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>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||
private string _allowfilemanagement;
|
||||
private string _allowrawhtml;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
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)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
|
||||
@ -39,7 +50,8 @@
|
||||
{
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
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)
|
||||
{
|
||||
|
@ -15,6 +15,8 @@ namespace Oqtane.Modules
|
||||
public abstract class ModuleBase : ComponentBase, IModuleControl
|
||||
{
|
||||
private Logger _logger;
|
||||
private string _urlparametersstate;
|
||||
private Dictionary<string, string> _urlparameters;
|
||||
|
||||
protected Logger logger => _logger ?? (_logger = new Logger(this));
|
||||
|
||||
@ -24,6 +26,9 @@ namespace Oqtane.Modules
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected SiteState SiteState { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
|
||||
@ -44,6 +49,21 @@ namespace Oqtane.Modules
|
||||
|
||||
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
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
@ -55,7 +75,8 @@ namespace Oqtane.Modules
|
||||
var scripts = new List<object>();
|
||||
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())
|
||||
{
|
||||
@ -149,15 +170,26 @@ namespace Oqtane.Modules
|
||||
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>();
|
||||
string[] templateSegments;
|
||||
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
var parameterId = 0;
|
||||
var parameters = _urlparametersstate.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
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++)
|
||||
{
|
||||
urlParameters.TryAdd("parameter" + i, parameters[i]);
|
||||
@ -165,39 +197,37 @@ namespace Oqtane.Modules
|
||||
}
|
||||
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])
|
||||
{
|
||||
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
|
||||
parameterId++;
|
||||
}
|
||||
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
|
||||
{
|
||||
var key = templateSegments[i].Replace("{", "");
|
||||
key = key.Replace("}", "");
|
||||
urlParameters.TryAdd(key, parameters[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
i = parameters.Length;
|
||||
urlParameters.Clear();
|
||||
}
|
||||
// dynamic segment
|
||||
key = key.Substring(1, key.Length - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// static segments use generic "parameter#" keys
|
||||
key = "parameter" + i.ToString();
|
||||
}
|
||||
}
|
||||
else // unspecified segments use generic "parameter#" keys
|
||||
{
|
||||
key = "parameter" + i.ToString();
|
||||
}
|
||||
urlParameters.TryAdd(key, parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return urlParameters;
|
||||
}
|
||||
|
||||
// user feedback methods
|
||||
// UI methods
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(message, type);
|
||||
@ -218,6 +248,18 @@ namespace Oqtane.Modules
|
||||
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
|
||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||
{
|
||||
|
@ -5,15 +5,15 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.1.4</Version>
|
||||
<Version>3.2.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
<Description>Modular Application Framework for Blazor</Description>
|
||||
<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.1.4</PackageReleaseNotes>
|
||||
<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</RootNamespace>
|
||||
@ -35,13 +35,8 @@
|
||||
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="System.Runtime" />
|
||||
<TrimmerRootAssembly Include="System.Linq.Parallel" />
|
||||
<TrimmerRootAssembly Include="System.Runtime.CompilerServices.VisualC" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
<BlazorEnableCompression>false</BlazorEnableCompression>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
@ -15,7 +16,6 @@ using Microsoft.JSInterop;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
|
||||
namespace Oqtane.Client
|
||||
@ -33,7 +33,7 @@ namespace Oqtane.Client
|
||||
|
||||
builder.Services.AddOptions();
|
||||
|
||||
// Register localization services
|
||||
// register localization services
|
||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
// register auth services
|
||||
@ -42,7 +42,9 @@ namespace Oqtane.Client
|
||||
// register scoped core services
|
||||
builder.Services.AddOqtaneScopedServices();
|
||||
|
||||
await LoadClientAssemblies(httpClient);
|
||||
var serviceProvider = builder.Services.BuildServiceProvider();
|
||||
|
||||
await LoadClientAssemblies(httpClient, serviceProvider);
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
@ -54,37 +56,105 @@ namespace Oqtane.Client
|
||||
RegisterClientStartups(assembly, builder.Services);
|
||||
}
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
await SetCultureFromLocalizationCookie(host.Services);
|
||||
|
||||
ServiceActivator.Configure(host.Services);
|
||||
|
||||
await host.RunAsync();
|
||||
await builder.Build().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 assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
|
||||
var dlls = new Dictionary<string, byte[]>();
|
||||
var pdbs = new Dictionary<string, byte[]>();
|
||||
var filter = new List<string>();
|
||||
|
||||
// get assemblies from server and load into client app domain
|
||||
var zip = await http.GetByteArrayAsync($"/api/Installation/load");
|
||||
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||
var interop = new Interop(jsRuntime);
|
||||
var files = await interop.GetIndexedDBKeys(".dll");
|
||||
|
||||
// asemblies and debug symbols are packaged in a zip file
|
||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||
if (files.Count() != 0)
|
||||
{
|
||||
var dlls = new Dictionary<string, byte[]>();
|
||||
var pdbs = new Dictionary<string, byte[]>();
|
||||
// get list of assemblies from server
|
||||
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())
|
||||
{
|
||||
entry.Open().CopyTo(memoryStream);
|
||||
byte[] file = memoryStream.ToArray();
|
||||
|
||||
// save assembly to indexeddb
|
||||
try
|
||||
{
|
||||
await interop.SetIndexedDBItem(entry.FullName, file);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
switch (Path.GetExtension(entry.FullName))
|
||||
{
|
||||
case ".dll":
|
||||
@ -97,21 +167,28 @@ 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]));
|
||||
}
|
||||
else
|
||||
{
|
||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
|
||||
}
|
||||
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key.Replace(".dll", ".pdb")]));
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
// dynamically register module scoped services
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -160,7 +160,7 @@
|
||||
<value>Error Loading Pane Layouts For Theme</value>
|
||||
</data>
|
||||
<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 name="Message.Required.PageInfo" xml:space="preserve">
|
||||
<value>You Must Provide Page Name, Theme, and Container</value>
|
||||
@ -228,13 +228,13 @@
|
||||
<data name="Appearance.Name" xml:space="preserve">
|
||||
<value>Appearance</value>
|
||||
</data>
|
||||
<data name="Message.Page.Deleted" xml:space="preserve">
|
||||
<value>A page with path {0} already exists for the selected parent page in the Recycle Bin. Either recover the page or remove from the Recycle Bin and create it again.</value>
|
||||
</data>
|
||||
<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>
|
||||
</data>
|
||||
<data name="Meta.Text" xml:space="preserve">
|
||||
<value>Meta:</value>
|
||||
</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>
|
@ -151,7 +151,7 @@
|
||||
<value>Error Loading Pane Layouts For Theme</value>
|
||||
</data>
|
||||
<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 name="Message.Required.PageInfo" xml:space="preserve">
|
||||
<value>You Must Provide Page Name, Theme, and Container</value>
|
||||
@ -270,4 +270,7 @@
|
||||
<data name="Meta.Text" xml:space="preserve">
|
||||
<value>Meta:</value>
|
||||
</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>
|
@ -333,4 +333,10 @@
|
||||
<data name="Confirm.Alias.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete {0}?</value>
|
||||
</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>
|
@ -123,4 +123,10 @@
|
||||
<data name="AllowFileManagement.Text" xml:space="preserve">
|
||||
<value>Allow File Management: </value>
|
||||
</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>
|
@ -318,6 +318,9 @@
|
||||
<data name="BlazorWebAssembly" xml:space="preserve">
|
||||
<value>Blazor WebAssembly</value>
|
||||
</data>
|
||||
<data name="BlazorHybrid" xml:space="preserve">
|
||||
<value>Blazor Hybrid</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
@ -336,4 +339,7 @@
|
||||
<data name="Visitor Management" xml:space="preserve">
|
||||
<value>Visitor Management</value>
|
||||
</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>
|
@ -1,10 +1,8 @@
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using Oqtane.Documentation;
|
||||
@ -20,9 +18,7 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<List<Folder>> GetFoldersAsync(int siteId)
|
||||
{
|
||||
List<Folder> folders = await GetJsonAsync<List<Folder>>($"{ApiUrl}?siteid={siteId}");
|
||||
folders = GetFoldersHierarchy(folders);
|
||||
return folders;
|
||||
return await GetJsonAsync<List<Folder>>($"{ApiUrl}?siteid={siteId}");
|
||||
}
|
||||
|
||||
public async Task<Folder> GetFolderAsync(int folderId)
|
||||
@ -58,48 +54,5 @@ namespace Oqtane.Services
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,13 +50,13 @@ namespace Oqtane.Services
|
||||
/// <param name="moduleId"></param>
|
||||
/// <param name="content">module in JSON format</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> ImportModuleAsync(int moduleId, string content);
|
||||
Task<bool> ImportModuleAsync(int moduleId, int pageId, string content);
|
||||
|
||||
/// <summary>
|
||||
/// Exports a given module
|
||||
/// </summary>
|
||||
/// <param name="moduleId"></param>
|
||||
/// <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.
|
||||
/// </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="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>
|
||||
Task<User> LoginUserAsync(User user);
|
||||
Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent);
|
||||
|
||||
/// <summary>
|
||||
/// Logout a <see cref="User"/>
|
||||
|
@ -5,6 +5,7 @@ using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Modules.Controls;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
@ -44,14 +45,14 @@ namespace Oqtane.Services
|
||||
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)
|
||||
{
|
||||
return await GetStringAsync($"{Apiurl}/export?moduleid={moduleId}");
|
||||
public async Task<string> ExportModuleAsync(int moduleId, int pageId)
|
||||
{
|
||||
return await GetStringAsync($"{Apiurl}/export?moduleid={moduleId}&pageid={pageId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.Net;
|
||||
using Oqtane.Documentation;
|
||||
|
||||
@ -19,9 +17,7 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<List<Page>> GetPagesAsync(int siteId)
|
||||
{
|
||||
List<Page> pages = await GetJsonAsync<List<Page>>($"{Apiurl}?siteid={siteId}");
|
||||
pages = GetPagesHierarchy(pages);
|
||||
return pages;
|
||||
return await GetJsonAsync<List<Page>>($"{Apiurl}?siteid={siteId}");
|
||||
}
|
||||
|
||||
public async Task<Page> GetPageAsync(int pageId)
|
||||
@ -73,45 +69,5 @@ namespace Oqtane.Services
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -19,9 +19,9 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private void CloseModal()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
private void CloseModal()
|
||||
{
|
||||
NavigationManager.NavigateTo((!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : NavigateUrl());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,9 @@
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
// 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.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
|
||||
@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">
|
||||
<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);">
|
||||
@foreach (var action in Actions.Where(item => !item.Name.Contains("Pane")))
|
||||
{
|
||||
if (string.IsNullOrEmpty(action.Name))
|
||||
{
|
||||
<li class="dropdown-divider"></li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@if (Actions.Where(item => item.Name.Contains("Pane")).Any())
|
||||
{
|
||||
<li class="dropdown-submenu">
|
||||
<a class="dropdown-item" onclick="return subMenu(this)">
|
||||
<span class="@Icons.AccountLogin" aria-hidden="true"></span> Move To >
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var action in Actions.Where(item => item.Name.Contains("Pane")))
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<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>
|
||||
<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")))
|
||||
{
|
||||
if (string.IsNullOrEmpty(action.Name))
|
||||
{
|
||||
<li class="dropdown-divider"></li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@if (Actions.Where(item => item.Name.Contains("Pane")).Any())
|
||||
{
|
||||
<li class="dropdown-submenu">
|
||||
<a class="dropdown-item" onclick="return subMenu(this)">
|
||||
<span class="@Icons.AccountLogin" aria-hidden="true"></span> Move To >
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var action in Actions.Where(item => item.Name.Contains("Pane")))
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
|
||||
<span class="@action.Icon" aria-hidden="true"></span> @action.Name
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
@ -29,54 +29,55 @@ namespace Oqtane.Themes.Controls
|
||||
protected virtual List<ActionViewModel> GetActions()
|
||||
{
|
||||
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)});
|
||||
|
||||
if (UserSecurity.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
|
||||
actionList.Add(new ActionViewModel { Icon = Icons.Cog, Name = "Manage Settings", Action = async (u, m) => await Settings(u, m) });
|
||||
|
||||
if (UserSecurity.ContainsRole(ModuleState.Permissions, PermissionNames.View, RoleNames.Everyone))
|
||||
{
|
||||
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
|
||||
{
|
||||
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 != "")
|
||||
{
|
||||
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.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
|
||||
actionList.Add(new ActionViewModel { Icon = Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import") });
|
||||
actionList.Add(new ActionViewModel { Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export") });
|
||||
}
|
||||
|
||||
actionList.Add(new ActionViewModel {Name = ""});
|
||||
actionList.Add(new ActionViewModel { Name = "" });
|
||||
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
@inherits ContainerBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject SiteState SiteState
|
||||
|
||||
<span class="app-moduletitle">
|
||||
<a id="@ModuleState.PageModuleId.ToString()">
|
||||
@ -9,17 +11,39 @@
|
||||
</span>
|
||||
|
||||
@code {
|
||||
private string title = "";
|
||||
private string title = "";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
|
||||
{
|
||||
title = ModuleState.ControlTitle;
|
||||
}
|
||||
else
|
||||
{
|
||||
title = ModuleState.Title;
|
||||
}
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged;
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@
|
||||
</div>
|
||||
<div class="row d-flex">
|
||||
<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>
|
||||
}
|
||||
@ -431,152 +431,166 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ToggleEditMode(bool EditMode)
|
||||
{
|
||||
if (_showEditMode)
|
||||
{
|
||||
if (EditMode)
|
||||
{
|
||||
PageState.EditMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PageState.EditMode = true;
|
||||
}
|
||||
private async Task ToggleEditMode(bool EditMode)
|
||||
{
|
||||
if (_showEditMode)
|
||||
{
|
||||
if (EditMode)
|
||||
{
|
||||
PageState.EditMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
PageState.EditMode = true;
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null)
|
||||
{
|
||||
await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
|
||||
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
|
||||
{
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null)
|
||||
{
|
||||
await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
|
||||
PageState.EditMode = true;
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Navigate(string location)
|
||||
{
|
||||
Module module;
|
||||
switch (location)
|
||||
{
|
||||
case "Admin":
|
||||
// get admin dashboard moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||
private void Navigate(string location)
|
||||
{
|
||||
Module module;
|
||||
switch (location)
|
||||
{
|
||||
case "Admin":
|
||||
// get admin dashboard moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||
|
||||
if (module != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
||||
}
|
||||
if (module != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
||||
}
|
||||
|
||||
break;
|
||||
case "Add":
|
||||
case "Edit":
|
||||
string url = "";
|
||||
// get page management moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
|
||||
break;
|
||||
case "Add":
|
||||
case "Edit":
|
||||
string url = "";
|
||||
// get page management moduleid
|
||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.PageManagementModule);
|
||||
|
||||
if (module != null)
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
case "Add":
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "cp=" + PageState.Page.PageId);
|
||||
break;
|
||||
case "Edit":
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString() + "&cp=" + PageState.Page.PageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (module != null)
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
case "Add":
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "cp=" + PageState.Page.PageId);
|
||||
break;
|
||||
case "Edit":
|
||||
url = EditUrl(PageState.Page.Path, module.ModuleId, location, "id=" + PageState.Page.PageId.ToString() + "&cp=" + PageState.Page.PageId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (url != "")
|
||||
{
|
||||
NavigationManager.NavigateTo(url);
|
||||
}
|
||||
if (url != "")
|
||||
{
|
||||
NavigationManager.NavigateTo(url);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void Publish(string action)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||
{
|
||||
List<PermissionString> permissions;
|
||||
private async void Publish(string action)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||
{
|
||||
List<PermissionString> permissions;
|
||||
|
||||
// publish/unpublish page
|
||||
var page = PageState.Page;
|
||||
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
|
||||
foreach (var permissionstring in permissions)
|
||||
{
|
||||
if (permissionstring.PermissionName == PermissionNames.View)
|
||||
{
|
||||
List<string> ids = permissionstring.Permissions.Split(';').ToList();
|
||||
switch (action)
|
||||
{
|
||||
case "publish":
|
||||
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
|
||||
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
|
||||
break;
|
||||
case "unpublish":
|
||||
ids.Remove(RoleNames.Everyone);
|
||||
ids.Remove(RoleNames.Registered);
|
||||
break;
|
||||
}
|
||||
permissionstring.Permissions = string.Join(";", ids.ToArray());
|
||||
}
|
||||
}
|
||||
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
||||
await PageService.UpdatePageAsync(page);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
}
|
||||
// publish/unpublish page
|
||||
var page = PageState.Page;
|
||||
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
|
||||
foreach (var permissionstring in permissions)
|
||||
{
|
||||
if (permissionstring.PermissionName == PermissionNames.View)
|
||||
{
|
||||
List<string> ids = permissionstring.Permissions.Split(';').ToList();
|
||||
switch (action)
|
||||
{
|
||||
case "publish":
|
||||
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
|
||||
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
|
||||
break;
|
||||
case "unpublish":
|
||||
ids.Remove(RoleNames.Everyone);
|
||||
ids.Remove(RoleNames.Registered);
|
||||
break;
|
||||
}
|
||||
permissionstring.Permissions = string.Join(";", ids.ToArray());
|
||||
}
|
||||
}
|
||||
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
||||
await PageService.UpdatePageAsync(page);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfirmDelete()
|
||||
{
|
||||
_deleteConfirmation = !_deleteConfirmation;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void ConfirmDelete()
|
||||
{
|
||||
_deleteConfirmation = !_deleteConfirmation;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task DeletePage()
|
||||
{
|
||||
ConfirmDelete();
|
||||
private async Task DeletePage()
|
||||
{
|
||||
ConfirmDelete();
|
||||
|
||||
var page = PageState.Page;
|
||||
try
|
||||
{
|
||||
if (page.UserId == null)
|
||||
{
|
||||
page.IsDeleted = true;
|
||||
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);
|
||||
NavigationManager.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
else // personalized page
|
||||
{
|
||||
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);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
var page = PageState.Page;
|
||||
try
|
||||
{
|
||||
if (page.UserId == null)
|
||||
{
|
||||
page.IsDeleted = true;
|
||||
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);
|
||||
NavigationManager.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
else // personalized page
|
||||
{
|
||||
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);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private string settingCategory = "CP-category";
|
||||
private string settingPane = "CP-pane";
|
||||
private string _pane = "";
|
||||
private string settingCategory = "CP-category";
|
||||
private string settingPane = "CP-pane";
|
||||
private string _pane = "";
|
||||
|
||||
private async Task LoadSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
_category = SettingService.GetSetting(settings, settingCategory, "Common");
|
||||
var pane = SettingService.GetSetting(settings, settingPane, "");
|
||||
_pane = PageState.Page.Panes.Contains(pane) ? pane : PaneNames.Admin;
|
||||
private async Task LoadSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
_category = SettingService.GetSetting(settings, settingCategory, "Common");
|
||||
var pane = SettingService.GetSetting(settings, settingPane, "");
|
||||
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()
|
||||
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Providers;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
@ -38,12 +39,23 @@ namespace Oqtane.Themes.Controls
|
||||
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
|
||||
{
|
||||
url = PageState.Alias.Path;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (PageState.Runtime == Shared.Runtime.Hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive logout
|
||||
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="row">
|
||||
<div class="col-md-12">
|
||||
<Pane Name="@PaneNames.Admin" />
|
||||
<Pane Name="@PaneNames.Default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Pane Name="Top Full Width" />
|
||||
<Pane Name="Top Full Width" />
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@ -102,20 +102,20 @@
|
||||
{
|
||||
<Pane Name="Footer" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@code {
|
||||
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>()
|
||||
{
|
||||
// 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.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;
|
||||
|
@ -1,20 +1,26 @@
|
||||
@using System.ComponentModel
|
||||
@namespace Oqtane.UI
|
||||
@inject SiteState SiteState
|
||||
|
||||
<CascadingValue Value="@ModuleState">
|
||||
@if (_useadminborder)
|
||||
{
|
||||
<div class="app-pane-admin-border">
|
||||
@DynamicComponent
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@DynamicComponent
|
||||
}
|
||||
</CascadingValue>
|
||||
@if (_visible)
|
||||
{
|
||||
<CascadingValue Value="@ModuleState">
|
||||
@if (_useadminborder)
|
||||
{
|
||||
<div class="app-pane-admin-border">
|
||||
@DynamicComponent
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@DynamicComponent
|
||||
}
|
||||
</CascadingValue>
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _useadminborder = false;
|
||||
private bool _visible = true;
|
||||
private bool _useadminborder = false;
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
@ -24,7 +30,12 @@
|
||||
|
||||
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;
|
||||
if (PageState.ModuleId != -1 && ModuleState.UseAdminContainer)
|
||||
@ -53,4 +64,21 @@
|
||||
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.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.UI
|
||||
{
|
||||
@ -293,5 +296,91 @@ namespace Oqtane.UI
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.UI
|
||||
@ -8,11 +10,8 @@ namespace Oqtane.UI
|
||||
{
|
||||
public Alias Alias { get; set; }
|
||||
public Site Site { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public List<Page> Pages { get; set; }
|
||||
public Page Page { get; set; }
|
||||
public User User { get; set; }
|
||||
public List<Module> Modules { get; set; }
|
||||
public Uri Uri { get; set; }
|
||||
public Dictionary<string, string> QueryString { get; set; }
|
||||
public string UrlParameters { get; set; }
|
||||
@ -20,8 +19,22 @@ namespace Oqtane.UI
|
||||
public string Action { get; set; }
|
||||
public bool EditMode { 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 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 {
|
||||
private bool _useadminborder = false;
|
||||
private string _panetitle = "";
|
||||
private bool _useadminborder = false;
|
||||
private string _panetitle = "";
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Name { get; set; }
|
||||
[Parameter]
|
||||
public string Name { get; set; }
|
||||
|
||||
RenderFragment DynamicComponent { get; set; }
|
||||
RenderFragment DynamicComponent { get; set; }
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
|
||||
{
|
||||
_useadminborder = true;
|
||||
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
_useadminborder = false;
|
||||
_panetitle = "";
|
||||
}
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
|
||||
{
|
||||
_useadminborder = true;
|
||||
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
_useadminborder = false;
|
||||
_panetitle = "";
|
||||
}
|
||||
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
|
||||
{
|
||||
if (Name.ToLower() == PaneNames.Admin.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);
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
|
||||
{
|
||||
// action route needs to inject module control into specific pane
|
||||
string pane = "";
|
||||
if (PageState.Page.Panes.FindIndex(item => item.Equals(PaneNames.Default, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||
{
|
||||
pane = PaneNames.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
pane = PaneNames.Admin;
|
||||
|
||||
}
|
||||
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
|
||||
{
|
||||
|
@ -3,7 +3,6 @@ namespace Oqtane.UI
|
||||
public enum Refresh
|
||||
{
|
||||
None,
|
||||
Page,
|
||||
Site,
|
||||
Application
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
@using System.Diagnostics.CodeAnalysis
|
||||
@using System.Net
|
||||
@namespace Oqtane.UI
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject SiteState SiteState
|
||||
@ -6,21 +7,25 @@
|
||||
@inject INavigationInterception NavigationInterception
|
||||
@inject ISyncService SyncService
|
||||
@inject ISiteService SiteService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject IPageService PageService
|
||||
@inject IUserService UserService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IUrlMappingService UrlMappingService
|
||||
@inject ILogService LogService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@implements IHandleAfterRender
|
||||
|
||||
@if (!string.IsNullOrEmpty(_error))
|
||||
{
|
||||
<ModuleMessage Message="@_error" Type="@MessageType.Warning" />
|
||||
}
|
||||
|
||||
@DynamicComponent
|
||||
|
||||
@code {
|
||||
private string _absoluteUri;
|
||||
private bool _navigationInterceptionEnabled;
|
||||
private PageState _pagestate;
|
||||
private string _error = "";
|
||||
|
||||
[Parameter]
|
||||
public string Runtime { get; set; }
|
||||
@ -71,20 +76,23 @@
|
||||
private async Task Refresh()
|
||||
{
|
||||
Site site;
|
||||
List<Language> languages;
|
||||
List<Page> pages;
|
||||
Page page;
|
||||
User user = null;
|
||||
List<Module> modules;
|
||||
var editmode = false;
|
||||
var refresh = UI.Refresh.None;
|
||||
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
||||
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
||||
_error = "";
|
||||
|
||||
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
||||
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
||||
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
||||
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
|
||||
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
||||
@ -117,6 +125,24 @@
|
||||
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
|
||||
var sync = await SyncService.GetSyncAsync(lastsyncdate);
|
||||
lastsyncdate = sync.SyncDate;
|
||||
@ -128,13 +154,19 @@
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
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))
|
||||
{
|
||||
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);
|
||||
refresh = UI.Refresh.Site;
|
||||
@ -146,99 +178,47 @@
|
||||
|
||||
if (site != null)
|
||||
{
|
||||
if (PageState == null || refresh == UI.Refresh.Site)
|
||||
if (PageState == null || refresh == UI.Refresh.Site || PageState.Page.Path != route.PagePath)
|
||||
{
|
||||
// get user
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
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)
|
||||
{
|
||||
languages = await LanguageService.GetLanguagesAsync(site.SiteId);
|
||||
pages = await PageService.GetPagesAsync(site.SiteId);
|
||||
pages = pages.Where(item => !item.IsDeleted).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
languages = PageState.Languages;
|
||||
pages = PageState.Pages;
|
||||
}
|
||||
|
||||
if (PageState == null || refresh == UI.Refresh.Site)
|
||||
{
|
||||
page = pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
||||
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
||||
editmode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
page = PageState.Page;
|
||||
}
|
||||
|
||||
// get the page if the path has changed
|
||||
if (page == null || page.Path != route.PagePath)
|
||||
if (page == null && route.PagePath == "") // naked path refers to site home page
|
||||
{
|
||||
page = pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
||||
// 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 == "")
|
||||
if (site.HomePageId != null)
|
||||
{
|
||||
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 (PageState == null)
|
||||
{
|
||||
editmode = false;
|
||||
}
|
||||
|
||||
// check if user is authorized to view page
|
||||
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.Permissions))
|
||||
{
|
||||
// load additional metadata for current page
|
||||
page = await ProcessPage(page, site, user);
|
||||
|
||||
if (PageState == null || refresh == UI.Refresh.Site)
|
||||
{
|
||||
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);
|
||||
// load additional metadata for modules
|
||||
(page, site.Modules) = ProcessModules(page, site.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
|
||||
{
|
||||
Alias = SiteState.Alias,
|
||||
Site = site,
|
||||
Languages = languages,
|
||||
Pages = pages,
|
||||
Page = page,
|
||||
User = user,
|
||||
Modules = modules,
|
||||
Uri = new Uri(_absoluteUri, UriKind.Absolute),
|
||||
QueryString = querystring,
|
||||
UrlParameters = route.UrlParameters,
|
||||
@ -248,7 +228,8 @@
|
||||
LastSyncDate = lastsyncdate,
|
||||
Runtime = runtime,
|
||||
VisitorId = VisitorId,
|
||||
RemoteIPAddress = SiteState.RemoteIPAddress
|
||||
RemoteIPAddress = SiteState.RemoteIPAddress,
|
||||
ReturnUrl = returnurl
|
||||
};
|
||||
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
@ -355,7 +336,7 @@
|
||||
page.Panes = new List<string>();
|
||||
page.Resources = new List<Resource>();
|
||||
|
||||
string panes = PaneNames.Admin;
|
||||
string panes = "";
|
||||
Type themetype = Type.GetType(page.ThemeType);
|
||||
if (themetype == null)
|
||||
{
|
||||
@ -375,7 +356,19 @@
|
||||
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
|
||||
{
|
||||
@ -467,10 +460,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ensure module's pane exists in current page and if not, assign it to the Admin pane
|
||||
if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
|
||||
// validate that module's pane exists in current page
|
||||
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
|
||||
|
@ -36,7 +36,8 @@
|
||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.4</Version>
|
||||
<Version>3.2.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
<Description>Modular Application Framework for Blazor</Description>
|
||||
<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.1.4</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>3.1.4</version>
|
||||
<version>3.2.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.4</Version>
|
||||
<Version>3.2.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
<Description>Modular Application Framework for Blazor</Description>
|
||||
<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.1.4</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>3.1.4</version>
|
||||
<version>3.2.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.4</Version>
|
||||
<Version>3.2.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
<Description>Modular Application Framework for Blazor</Description>
|
||||
<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.1.4</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>3.1.4</version>
|
||||
<version>3.2.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.1.4</Version>
|
||||
<Version>3.2.0</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
<Description>Modular Application Framework for Blazor</Description>
|
||||
<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.1.4</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>3.1.4</version>
|
||||
<version>3.2.0</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.1.4</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</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
|
||||
{
|
||||
}
|
15
Oqtane.Maui/Platforms/Android/MainApplication.cs
Normal file
15
Oqtane.Maui/Platforms/Android/MainApplication.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
[Application]
|
||||
public class MainApplication : MauiApplication
|
||||
{
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
|
||||
: base(handle, ownership)
|
||||
{
|
||||
}
|
||||
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#512BD4</color>
|
||||
<color name="colorPrimaryDark">#2B0B98</color>
|
||||
<color name="colorAccent">#2B0B98</color>
|
||||
</resources>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">10.0.2.2</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
9
Oqtane.Maui/Platforms/MacCatalyst/AppDelegate.cs
Normal file
9
Oqtane.Maui/Platforms/MacCatalyst/AppDelegate.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Foundation;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
30
Oqtane.Maui/Platforms/MacCatalyst/Info.plist
Normal file
30
Oqtane.Maui/Platforms/MacCatalyst/Info.plist
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
</dict>
|
||||
</plist>
|
15
Oqtane.Maui/Platforms/MacCatalyst/Program.cs
Normal file
15
Oqtane.Maui/Platforms/MacCatalyst/Program.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using ObjCRuntime;
|
||||
using UIKit;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
public class Program
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||
// you can specify it here.
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
16
Oqtane.Maui/Platforms/Tizen/Main.cs
Normal file
16
Oqtane.Maui/Platforms/Tizen/Main.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using Microsoft.Maui;
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
class Program : MauiApplication
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var app = new Program();
|
||||
app.Run(args);
|
||||
}
|
||||
}
|
15
Oqtane.Maui/Platforms/Tizen/tizen-manifest.xml
Normal file
15
Oqtane.Maui/Platforms/Tizen/tizen-manifest.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.companyname.oqtane.maui" version="1.0.0" api-version="7" xmlns="http://tizen.org/ns/packages">
|
||||
<profile name="common" />
|
||||
<ui-application appid="com.companyname.oqtane.maui" exec="Oqtane.Maui.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
|
||||
<label>MauiApp1</label>
|
||||
<icon>appicon.xhigh.png</icon>
|
||||
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
|
||||
</ui-application>
|
||||
<shortcut-list />
|
||||
<privileges>
|
||||
<privilege>http://tizen.org/privilege/internet</privilege>
|
||||
</privileges>
|
||||
<dependencies />
|
||||
<provides-appdefined-privileges />
|
||||
</manifest>
|
8
Oqtane.Maui/Platforms/Windows/App.xaml
Normal file
8
Oqtane.Maui/Platforms/Windows/App.xaml
Normal file
@ -0,0 +1,8 @@
|
||||
<maui:MauiWinUIApplication
|
||||
x:Class="Oqtane.Maui.WinUI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:maui="using:Microsoft.Maui"
|
||||
xmlns:local="using:Oqtane.Maui.WinUI">
|
||||
|
||||
</maui:MauiWinUIApplication>
|
24
Oqtane.Maui/Platforms/Windows/App.xaml.cs
Normal file
24
Oqtane.Maui/Platforms/Windows/App.xaml.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace Oqtane.Maui.WinUI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class App : MauiWinUIApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
||||
|
43
Oqtane.Maui/Platforms/Windows/Package.appxmanifest
Normal file
43
Oqtane.Maui/Platforms/Windows/Package.appxmanifest
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>$placeholder$</DisplayName>
|
||||
<PublisherDisplayName>User Name</PublisherDisplayName>
|
||||
<Logo>$placeholder$.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="$placeholder$"
|
||||
Description="$placeholder$"
|
||||
Square150x150Logo="$placeholder$.png"
|
||||
Square44x44Logo="$placeholder$.png"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
|
||||
<uap:SplashScreen Image="$placeholder$.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
|
||||
</Package>
|
15
Oqtane.Maui/Platforms/Windows/app.manifest
Normal file
15
Oqtane.Maui/Platforms/Windows/app.manifest
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="Oqtane.Maui.WinUI.app"/>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
9
Oqtane.Maui/Platforms/iOS/AppDelegate.cs
Normal file
9
Oqtane.Maui/Platforms/iOS/AppDelegate.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Foundation;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : MauiUIApplicationDelegate
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
32
Oqtane.Maui/Platforms/iOS/Info.plist
Normal file
32
Oqtane.Maui/Platforms/iOS/Info.plist
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
</dict>
|
||||
</plist>
|
15
Oqtane.Maui/Platforms/iOS/Program.cs
Normal file
15
Oqtane.Maui/Platforms/iOS/Program.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using ObjCRuntime;
|
||||
using UIKit;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
public class Program
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||
// you can specify it here.
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
8
Oqtane.Maui/Properties/launchSettings.json
Normal file
8
Oqtane.Maui/Properties/launchSettings.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Windows Machine": {
|
||||
"commandName": "MsixPackage",
|
||||
"nativeDebugging": false
|
||||
}
|
||||
}
|
||||
}
|
4
Oqtane.Maui/Resources/AppIcon/appicon.svg
Normal file
4
Oqtane.Maui/Resources/AppIcon/appicon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
|
||||
</svg>
|
After Width: | Height: | Size: 228 B |
8
Oqtane.Maui/Resources/AppIcon/appiconfg.svg
Normal file
8
Oqtane.Maui/Resources/AppIcon/appiconfg.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Oqtane.Maui/Resources/Fonts/OpenSans-Regular.ttf
Normal file
BIN
Oqtane.Maui/Resources/Fonts/OpenSans-Regular.ttf
Normal file
Binary file not shown.
93
Oqtane.Maui/Resources/Images/dotnet_bot.svg
Normal file
93
Oqtane.Maui/Resources/Images/dotnet_bot.svg
Normal file
@ -0,0 +1,93 @@
|
||||
<svg width="419" height="519" viewBox="0 0 419 519" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M284.432 247.568L284.004 221.881C316.359 221.335 340.356 211.735 355.308 193.336C382.408 159.996 372.893 108.183 372.786 107.659L398.013 102.831C398.505 105.432 409.797 167.017 375.237 209.53C355.276 234.093 324.719 246.894 284.432 247.568Z" fill="#8A6FE8"/>
|
||||
<path d="M331.954 109.36L361.826 134.245C367.145 138.676 375.055 137.959 379.497 132.639C383.928 127.32 383.211 119.41 377.891 114.969L348.019 90.0842C342.7 85.6531 334.79 86.3702 330.348 91.6896C325.917 97.0197 326.634 104.929 331.954 109.36Z" fill="#8A6FE8"/>
|
||||
<path d="M407.175 118.062L417.92 94.2263C420.735 87.858 417.856 80.4087 411.488 77.5831C405.12 74.7682 397.67 77.6473 394.845 84.0156L383.831 108.461L407.175 118.062Z" fill="#8A6FE8"/>
|
||||
<path d="M401.363 105.175L401.234 69.117C401.181 62.1493 395.498 56.541 388.53 56.5945C381.562 56.648 375.954 62.3313 376.007 69.2989L376.018 96.11L401.363 105.175Z" fill="#8A6FE8"/>
|
||||
<path d="M386.453 109.071L378.137 73.9548C376.543 67.169 369.757 62.9628 362.971 64.5575C356.185 66.1523 351.979 72.938 353.574 79.7237L362.04 115.482L386.453 109.071Z" fill="#8A6FE8"/>
|
||||
<path d="M381.776 142.261C396.359 142.261 408.181 130.44 408.181 115.857C408.181 101.274 396.359 89.4527 381.776 89.4527C367.194 89.4527 355.372 101.274 355.372 115.857C355.372 130.44 367.194 142.261 381.776 142.261Z" fill="url(#paint0_radial)"/>
|
||||
<path d="M248.267 406.979C248.513 384.727 245.345 339.561 222.376 301.736L199.922 315.372C220.76 349.675 222.323 389.715 221.841 407.182C221.798 408.627 235.263 409.933 248.267 406.979Z" fill="url(#paint1_linear)"/>
|
||||
<path d="M221.841 406.936L242.637 406.84L262.052 518.065L220.311 518.258C217.132 518.269 214.724 515.711 214.938 512.532L221.841 406.936Z" fill="#522CD5"/>
|
||||
<path d="M306.566 488.814C310.173 491.661 310.109 495.782 309.831 500.127L308.964 513.452C308.803 515.839 306.727 517.798 304.34 517.809L260.832 518.012C258.125 518.023 256.08 515.839 256.262 513.142L256.551 499.335C256.883 494.315 255.192 492.474 251.307 487.744C244.649 479.663 224.967 435.62 226.84 406.925L248.256 406.829C249.691 423.858 272.167 461.682 306.566 488.814Z" fill="url(#paint2_linear)"/>
|
||||
<path d="M309.82 500.127C310.023 497.088 310.077 494.176 308.889 491.715L254.635 491.961C256.134 494.166 256.765 496.092 256.562 499.314L256.273 513.121C256.091 515.828 258.146 518.012 260.843 517.99L304.34 517.798C306.727 517.787 308.803 515.828 308.964 513.442L309.82 500.127Z" fill="url(#paint3_radial)"/>
|
||||
<path d="M133.552 407.471C133.103 385.22 135.864 340.021 158.49 301.993L181.073 315.425C160.545 349.921 159.346 389.972 159.989 407.428C160.042 408.884 146.578 410.318 133.552 407.471Z" fill="url(#paint4_linear)"/>
|
||||
<path d="M110.798 497.152C110.765 494.187 111.204 491.575 112.457 487.23C131.882 434.132 133.52 407.364 133.52 407.364L159.999 407.246C159.999 407.246 161.819 433.512 181.716 486.427C183.289 490.195 183.471 493.641 183.674 496.831L183.792 513.816C183.803 516.374 181.716 518.483 179.158 518.494L177.873 518.504L116.781 518.782L115.496 518.793C112.927 518.804 110.83 516.728 110.819 514.159L110.798 497.152Z" fill="url(#paint5_linear)"/>
|
||||
<path d="M110.798 497.152C110.798 496.67 110.808 496.199 110.83 495.739C110.969 494.262 111.643 492.603 114.875 492.582L180.207 492.282C182.561 492.367 183.343 494.176 183.589 495.311C183.621 495.814 183.664 496.328 183.696 496.82L183.813 513.806C183.824 515.411 183.011 516.824 181.769 517.669C181.031 518.172 180.132 518.472 179.179 518.483L177.895 518.494L116.802 518.772L115.528 518.782C114.244 518.793 113.077 518.269 112.232 517.434C111.386 516.599 110.862 515.432 110.851 514.148L110.798 497.152Z" fill="url(#paint6_radial)"/>
|
||||
<path d="M314.979 246.348C324.162 210.407 318.008 181.777 318.008 181.777L326.452 181.734L326.656 181.574C314.262 115.75 256.326 66.0987 186.949 66.4198C108.796 66.773 45.7233 130.424 46.0765 208.577C46.4297 286.731 110.08 349.803 188.234 349.45C249.905 349.172 302.178 309.474 321.304 254.343C321.872 251.999 321.797 247.804 314.979 246.348Z" fill="url(#paint7_radial)"/>
|
||||
<path d="M310.237 279.035L65.877 280.148C71.3998 289.428 77.95 298.012 85.3672 305.761L290.972 304.829C298.336 297.005 304.8 288.368 310.237 279.035Z" fill="#D8CFF7"/>
|
||||
<path d="M235.062 312.794L280.924 312.585L280.74 272.021L234.877 272.23L235.062 312.794Z" fill="#512BD4"/>
|
||||
<path d="M243.001 297.626C242.691 297.626 242.434 297.53 242.22 297.327C242.006 297.123 241.899 296.866 241.899 296.588C241.899 296.299 242.006 296.042 242.22 295.839C242.434 295.625 242.691 295.528 243.001 295.528C243.312 295.528 243.568 295.635 243.782 295.839C243.996 296.042 244.114 296.299 244.114 296.588C244.114 296.877 244.007 297.123 243.793 297.327C243.568 297.519 243.312 297.626 243.001 297.626Z" fill="white"/>
|
||||
<path d="M255.192 297.434H253.212L247.967 289.203C247.839 289 247.721 288.775 247.636 288.55H247.593C247.636 288.786 247.657 289.299 247.657 290.091L247.668 297.444H245.912L245.891 286.228H247.999L253.062 294.265C253.276 294.597 253.415 294.833 253.479 294.95H253.511C253.458 294.651 253.437 294.148 253.437 293.441L253.426 286.217H255.17L255.192 297.434Z" fill="white"/>
|
||||
<path d="M263.733 297.412L257.589 297.423L257.568 286.206L263.465 286.195V287.779L259.387 287.79L259.398 290.969L263.155 290.958V292.532L259.398 292.542L259.409 295.86L263.733 295.85V297.412Z" fill="white"/>
|
||||
<path d="M272.445 287.758L269.298 287.769L269.32 297.401H267.5L267.479 287.769L264.343 287.779V286.195L272.434 286.174L272.445 287.758Z" fill="white"/>
|
||||
<path d="M315.279 246.337C324.355 210.836 318.457 182.483 318.308 181.798L171.484 182.462C171.484 182.462 162.226 181.563 162.268 190.018C162.311 198.463 162.761 222.341 162.878 248.746C162.9 254.172 167.363 256.773 170.863 256.751C170.874 256.751 311.618 252.213 315.279 246.337Z" fill="url(#paint8_radial)"/>
|
||||
<path d="M227.685 246.798C227.685 246.798 250.183 228.827 254.571 225.499C258.959 222.17 262.812 221.977 266.869 225.445C270.925 228.913 293.616 246.498 293.616 246.498L227.685 246.798Z" fill="#A08BE8"/>
|
||||
<path d="M320.748 256.141C320.748 256.141 324.943 248.414 315.279 246.348C315.289 246.305 170.927 246.894 170.927 246.894C167.566 246.905 163.232 244.925 162.846 241.671C162.857 244.004 162.878 246.369 162.889 248.756C162.91 253.68 166.582 256.27 169.878 256.698C170.21 256.73 170.542 256.773 170.874 256.773L180.742 256.73L320.748 256.141Z" fill="#512BD4"/>
|
||||
<path d="M206.4 233.214C212.511 233.095 217.302 224.667 217.102 214.39C216.901 204.112 211.785 195.878 205.674 195.997C199.563 196.116 194.772 204.544 194.973 214.821C195.173 225.099 200.289 233.333 206.4 233.214Z" fill="#512BD4"/>
|
||||
<path d="M306.249 214.267C306.356 203.989 301.488 195.605 295.377 195.541C289.266 195.478 284.225 203.758 284.118 214.037C284.011 224.315 288.878 232.699 294.99 232.763C301.101 232.826 306.142 224.545 306.249 214.267Z" fill="#512BD4"/>
|
||||
<path d="M205.905 205.291C208.152 203.022 211.192 202.016 214.157 202.262C215.912 205.495 217.014 209.733 217.111 214.389C217.164 217.3 216.811 220.04 216.158 222.513C212.669 223.519 208.752 222.662 205.979 219.922C201.912 215.909 201.88 209.348 205.905 205.291Z" fill="#8065E0"/>
|
||||
<path d="M294.996 204.285C297.255 202.016 300.294 200.999 303.259 201.256C305.164 204.628 306.309 209.209 306.256 214.239C306.224 216.808 305.892 219.259 305.303 221.485C301.793 222.523 297.843 221.678 295.061 218.916C291.004 214.892 290.972 208.342 294.996 204.285Z" fill="#8065E0"/>
|
||||
<path d="M11.6342 357.017C10.9171 354.716 -5.72611 300.141 21.3204 258.903C36.9468 235.078 63.3083 221.035 99.6664 217.15L102.449 243.276C74.3431 246.273 54.4676 256.345 43.3579 273.202C23.0971 303.941 36.5722 348.733 36.7113 349.183L11.6342 357.017Z" fill="url(#paint9_linear)"/>
|
||||
<path d="M95.1498 252.802C109.502 252.802 121.137 241.167 121.137 226.815C121.137 212.463 109.502 200.828 95.1498 200.828C80.7976 200.828 69.1628 212.463 69.1628 226.815C69.1628 241.167 80.7976 252.802 95.1498 252.802Z" fill="url(#paint10_radial)"/>
|
||||
<path d="M72.0098 334.434L33.4683 329.307C26.597 328.397 20.2929 333.214 19.3725 340.085C18.4627 346.956 23.279 353.26 30.1504 354.181L68.6919 359.308C75.5632 360.217 81.8673 355.401 82.7878 348.53C83.6975 341.658 78.8705 335.344 72.0098 334.434Z" fill="#8A6FE8"/>
|
||||
<path d="M3.73535 367.185L7.35297 393.076C8.36975 399.968 14.7702 404.731 21.6629 403.725C28.5556 402.708 33.3185 396.308 32.3124 389.415L28.5984 362.861L3.73535 367.185Z" fill="#8A6FE8"/>
|
||||
<path d="M15.5194 374.988L34.849 405.427C38.6058 411.292 46.4082 413.005 52.2735 409.248C58.1387 405.491 59.8512 397.689 56.0945 391.823L41.7953 369.144L15.5194 374.988Z" fill="#8A6FE8"/>
|
||||
<path d="M26.0511 363.739L51.8026 389.019C56.7688 393.911 64.7532 393.846 69.6445 388.88C74.5358 383.914 74.4715 375.929 69.516 371.038L43.2937 345.297L26.0511 363.739Z" fill="#8A6FE8"/>
|
||||
<path d="M26.4043 381.912C40.987 381.912 52.8086 370.091 52.8086 355.508C52.8086 340.925 40.987 329.104 26.4043 329.104C11.8216 329.104 0 340.925 0 355.508C0 370.091 11.8216 381.912 26.4043 381.912Z" fill="url(#paint11_radial)"/>
|
||||
<path d="M184.73 63.6308L157.819 66.5892L158.561 38.5412L177.888 36.4178L184.73 63.6308Z" fill="#8A6FE8"/>
|
||||
<path d="M170.018 41.647C180.455 39.521 187.193 29.3363 185.067 18.8988C182.941 8.46126 172.757 1.72345 162.319 3.84944C151.882 5.97543 145.144 16.1601 147.27 26.5976C149.396 37.0351 159.58 43.773 170.018 41.647Z" fill="#D8CFF7"/>
|
||||
<path d="M196.885 79.385C198.102 79.2464 198.948 78.091 198.684 76.8997C195.851 64.2818 183.923 55.5375 170.773 56.9926C157.622 58.4371 147.886 69.5735 147.865 82.4995C147.863 83.7232 148.949 84.6597 150.168 84.5316L196.885 79.385Z" fill="url(#paint12_radial)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(382.004 103.457) scale(26.4058)">
|
||||
<stop stop-color="#8065E0"/>
|
||||
<stop offset="1" stop-color="#512BD4"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint1_linear" x1="214.439" y1="303.482" x2="236.702" y2="409.505" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#522CD5"/>
|
||||
<stop offset="0.4397" stop-color="#8A6FE8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear" x1="231.673" y1="404.144" x2="297.805" y2="522.048" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#522CD5"/>
|
||||
<stop offset="0.4397" stop-color="#8A6FE8"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint3_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(280.957 469.555) rotate(-0.260742) scale(45.8326)">
|
||||
<stop offset="0.034" stop-color="#522CD5"/>
|
||||
<stop offset="0.9955" stop-color="#8A6FE8"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint4_linear" x1="166.061" y1="303.491" x2="144.763" y2="409.709" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#522CD5"/>
|
||||
<stop offset="0.4397" stop-color="#8A6FE8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear" x1="146.739" y1="407.302" x2="147.246" y2="518.627" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#522CD5"/>
|
||||
<stop offset="0.4397" stop-color="#8A6FE8"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint6_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(148.63 470.023) rotate(179.739) scale(50.2476)">
|
||||
<stop offset="0.034" stop-color="#522CD5"/>
|
||||
<stop offset="0.9955" stop-color="#8A6FE8"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint7_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(219.219 153.929) rotate(179.739) scale(140.935)">
|
||||
<stop offset="0.4744" stop-color="#A08BE8"/>
|
||||
<stop offset="0.8618" stop-color="#8065E0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint8_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(314.861 158.738) rotate(179.739) scale(146.053)">
|
||||
<stop offset="0.0933" stop-color="#E1DFDD"/>
|
||||
<stop offset="0.6573" stop-color="white"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint9_linear" x1="54.1846" y1="217.159" x2="54.1846" y2="357.022" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.3344" stop-color="#9780E6"/>
|
||||
<stop offset="0.8488" stop-color="#8A6FE8"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint10_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(90.3494 218.071) rotate(-0.260742) scale(25.9924)">
|
||||
<stop stop-color="#8065E0"/>
|
||||
<stop offset="1" stop-color="#512BD4"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint11_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(25.805 345.043) scale(26.4106)">
|
||||
<stop stop-color="#8065E0"/>
|
||||
<stop offset="1" stop-color="#512BD4"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint12_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(169.113 67.3662) rotate(-32.2025) scale(21.0773)">
|
||||
<stop stop-color="#8065E0"/>
|
||||
<stop offset="1" stop-color="#512BD4"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 12 KiB |
8
Oqtane.Maui/Resources/Splash/splash.svg
Normal file
8
Oqtane.Maui/Resources/Splash/splash.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
7
Oqtane.Maui/_Imports.razor
Normal file
7
Oqtane.Maui/_Imports.razor
Normal file
@ -0,0 +1,7 @@
|
||||
@using System.Net.Http
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Maui
|
215
Oqtane.Maui/wwwroot/css/app.css
Normal file
215
Oqtane.Maui/wwwroot/css/app.css
Normal file
@ -0,0 +1,215 @@
|
||||
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
|
||||
|
||||
html, body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
app {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Admin Modal */
|
||||
.app-admin-modal .modal {
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 9999; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background: rgba(0,0,0,0.3); /* Dim background */
|
||||
}
|
||||
|
||||
.app-admin-modal .modal-dialog {
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
max-width: none; /* Override default of 500px */
|
||||
}
|
||||
|
||||
.app-admin-modal .modal-content {
|
||||
margin: 5% auto; /* 5% from the top and centered */
|
||||
width: 80%; /* Could be more or less, depending on screen size */
|
||||
}
|
||||
|
||||
/* Action Dialog */
|
||||
.app-actiondialog .modal {
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 9999; /* Sit on top */
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
overflow: auto; /* Enable scroll if needed */
|
||||
background: rgba(0,0,0,0.3); /* Dim background */
|
||||
}
|
||||
|
||||
.app-actiondialog .modal-dialog {
|
||||
width: 100%; /* Full width */
|
||||
height: 100%; /* Full height */
|
||||
max-width: none; /* Override default of 500px */
|
||||
}
|
||||
|
||||
.app-actiondialog .modal-content {
|
||||
margin: 15% auto; /* 15% from the top and centered */
|
||||
width: 40%; /* Could be more or less, depending on screen size */
|
||||
}
|
||||
|
||||
/* Admin Pane */
|
||||
.app-pane-admin-border {
|
||||
width: 100%;
|
||||
border-width: 1px;
|
||||
border-style: dashed;
|
||||
border-color: gray;
|
||||
}
|
||||
|
||||
.app-pane-admin-title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.app-moduleactions .dropdown-submenu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app-moduleactions .dropdown-submenu > .dropdown-menu {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
margin-top: 0px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.app-progress-indicator {
|
||||
background: rgba(0,0,0,0.2) url('../loading.gif') no-repeat 50% 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999; /* Sit on top */
|
||||
}
|
||||
|
||||
.app-rule {
|
||||
width: 100%;
|
||||
color: gray;
|
||||
height: 1px;
|
||||
background-color: gray;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.app-link-unstyled, .app-link-unstyled:visited, .app-link-unstyled:hover, .app-link-unstyled:active, .app-link-unstyled:focus, .app-link-unstyled:active:hover {
|
||||
font-style: inherit;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
font-size: inherit;
|
||||
text-decoration: none;
|
||||
font-variant: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
font-family: inherit;
|
||||
border-radius: inherit;
|
||||
border: inherit;
|
||||
outline: inherit;
|
||||
box-shadow: inherit;
|
||||
padding: inherit;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
|
||||
.app-alert {
|
||||
padding: 20px;
|
||||
background-color: #f44336; /* red */
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.app-moduletitle a {
|
||||
scroll-margin-top: 7rem;
|
||||
}
|
||||
|
||||
/* Tooltips */
|
||||
.app-tooltip {
|
||||
cursor: help;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app-tooltip::before,
|
||||
.app-tooltip::after {
|
||||
left: 25%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
z-index: -100;
|
||||
}
|
||||
|
||||
.app-tooltip:hover::before,
|
||||
.app-tooltip:focus::before,
|
||||
.app-tooltip:hover::after,
|
||||
.app-tooltip:focus::after {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.app-tooltip::before {
|
||||
border-style: solid;
|
||||
border-width: 1em 0.75em 0 0.75em;
|
||||
border-color: #3E474F transparent transparent transparent;
|
||||
bottom: 100%;
|
||||
content: "";
|
||||
margin-left: -0.5em;
|
||||
transition: all .65s cubic-bezier(.84,-0.18,.31,1.26), opacity .65s .5s;
|
||||
transform: scale(.6) translateY(-90%);
|
||||
}
|
||||
|
||||
.app-tooltip:hover::before,
|
||||
.app-tooltip:focus::before {
|
||||
transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s;
|
||||
}
|
||||
|
||||
.app-tooltip::after {
|
||||
background: #3E474F;
|
||||
border-radius: .25em;
|
||||
bottom: 140%;
|
||||
color: #EDEFF0;
|
||||
content: attr(data-tip);
|
||||
margin-left: -8.75em;
|
||||
padding: 1em;
|
||||
transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s;
|
||||
transform: scale(.6) translateY(50%);
|
||||
width: 17.5em;
|
||||
}
|
||||
|
||||
.app-tooltip:hover::after,
|
||||
.app-tooltip:focus::after {
|
||||
transition: all .65s cubic-bezier(.84,-0.18,.31,1.26);
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.app-tooltip::after {
|
||||
font-size: .75em;
|
||||
margin-left: -5em;
|
||||
width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user