Compare commits
130 Commits
Author | SHA1 | Date | |
---|---|---|---|
3d692e4198 | |||
215ca9f755 | |||
7dbcb24f3d | |||
18b4bd62f7 | |||
20f3cf5327 | |||
c1d35e8af5 | |||
e1cd318f61 | |||
4da0952091 | |||
a60f75f0a9 | |||
3814009ad9 | |||
fe7bb00112 | |||
2356753dfc | |||
bd23ec268b | |||
1d5f23c3c7 | |||
f424bf53b3 | |||
efbdf0006e | |||
625b173ee4 | |||
0a8d9bc3d3 | |||
0d0909a461 | |||
ebb0a538f8 | |||
337a566617 | |||
fecee7a12b | |||
282ec99ce8 | |||
17a4985ebe | |||
13b17d91a9 | |||
5782005007 | |||
258f2dbe8f | |||
e7b35bd0c2 | |||
176bd229e6 | |||
c12f1251b0 | |||
0710736661 | |||
51ebe520f4 | |||
1ef566c824 | |||
96cf06fe8d | |||
38ebfdcd0c | |||
b5649e2a6f | |||
5fc6dadeae | |||
22cfec9276 | |||
0a82ec5f0a | |||
3bdd1f6c4f | |||
1e466dc1fe | |||
3b302d2f86 | |||
7f108eebf9 | |||
9d41fee2fe | |||
bef686f95a | |||
4bdcb974bd | |||
af042bd23c | |||
19e3cef7dd | |||
085ab4bfb4 | |||
f7bd03d051 | |||
b48d751717 | |||
92a4a1b210 | |||
6a57fcc04a | |||
749e11762f | |||
61817726c3 | |||
808354e969 | |||
d2f594ff4a | |||
fa18467cdd | |||
4c940d02ef | |||
04202a6b70 | |||
4483901270 | |||
f02d894697 | |||
2bc130856a | |||
aa3a4dca65 | |||
4f65931f51 | |||
93be61e483 | |||
cb7fe364bc | |||
9cdcb4b22c | |||
c49072f9b6 | |||
61df26b667 | |||
25c935f1db | |||
6b982bc0c7 | |||
caccc803d3 | |||
5be640632b | |||
40912f0ffd | |||
d518860a34 | |||
185617ed9e | |||
5ba96b627e | |||
edf955f4cd | |||
8d0b04668e | |||
a72e5e02da | |||
4740404c2e | |||
34253916ea | |||
1d77ba2694 | |||
765041c587 | |||
8c6bca7666 | |||
c1b1cef590 | |||
507f7804c3 | |||
453bacf9bd | |||
25778458c6 | |||
7a42646bed | |||
755b7034d2 | |||
122fcfd701 | |||
2c905eddc2 | |||
3e8eb9abb5 | |||
d2df3b2d9a | |||
2a0c983c2e | |||
1319432fde | |||
02e2aeb6d1 | |||
5e35edd976 | |||
4d63c6266c | |||
48f8d41993 | |||
0b41ccad83 | |||
f994afbc11 | |||
5dea783677 | |||
347ef9a1d0 | |||
739aa5311c | |||
805018286c | |||
45f2a47ba5 | |||
fe503b7bee | |||
6736570ee7 | |||
88c06eea6e | |||
13693ef9e5 | |||
3a54326e73 | |||
941c1c8a45 | |||
d328058075 | |||
9108eb5616 | |||
ea45044dc7 | |||
6b16808207 | |||
4071b285ce | |||
7bc80f0814 | |||
6b57202421 | |||
ca3edb6687 | |||
7428f87899 | |||
02481560a9 | |||
2d66063763 | |||
9158e24295 | |||
118e177756 | |||
88ac82a013 | |||
c025013ca8 |
@ -66,6 +66,12 @@
|
||||
|
||||
public override string Title => "File Management";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
|
@ -110,8 +110,13 @@
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
public override string Title => "Folder Management";
|
||||
public override string Title => "Folder Management";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
|
@ -11,7 +11,7 @@
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" ResourceKey="ViewJobs" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">Refresh</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">@Localizer["Refresh.Text"]</button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Manage" ResourceKey="Manage">
|
||||
<TabPanel Name="Manage" ResourceKey="Manage" Heading="Manage">
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -45,7 +45,7 @@ else
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host" Heading="Upload">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install." ResourceKey="LanguageUpload">Translation: </Label>
|
||||
|
@ -11,9 +11,6 @@
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<div>@Localizer["Info.SignedIn"]</div>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
@if (!twofactor)
|
||||
{
|
||||
@ -69,259 +66,265 @@
|
||||
</AuthorizeView>
|
||||
|
||||
@code {
|
||||
private bool _allowsitelogin = true;
|
||||
private bool _allowexternallogin = false;
|
||||
private ElementReference login;
|
||||
private bool validated = false;
|
||||
private bool twofactor = false;
|
||||
private string _username = string.Empty;
|
||||
private ElementReference username;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private bool _remember = false;
|
||||
private string _code = string.Empty;
|
||||
private bool _allowsitelogin = true;
|
||||
private bool _allowexternallogin = false;
|
||||
private ElementReference login;
|
||||
private bool validated = false;
|
||||
private bool twofactor = false;
|
||||
private string _username = string.Empty;
|
||||
private ElementReference username;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private bool _remember = false;
|
||||
private string _code = string.Empty;
|
||||
|
||||
private string _returnUrl = string.Empty;
|
||||
private string _returnUrl = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
};
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
|
||||
{
|
||||
_allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
|
||||
}
|
||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
|
||||
{
|
||||
_allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
|
||||
}
|
||||
|
||||
if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
|
||||
{
|
||||
_allowexternallogin = true;
|
||||
}
|
||||
if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
|
||||
{
|
||||
_allowexternallogin = true;
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
_returnUrl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
_returnUrl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("name"))
|
||||
{
|
||||
_username = PageState.QueryString["name"];
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("name"))
|
||||
{
|
||||
_username = PageState.QueryString["name"];
|
||||
}
|
||||
|
||||
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
||||
{
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _username;
|
||||
if (PageState.QueryString.ContainsKey("token") && !string.IsNullOrEmpty(_username))
|
||||
{
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _username;
|
||||
|
||||
if (PageState.QueryString.ContainsKey("key"))
|
||||
{
|
||||
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Success.Account.Linked"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError(LogFunction.Security, "External Login Linkage Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.Account.NotLinked"], MessageType.Warning);
|
||||
}
|
||||
_username = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("status"))
|
||||
{
|
||||
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Login {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.LoadLogin"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
if (PageState.QueryString.ContainsKey("key"))
|
||||
{
|
||||
user = await UserService.LinkUserAsync(user, PageState.QueryString["token"], PageState.Site.Settings["ExternalLogin:ProviderType"], PageState.QueryString["key"], PageState.Site.Settings["ExternalLogin:ProviderName"]);
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "External Login Linkage Successful For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Success.Account.Linked"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError(LogFunction.Security, "External Login Linkage Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.Account.NotLinked"], MessageType.Warning);
|
||||
}
|
||||
_username = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError(LogFunction.Security, "Email Verification Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.Account.NotVerified"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("status"))
|
||||
{
|
||||
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Login {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.LoadLogin"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender && PageState.User == null)
|
||||
{
|
||||
await username.FocusAsync();
|
||||
}
|
||||
}
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender && PageState.User == null)
|
||||
{
|
||||
await username.FocusAsync();
|
||||
}
|
||||
|
||||
private async Task Login()
|
||||
{
|
||||
try
|
||||
{
|
||||
validated = true;
|
||||
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, hybrid, _remember);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
||||
}
|
||||
// redirect logged in user to specified page
|
||||
if (PageState.User != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||
private async Task Login()
|
||||
{
|
||||
try
|
||||
{
|
||||
validated = true;
|
||||
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 (hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive login
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider
|
||||
.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_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
|
||||
{
|
||||
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||
{
|
||||
twofactor = true;
|
||||
validated = false;
|
||||
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!twofactor)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Performing Login {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Login"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
if (!twofactor)
|
||||
{
|
||||
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = await UserService.VerifyTwoFactorAsync(user, _code);
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(_returnUrl);
|
||||
}
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
|
||||
|
||||
private async Task Forgot()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_username != string.Empty)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
await UserService.ForgotPasswordAsync(user);
|
||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
||||
}
|
||||
if (hybrid)
|
||||
{
|
||||
// hybrid apps utilize an interactive login
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider
|
||||
.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_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
|
||||
{
|
||||
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired)
|
||||
{
|
||||
twofactor = true;
|
||||
validated = false;
|
||||
AddModuleMessage(Localizer["Message.TwoFactor"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!twofactor)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.UserInfo"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Performing Login {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Login"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Resetting Password {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ResetPassword"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo(_returnUrl);
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
twofactor = false;
|
||||
_username = "";
|
||||
_password = "";
|
||||
ClearModuleMessage();
|
||||
StateHasChanged();
|
||||
}
|
||||
private async Task Forgot()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_username != string.Empty)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(_username, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
await UserService.ForgotPasswordAsync(user);
|
||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Message.ForgotUser"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.UserDoesNotExist"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.ForgotPassword"], MessageType.Info);
|
||||
}
|
||||
|
||||
private async Task KeyPressed(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||
{
|
||||
await Login();
|
||||
}
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Resetting Password {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ResetPassword"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordtype == "password")
|
||||
{
|
||||
_passwordtype = "text";
|
||||
_togglepassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordtype = "password";
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
private void Reset()
|
||||
{
|
||||
twofactor = false;
|
||||
_username = "";
|
||||
_password = "";
|
||||
ClearModuleMessage();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void ExternalLogin()
|
||||
{
|
||||
private async Task KeyPressed(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Code == "Enter" || e.Code == "NumpadEnter")
|
||||
{
|
||||
await Login();
|
||||
}
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordtype == "password")
|
||||
{
|
||||
_passwordtype = "text";
|
||||
_togglepassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordtype = "password";
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private void ExternalLogin()
|
||||
{
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,65 +10,110 @@
|
||||
<TabStrip>
|
||||
<TabPanel Name="Download" ResourceKey="Download">
|
||||
<div class="row justify-content-center mb-3">
|
||||
<div class="col-sm-6">
|
||||
<div class="text-center">
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="free" class="form-check-input" type="radio" checked="@(_price == "free")" name="Price" @onchange="@(() => PriceChanged("free"))" />
|
||||
<label class="form-check-label" for="free">@SharedLocalizer["Free"]</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="paid" class="form-check-input" type="radio" checked="@(_price == "paid")" name="Price" @onchange="@(() => PriceChanged("paid"))" />
|
||||
<label class="form-check-label" for="paid">@SharedLocalizer["Paid"]</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center mb-3">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
|
||||
<option value="free">@SharedLocalizer["Free"]</option>
|
||||
<option value="paid">@SharedLocalizer["Paid"]</option>
|
||||
</select>
|
||||
<span class="input-group-text">@Localizer["Product"]</span>
|
||||
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
|
||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (_packages != null)
|
||||
{
|
||||
if (_packages.Count > 0)
|
||||
{
|
||||
<Pager Items="@_packages">
|
||||
<Row>
|
||||
<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))
|
||||
{
|
||||
<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>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||
}
|
||||
</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
else
|
||||
{
|
||||
<br />
|
||||
<div class="mx-auto text-center">
|
||||
@Localizer["Search.NoResults"]
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (_initialized)
|
||||
{
|
||||
<br />
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-4">
|
||||
<h3>@((_packages != null) ? _packages.Count : 0) @SharedLocalizer["Search.Results"]</h3>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<select class="form-select" value="@_sort" @onchange="(e => SortChanged(e))">
|
||||
<option value="popularity">@SharedLocalizer["Search.Popularity"]</option>
|
||||
<option value="alphabetical">@SharedLocalizer["Search.Alphabetical"]</option>
|
||||
<option value="downloads">@SharedLocalizer["Search.Downloads"]</option>
|
||||
<option value="recent">@SharedLocalizer["Search.RecentlyReleased"]</option>
|
||||
@if (_price == "paid")
|
||||
{
|
||||
<option value="price">@SharedLocalizer["Search.Price"]</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<Pager Format="Grid" Items="@_packages" DisplayPages="1" PageSize="9" Toolbar="Both" Class="container-fluid px-0" RowClass="row g-0" ColumnClass="col-lg-4 col-md-6">
|
||||
<Row>
|
||||
<div class="m-2 p-2 d-flex justify-content-center">
|
||||
<div class="container-fluid px-0">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
@if (context.LogoFileId != null)
|
||||
{
|
||||
<img src="@GetLogo(context.LogoFileId.Value)" class="img-fluid" alt="@context.Name" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<img src="/package.png" class="img-fluid" alt="@context.Name" />
|
||||
}
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
|
||||
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
|
||||
<br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong>
|
||||
@if (!string.IsNullOrEmpty(context.PackageUrl))
|
||||
{
|
||||
<br /><small>@SharedLocalizer["Search.Source"]:</small> <strong>@(new Uri(context.PackageUrl).Host)</strong>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_blank">@context.Name</a></h3><br />
|
||||
<small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
||||
{
|
||||
<small>@SharedLocalizer["Search.Price"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
|
||||
}
|
||||
<br />
|
||||
@if (!string.IsNullOrEmpty(context.PackageUrl))
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||
}
|
||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
||||
{
|
||||
<a class="btn btn-success ms-2" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@SharedLocalizer["Buy"]</a>
|
||||
}
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||
</TabPanel>
|
||||
<TabPanel Name="Upload" ResourceKey="Upload">
|
||||
<TabPanel Name="Upload" ResourceKey="Upload" Heading="Upload">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label>
|
||||
@ -116,8 +161,10 @@
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
private string _sort = "popularity";
|
||||
private string _search = "";
|
||||
private string _productname = "";
|
||||
private string _packageid = "";
|
||||
@ -131,6 +178,7 @@
|
||||
try
|
||||
{
|
||||
await LoadModuleDefinitions();
|
||||
_initialized = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -141,8 +189,10 @@
|
||||
|
||||
private async Task LoadModuleDefinitions()
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
|
||||
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
|
||||
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
|
||||
_packages = await PackageService.GetPackagesAsync("module", _search, _price, "", _sort);
|
||||
|
||||
if (_packages != null)
|
||||
{
|
||||
@ -154,21 +204,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HideProgressIndicator();
|
||||
}
|
||||
|
||||
private async void PriceChanged(ChangeEventArgs e)
|
||||
private string GetLogo(int fileid)
|
||||
{
|
||||
try
|
||||
{
|
||||
_price = (string)e.Value;
|
||||
_search = "";
|
||||
await LoadModuleDefinitions();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error On PriceChanged");
|
||||
}
|
||||
var url = ImageUrl(fileid, 100, 100);
|
||||
url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
|
||||
return Constants.PackageRegistryUrl + url;
|
||||
}
|
||||
|
||||
private async void PriceChanged(string price)
|
||||
{
|
||||
_price = price;
|
||||
await LoadModuleDefinitions();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task Search()
|
||||
@ -196,6 +247,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async void SortChanged(ChangeEventArgs e)
|
||||
{
|
||||
_sort = (string)e.Value;
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
|
||||
private void HideModal()
|
||||
{
|
||||
_productname = "";
|
||||
|
@ -80,7 +80,7 @@ else
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)SupportLink(context.PackageName))
|
||||
@((MarkupString)SupportLink(context.PackageName, context.Version))
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
@ -145,7 +145,7 @@ else
|
||||
return link;
|
||||
}
|
||||
|
||||
private string SupportLink(string packagename)
|
||||
private string SupportLink(string packagename, string version)
|
||||
{
|
||||
string link = "";
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
@ -153,7 +153,7 @@ else
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null && !string.IsNullOrEmpty(package.SupportUrl))
|
||||
{
|
||||
link += "<a class=\"btn btn-success\" style=\"text-decoration: none !important\" href=\"" + package.SupportUrl + "\" target=\"_new\">" + SharedLocalizer["Help"] + "</a>";
|
||||
link += "<a class=\"btn btn-info\" style=\"text-decoration: none !important\" href=\"" + package.SupportUrl.Replace("{Version}", version) + "\" target=\"_new\">" + SharedLocalizer["Help"] + "</a>";
|
||||
}
|
||||
}
|
||||
return link;
|
||||
|
@ -22,6 +22,11 @@
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Export Content";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
|
||||
private async Task ExportModule()
|
||||
{
|
||||
|
@ -28,6 +28,11 @@
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Import Content";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
private async Task ImportModule()
|
||||
{
|
||||
validated = true;
|
||||
|
@ -130,6 +130,11 @@
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_module = ModuleState.ModuleDefinition.Name;
|
||||
|
@ -10,7 +10,7 @@
|
||||
{
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<TabStrip Refresh="@_refresh">
|
||||
<TabPanel Name="Settings" ResourceKey="Settings">
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||
@ -126,7 +126,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||
<Section Name="Appearance" ResourceKey="Appearance" Heading=@Localizer["Appearance.Name"]>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||
@ -158,7 +158,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
||||
<Section Name="PageContent" ResourceKey="PageContent" Heading=@Localizer["PageContent.Heading"]>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||
@ -175,7 +175,7 @@
|
||||
</div>
|
||||
</Section>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading=@Localizer["Permissions.Heading"]>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
|
||||
@ -184,7 +184,7 @@
|
||||
</TabPanel>
|
||||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
<TabPanel Name="ThemeSettings" Heading=@Localizer["Theme.Heading"] ResourceKey="ThemeSettings">
|
||||
@ThemeSettingsComponent
|
||||
</TabPanel>
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
@if (_page.UserId == null)
|
||||
{
|
||||
<TabStrip Refresh="@_refresh">
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading=@Localizer["Settings.Heading"]>
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||
@ -137,7 +137,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Section Name="Appearance" ResourceKey="Appearance">
|
||||
<Section Name="Appearance" ResourceKey="Appearance" Heading="Appearance">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||
@ -189,7 +189,7 @@
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions">
|
||||
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading="Permissions">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||
@ -224,7 +224,7 @@
|
||||
else
|
||||
{
|
||||
<TabStrip Refresh="@_refresh">
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading=@Localizer["Settings.Heading"]>
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||
|
@ -14,7 +14,7 @@
|
||||
else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Pages" ResourceKey="Pages">
|
||||
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
|
||||
@if (!_pages.Where(item => item.IsDeleted).Any())
|
||||
{
|
||||
<br />
|
||||
@ -31,7 +31,7 @@ else
|
||||
<th>@Localizer["DeletedOn"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">Restore</button></td>
|
||||
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.DeletedBy</td>
|
||||
@ -42,7 +42,7 @@ else
|
||||
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Modules" ResourceKey="Modules">
|
||||
<TabPanel Name="Modules" ResourceKey="Modules" Heading="Modules">
|
||||
@if (!_modules.Where(item => item.IsDeleted).Any())
|
||||
{
|
||||
<br />
|
||||
|
@ -26,7 +26,7 @@
|
||||
<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>
|
||||
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (Page page in PageState.Pages)
|
||||
{
|
||||
if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
|
||||
@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<Section Name="Appearance" ResourceKey="Appearance">
|
||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
@if (_sites == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -10,61 +10,108 @@
|
||||
<TabStrip>
|
||||
<TabPanel Name="Download" ResourceKey="Download">
|
||||
<div class="row justify-content-center mb-3">
|
||||
<div class="col-sm-6">
|
||||
<div class="text-center">
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="free" class="form-check-input" type="radio" checked="@(_price == "free")" name="Price" @onchange="@(() => PriceChanged("free"))" />
|
||||
<label class="form-check-label" for="free">@SharedLocalizer["Free"]</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input id="paid" class="form-check-input" type="radio" checked="@(_price == "paid")" name="Price" @onchange="@(() => PriceChanged("paid"))" />
|
||||
<label class="form-check-label" for="paid">@SharedLocalizer["Paid"]</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center mb-3">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
|
||||
<option value="free">@SharedLocalizer["Free"]</option>
|
||||
<option value="paid">@SharedLocalizer["Paid"]</option>
|
||||
</select>
|
||||
<span class="input-group-text">Product</span>
|
||||
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
|
||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (_initialized)
|
||||
{
|
||||
<br />
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-4">
|
||||
<h3>@((_packages != null) ? _packages.Count : 0) @SharedLocalizer["Search.Results"]</h3>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<select class="form-select" value="@_sort" @onchange="(e => SortChanged(e))">
|
||||
<option value="popularity">@SharedLocalizer["Search.Popularity"]</option>
|
||||
<option value="alphabetical">@SharedLocalizer["Search.Alphabetical"]</option>
|
||||
<option value="downloads">@SharedLocalizer["Search.Downloads"]</option>
|
||||
<option value="recent">@SharedLocalizer["Search.RecentlyReleased"]</option>
|
||||
@if (_price == "paid")
|
||||
{
|
||||
<option value="price">@SharedLocalizer["Search.Price"]</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<Pager Format="Grid" Items="@_packages" DisplayPages="1" PageSize="9" Toolbar="Both" Class="container-fluid px-0" RowClass="row g-0" ColumnClass="col-lg-4 col-md-6">
|
||||
<Row>
|
||||
<div class="m-2 p-2 d-flex justify-content-center">
|
||||
<div class="container-fluid px-0">
|
||||
<div class="row g-0">
|
||||
<div class="col-6">
|
||||
@if (context.LogoFileId != null)
|
||||
{
|
||||
<img src="@GetLogo(context.LogoFileId.Value)" class="img-fluid" alt="@context.Name" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<img src="/package.png" class="img-fluid" alt="@context.Name" />
|
||||
}
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
|
||||
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
|
||||
<br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong>
|
||||
@if (!string.IsNullOrEmpty(context.PackageUrl))
|
||||
{
|
||||
<br />
|
||||
|
||||
@if (_packages != null)
|
||||
{
|
||||
if (_packages.Count > 0)
|
||||
{
|
||||
<Pager Items="@_packages">
|
||||
<Row>
|
||||
<td>
|
||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3> @SharedLocalizer["Search.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))
|
||||
{
|
||||
<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>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||
}
|
||||
</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
else
|
||||
{
|
||||
<br />
|
||||
<div class="mx-auto text-center">
|
||||
@Localizer["Search.NoResults"]
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<small>@SharedLocalizer["Search.Source"]:</small> <strong>@(new Uri(context.PackageUrl).Host)</strong>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<div class="col">
|
||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_blank">@context.Name</a></h3><br />
|
||||
<small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
||||
{
|
||||
<small>@SharedLocalizer["Search.Price"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
|
||||
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
|
||||
}
|
||||
<br />
|
||||
@if (!string.IsNullOrEmpty(context.PackageUrl))
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
||||
}
|
||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
||||
{
|
||||
<a class="btn btn-success ms-2" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@SharedLocalizer["Buy"]</a>
|
||||
}
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
||||
</TabPanel>
|
||||
@ -116,8 +163,10 @@
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private List<Package> _packages;
|
||||
private string _price = "free";
|
||||
private string _sort = "popularity";
|
||||
private string _search = "";
|
||||
private string _productname = "";
|
||||
private string _license = "";
|
||||
@ -131,6 +180,7 @@
|
||||
try
|
||||
{
|
||||
await LoadThemes();
|
||||
_initialized = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -141,8 +191,10 @@
|
||||
|
||||
private async Task LoadThemes()
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
_packages = await PackageService.GetPackagesAsync("theme", _search, _price, "");
|
||||
_packages = await PackageService.GetPackagesAsync("theme", _search, _price, "", _sort);
|
||||
|
||||
if (_packages != null)
|
||||
{
|
||||
@ -154,21 +206,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HideProgressIndicator();
|
||||
}
|
||||
|
||||
private async void PriceChanged(ChangeEventArgs e)
|
||||
private string GetLogo(int fileid)
|
||||
{
|
||||
try
|
||||
{
|
||||
_price = (string)e.Value;
|
||||
_search = "";
|
||||
await LoadThemes();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error On PriceChanged");
|
||||
}
|
||||
var url = ImageUrl(fileid, 100, 100);
|
||||
url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
|
||||
return Constants.PackageRegistryUrl + url;
|
||||
}
|
||||
|
||||
private async void PriceChanged(string price)
|
||||
{
|
||||
_price = price;
|
||||
await LoadThemes();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task Search()
|
||||
@ -196,6 +249,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async void SortChanged(ChangeEventArgs e)
|
||||
{
|
||||
_sort = (string)e.Value;
|
||||
await LoadThemes();
|
||||
}
|
||||
|
||||
private void HideModal()
|
||||
{
|
||||
_productname = "";
|
||||
|
@ -27,7 +27,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<Section Name="Information" ResourceKey="Information">
|
||||
<Section Name="Information" ResourceKey="Information" Heading="Information">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="themename" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
|
||||
|
@ -13,7 +13,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Install Theme" />
|
||||
<ActionLink Action="Add" Text="Install Theme" ResourceKey="InstallTheme" />
|
||||
@((MarkupString)" ")
|
||||
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" Class="btn btn-secondary" />
|
||||
|
||||
@ -49,7 +49,7 @@ else
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)SupportLink(context.PackageName))
|
||||
@((MarkupString)SupportLink(context.PackageName, context.Version))
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
@ -112,7 +112,7 @@ else
|
||||
return link;
|
||||
}
|
||||
|
||||
private string SupportLink(string packagename)
|
||||
private string SupportLink(string packagename, string version)
|
||||
{
|
||||
string link = "";
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
@ -120,7 +120,7 @@ else
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null && !string.IsNullOrEmpty(package.SupportUrl))
|
||||
{
|
||||
link += "<a class=\"btn btn-success\" style=\"text-decoration: none !important\" href=\"" + package.SupportUrl + "\" target=\"_new\">" + SharedLocalizer["Help"] + "</a>";
|
||||
link += "<a class=\"btn btn-info\" style=\"text-decoration: none !important\" href=\"" + package.SupportUrl.Replace("{Version}", version) + "\" target=\"_new\">" + SharedLocalizer["Help"] + "</a>";
|
||||
}
|
||||
}
|
||||
return link;
|
||||
|
@ -17,11 +17,11 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message="Framework Is Already Up To Date"></ModuleMessage>
|
||||
<ModuleMessage Type="MessageType.Info" Message=@Localizer["Message.Text"]></ModuleMessage>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Upload" ResourceKey="Upload">
|
||||
<ModuleMessage Type="MessageType.Info" Message="Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade"></ModuleMessage>
|
||||
<ModuleMessage Type="MessageType.Info" Message=@Localizer["MessgeUpgrade.Text"]></ModuleMessage>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label>
|
||||
|
@ -42,6 +42,12 @@
|
||||
|
||||
public override string Title => "Send Notification";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
|
||||
private async Task Send()
|
||||
{
|
||||
try
|
||||
|
@ -110,6 +110,11 @@
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
public override string Title => "View Notification";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
|
@ -140,34 +140,26 @@
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||
if (user == null)
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
|
||||
user = await UserService.AddUserAsync(user);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
|
||||
user = await UserService.AddUserAsync(user);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Created {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Adding User {Username} {Email}", username, email);
|
||||
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
|
||||
}
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Created {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Username.Exists"], MessageType.Warning);
|
||||
await logger.LogError("Error Adding User {Username} {Email}", username, email);
|
||||
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2,7 +2,7 @@
|
||||
@inherits LocalizableComponent
|
||||
|
||||
<div class="app-autocomplete">
|
||||
<input class="form-control" value="@Value" @oninput="OnInput" @onkeyup="OnKeyUp" placeholder="@Placeholder" autocomplete="off" />
|
||||
<input class="form-control" value="@Value" @oninput="OnInput" @onkeyup="OnKeyUp" placeholder="@Placeholder" autocomplete="off" @attributes="InputAttributes" />
|
||||
@if (_results != null)
|
||||
{
|
||||
<select class="form-select" style="position: relative;" value="@Value" size="@Rows" @onkeyup="OnKeyUp" @onchange="(e => OnChange(e))">
|
||||
@ -29,27 +29,48 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
Dictionary<string, string> _results;
|
||||
Dictionary<string, string> _results;
|
||||
Dictionary<string, object> InputAttributes { get; set; } = new();
|
||||
|
||||
[Parameter]
|
||||
public Func<string, Task<Dictionary<string, string>>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
|
||||
[Parameter]
|
||||
public Func<string, Task<Dictionary<string, string>>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
|
||||
|
||||
[Parameter]
|
||||
public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
|
||||
[Parameter]
|
||||
public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
|
||||
|
||||
[Parameter]
|
||||
public int Rows { get; set; } = 3; // optional - number of result rows to display
|
||||
[Parameter]
|
||||
public int Rows { get; set; } = 3; // optional - number of result rows to display
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } // optional - placeholder input text
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } // optional - placeholder input text
|
||||
|
||||
[Parameter]
|
||||
public string Value { get; set; } // value of item selected
|
||||
[Parameter]
|
||||
public string Value { get; set; } // value of item selected
|
||||
|
||||
[Parameter]
|
||||
public string Key { get; set; } // key of item selected
|
||||
[Parameter]
|
||||
public string Key { get; set; } // key of item selected
|
||||
|
||||
private async Task OnInput(ChangeEventArgs e)
|
||||
[Parameter]
|
||||
public bool Required { get; set; } // optional - if the item is required
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (Required)
|
||||
{
|
||||
if (!InputAttributes.ContainsKey(nameof(Required)))
|
||||
{
|
||||
InputAttributes.Add(nameof(Required), true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (InputAttributes.ContainsKey(nameof(Required)))
|
||||
{
|
||||
InputAttributes.Remove(nameof(Required));
|
||||
}
|
||||
}
|
||||
}
|
||||
private async Task OnInput(ChangeEventArgs e)
|
||||
{
|
||||
Value = e.Value?.ToString();
|
||||
if (Value?.Length >= Characters)
|
||||
|
@ -168,8 +168,6 @@
|
||||
ShowSuccess = true;
|
||||
}
|
||||
|
||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||
|
||||
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
|
||||
{
|
||||
Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
|
||||
@ -185,6 +183,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (ShowFolders)
|
||||
{
|
||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FolderId != -1)
|
||||
{
|
||||
var folder = await FolderService.GetFolderAsync(FolderId);
|
||||
if (folder != null)
|
||||
{
|
||||
_folders = new List<Folder> { folder };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FileId != -1)
|
||||
{
|
||||
File file = await FileService.GetFileAsync(FileId);
|
||||
@ -358,10 +372,21 @@
|
||||
attempts += 1;
|
||||
Thread.Sleep(1000 * attempts); // progressive retry
|
||||
|
||||
var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]);
|
||||
if (file != null)
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
success = true;
|
||||
var files = await FileService.GetFilesAsync(folder);
|
||||
if (files != null && files.Any(item => item.Name == uploads[upload]))
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]);
|
||||
if (file != null)
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
|
@ -42,6 +42,8 @@
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet(); // must be included to call method in LocalizableComponent
|
||||
|
||||
_heading = !string.IsNullOrEmpty(Heading) ? Localize(nameof(Heading), Heading) : Localize(nameof(Name), Name);
|
||||
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded.ToLower() : "false";
|
||||
if (_expanded == "true") { _show = "show"; }
|
||||
|
@ -68,6 +68,12 @@
|
||||
private List<Models.HtmlText> _htmltexts;
|
||||
private string _view = "";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -12,7 +12,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -1,19 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.UI;
|
||||
@ -65,6 +69,14 @@ namespace Oqtane.Client
|
||||
|
||||
private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider)
|
||||
{
|
||||
// get alias
|
||||
var navigationManager = serviceProvider.GetRequiredService<NavigationManager>();
|
||||
var urlpath = GetUrlPath(navigationManager.Uri);
|
||||
var json = await http.GetStringAsync($"api/Installation/installed/?path={WebUtility.UrlEncode(urlpath)}");
|
||||
var installation = JsonSerializer.Deserialize<Installation>(json, new JsonSerializerOptions(JsonSerializerDefaults.Web));
|
||||
urlpath = installation.Alias.Path;
|
||||
urlpath = (!string.IsNullOrEmpty(urlpath)) ? urlpath + "/" : urlpath;
|
||||
|
||||
var dlls = new Dictionary<string, byte[]>();
|
||||
var pdbs = new Dictionary<string, byte[]>();
|
||||
var list = new List<string>();
|
||||
@ -76,7 +88,7 @@ namespace Oqtane.Client
|
||||
if (files.Count() != 0)
|
||||
{
|
||||
// get list of assemblies from server
|
||||
var json = await http.GetStringAsync("/api/Installation/list");
|
||||
json = await http.GetStringAsync($"{urlpath}api/Installation/list");
|
||||
var assemblies = JsonSerializer.Deserialize<List<string>>(json);
|
||||
|
||||
// determine which assemblies need to be downloaded
|
||||
@ -138,7 +150,7 @@ namespace Oqtane.Client
|
||||
if (list.Count != 0)
|
||||
{
|
||||
// get assemblies from server and load into client app domain
|
||||
var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", list));
|
||||
var zip = await http.GetByteArrayAsync($"{urlpath}api/Installation/load?list=" + string.Join(",", list));
|
||||
|
||||
// asemblies and debug symbols are packaged in a zip file
|
||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||
@ -254,5 +266,10 @@ namespace Oqtane.Client
|
||||
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
|
||||
}
|
||||
|
||||
private static string GetUrlPath(string url)
|
||||
{
|
||||
return new Uri(url).AbsolutePath.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,4 +162,7 @@
|
||||
<data name="Name.Text" xml:space="preserve">
|
||||
<value>Name:</value>
|
||||
</data>
|
||||
<data name="DownloadFiles.Heading" xml:space="preserve">
|
||||
<value>Download Files</value>
|
||||
</data>
|
||||
</root>
|
@ -150,4 +150,7 @@
|
||||
<data name="Description.Text" xml:space="preserve">
|
||||
<value>Description:</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>File Management</value>
|
||||
</data>
|
||||
</root>
|
@ -183,4 +183,16 @@
|
||||
<data name="ImageSizes.Text" xml:space="preserve">
|
||||
<value>Image Sizes:</value>
|
||||
</data>
|
||||
<data name="FolderManagement.Title" xml:space="preserve">
|
||||
<value>Folder Management!</value>
|
||||
</data>
|
||||
<data name="Private" xml:space="preserve">
|
||||
<value>Private</value>
|
||||
</data>
|
||||
<data name="Public" xml:space="preserve">
|
||||
<value>Public</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>Folder Management</value>
|
||||
</data>
|
||||
</root>
|
@ -195,4 +195,7 @@
|
||||
<data name="Message.NoJobs" xml:space="preserve">
|
||||
<value>Please Note That After An Initial Installation You Must <a href={0}>Restart</a> The Application In Order To Activate The Default Scheduled Jobs.</value>
|
||||
</data>
|
||||
<data name="Refresh.Text" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
</root>
|
@ -136,9 +136,12 @@
|
||||
<value>No Modules Match The Criteria Provided Or Package Service Is Disabled</value>
|
||||
</data>
|
||||
<data name="Download.Heading" xml:space="preserve">
|
||||
<value>Download</value>
|
||||
<value>Marketplace</value>
|
||||
</data>
|
||||
<data name="Upload.Heading" xml:space="preserve">
|
||||
<value>Upload</value>
|
||||
</data>
|
||||
<data name="Product.Text" xml:space="preserve">
|
||||
<value>Product</value>
|
||||
</data>
|
||||
</root>
|
@ -144,6 +144,9 @@
|
||||
<data name="DeleteModule.Header" xml:space="preserve">
|
||||
<value>Delete Module</value>
|
||||
</data>
|
||||
<data name="DeleteModule.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="InUse" xml:space="preserve">
|
||||
<value>In Use?</value>
|
||||
</data>
|
||||
|
@ -132,4 +132,7 @@
|
||||
<data name="Success.Content.Export" xml:space="preserve">
|
||||
<value>Content Exported Successfully</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>Export Content</value>
|
||||
</data>
|
||||
</root>
|
@ -138,4 +138,7 @@
|
||||
<data name="Message.Required.ImportContent" xml:space="preserve">
|
||||
<value>You Must Enter Some Content To Import</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>Import Content</value>
|
||||
</data>
|
||||
</root>
|
@ -156,4 +156,7 @@
|
||||
<data name="Module.Text" xml:space="preserve">
|
||||
<value>Module:</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>Module Settings</value>
|
||||
</data>
|
||||
</root>
|
@ -249,4 +249,10 @@
|
||||
<data name="ThemeChanged.Message" xml:space="preserve">
|
||||
<value>Please Note That Overriding The Default Site Theme With An Unrelated Page Theme May Result In Compatibility Issues For Your Site</value>
|
||||
</data>
|
||||
<data name="Permissions.Heading" xml:space="preserve">
|
||||
<value>Permissions</value>
|
||||
</data>
|
||||
<data name="Theme.Heading" xml:space="preserve">
|
||||
<value>Theme Settings</value>
|
||||
</data>
|
||||
</root>
|
@ -120,9 +120,15 @@
|
||||
<data name="DeleteModule.Header" xml:space="preserve">
|
||||
<value>Delete Module</value>
|
||||
</data>
|
||||
<data name="DeleteModule.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="DeletePage.Header" xml:space="preserve">
|
||||
<value>Delete Page</value>
|
||||
</data>
|
||||
<data name="DeletePage.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="NoPage.Deleted" xml:space="preserve">
|
||||
<value>No Deleted Pages</value>
|
||||
</data>
|
||||
|
@ -135,4 +135,10 @@
|
||||
<data name="Search.NoResults" xml:space="preserve">
|
||||
<value>No Themes Match The Criteria Provided Or Package Service Is Disabled</value>
|
||||
</data>
|
||||
<data name="Download.Heading" xml:space="preserve">
|
||||
<value>Marketplace</value>
|
||||
</data>
|
||||
<data name="Upload.Heading" xml:space="preserve">
|
||||
<value>Upload</value>
|
||||
</data>
|
||||
</root>
|
@ -141,6 +141,9 @@
|
||||
<data name="CreateTheme.Text" xml:space="preserve">
|
||||
<value>Create Theme</value>
|
||||
</data>
|
||||
<data name="InstallTheme.Text" xml:space="preserve">
|
||||
<value>Install Theme</value>
|
||||
</data>
|
||||
<data name="ViewTheme.Text" xml:space="preserve">
|
||||
<value>View</value>
|
||||
</data>
|
||||
|
@ -141,4 +141,10 @@
|
||||
<data name="Upload.Heading" xml:space="preserve">
|
||||
<value>Upload</value>
|
||||
</data>
|
||||
<data name="Message.Text" xml:space="preserve">
|
||||
<value>Framework Is Already Up To Date</value>
|
||||
</data>
|
||||
<data name="MessgeUpgrade.Text" xml:space="preserve">
|
||||
<value>Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade</value>
|
||||
</data>
|
||||
</root>
|
@ -141,4 +141,7 @@
|
||||
<data name="Subject.Text" xml:space="preserve">
|
||||
<value>Subject: </value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>Send Notification</value>
|
||||
</data>
|
||||
</root>
|
@ -144,4 +144,7 @@
|
||||
<data name="OriginalMessage" xml:space="preserve">
|
||||
<value>Original Message</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>View Notification</value>
|
||||
</data>
|
||||
</root>
|
@ -156,6 +156,9 @@
|
||||
<data name="Message.Content.Restored" xml:space="preserve">
|
||||
<value>Version Restored</value>
|
||||
</data>
|
||||
<data name="ModuleTitle.Text" xml:space="preserve">
|
||||
<value>Edit Html/Text</value>
|
||||
</data>
|
||||
<data name="Restore.Header" xml:space="preserve">
|
||||
<value>Restore Version</value>
|
||||
</data>
|
||||
|
@ -223,13 +223,13 @@
|
||||
<value>by</value>
|
||||
</data>
|
||||
<data name="Search.Downloads" xml:space="preserve">
|
||||
<value>downloads</value>
|
||||
<value>Downloads</value>
|
||||
</data>
|
||||
<data name="Search.Released" xml:space="preserve">
|
||||
<value>released</value>
|
||||
<value>Released</value>
|
||||
</data>
|
||||
<data name="Search.Version" xml:space="preserve">
|
||||
<value>version</value>
|
||||
<value>Version</value>
|
||||
</data>
|
||||
<data name="Edit" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
@ -277,19 +277,19 @@
|
||||
<value>Installed Version</value>
|
||||
</data>
|
||||
<data name="Search.Source" xml:space="preserve">
|
||||
<value>source</value>
|
||||
<value>Source</value>
|
||||
</data>
|
||||
<data name="Message.InfoRequired" xml:space="preserve">
|
||||
<value>Please Provide All Required Information</value>
|
||||
</data>
|
||||
<data name="Free" xml:space="preserve">
|
||||
<value>Free</value>
|
||||
<value>Open Source</value>
|
||||
</data>
|
||||
<data name="Paid" xml:space="preserve">
|
||||
<value>Paid</value>
|
||||
<value>Commercial</value>
|
||||
</data>
|
||||
<data name="Search.Price" xml:space="preserve">
|
||||
<value>price</value>
|
||||
<value>Price</value>
|
||||
</data>
|
||||
<data name="Accept" xml:space="preserve">
|
||||
<value>Accept</value>
|
||||
@ -390,4 +390,19 @@
|
||||
<data name="Support" xml:space="preserve">
|
||||
<value>Support</value>
|
||||
</data>
|
||||
<data name="Search.Alphabetical" xml:space="preserve">
|
||||
<value>Alphabetical</value>
|
||||
</data>
|
||||
<data name="Buy" xml:space="preserve">
|
||||
<value>Buy Now</value>
|
||||
</data>
|
||||
<data name="Search.Popularity" xml:space="preserve">
|
||||
<value>Popularity</value>
|
||||
</data>
|
||||
<data name="Search.Results" xml:space="preserve">
|
||||
<value>Results</value>
|
||||
</data>
|
||||
<data name="Search.RecentlyReleased" xml:space="preserve">
|
||||
<value>Recently Released</value>
|
||||
</data>
|
||||
</root>
|
@ -22,7 +22,7 @@ namespace Oqtane.Services
|
||||
/// <inheritdoc />
|
||||
public async Task<List<Alias>> GetAliasesAsync()
|
||||
{
|
||||
List<Alias> aliases = await GetJsonAsync<List<Alias>>(ApiUrl);
|
||||
List<Alias> aliases = await GetJsonAsync<List<Alias>>(ApiUrl, Enumerable.Empty<Alias>().ToList());
|
||||
return aliases.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ using Oqtane.Shared;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
@ -14,11 +15,13 @@ namespace Oqtane.Services
|
||||
{
|
||||
private readonly NavigationManager _navigationManager;
|
||||
private readonly SiteState _siteState;
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public InstallationService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http, siteState)
|
||||
{
|
||||
_navigationManager = navigationManager;
|
||||
_siteState = siteState;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private string ApiUrl => (_siteState.Alias == null)
|
||||
@ -27,7 +30,15 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<Installation> IsInstalled()
|
||||
{
|
||||
var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
|
||||
var path = "";
|
||||
if (_http.DefaultRequestHeaders.UserAgent.ToString().Contains(Constants.MauiUserAgent))
|
||||
{
|
||||
path = _http.DefaultRequestHeaders.GetValues(Constants.MauiAliasPath).First();
|
||||
}
|
||||
else
|
||||
{
|
||||
path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
|
||||
}
|
||||
return await GetJsonAsync<Installation>($"{ApiUrl}/installed/?path={WebUtility.UrlEncode(path)}");
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,6 @@ namespace Oqtane.Services
|
||||
/// </summary>
|
||||
/// <param name="lastSyncDate"></param>
|
||||
/// <returns></returns>
|
||||
Task<Sync> GetSyncAsync(DateTime lastSyncDate);
|
||||
Task<Sync> GetSyncEventsAsync(DateTime lastSyncDate);
|
||||
}
|
||||
}
|
||||
|
@ -145,10 +145,19 @@ namespace Oqtane.Services
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<T>();
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
protected async Task<T> GetJsonAsync<T>(string uri, T defaultResult)
|
||||
{
|
||||
var response = await GetHttpClient().GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
|
||||
if (await CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<T>();
|
||||
}
|
||||
return defaultResult;
|
||||
}
|
||||
|
||||
protected async Task PutAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().PutAsync(uri, null);
|
||||
@ -202,17 +211,27 @@ namespace Oqtane.Services
|
||||
|
||||
private async Task<bool> CheckResponse(HttpResponseMessage response, string uri)
|
||||
{
|
||||
if (response.IsSuccessStatusCode && uri.Contains("/api/") && !response.RequestMessage.RequestUri.AbsolutePath.Contains("/api/"))
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Not Mapped To An API Controller Method", uri);
|
||||
// if response from api call is not from an api url then the route was not mapped correctly
|
||||
if (uri.Contains("/api/") && !response.RequestMessage.RequestUri.AbsolutePath.Contains("/api/"))
|
||||
{
|
||||
await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Not Mapped To An API Controller Method", uri);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Failed With Status {StatusCode} - {ReasonPhrase}", uri, response.StatusCode, response.ReasonPhrase);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (response.IsSuccessStatusCode) return true;
|
||||
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Failed With Status {StatusCode} - {ReasonPhrase}", uri, response.StatusCode, response.ReasonPhrase);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ValidateJsonContent(HttpContent content)
|
||||
|
@ -16,7 +16,7 @@ namespace Oqtane.Services
|
||||
private string ApiUrl => CreateApiUrl("Sync");
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Sync> GetSyncAsync(DateTime lastSyncDate)
|
||||
public async Task<Sync> GetSyncEventsAsync(DateTime lastSyncDate)
|
||||
{
|
||||
return await GetJsonAsync<Sync>($"{ApiUrl}/{lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ContainerBase
|
||||
@attribute [OqtaneIgnore]
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<span class="app-moduletitle">
|
||||
@((MarkupString)title)
|
||||
@ -23,7 +24,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
title = ModuleState.Title;
|
||||
title = SharedLocalizer[ModuleState.Title];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,12 +93,12 @@
|
||||
[SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")]
|
||||
private async Task Refresh()
|
||||
{
|
||||
Site site;
|
||||
Page page;
|
||||
Site site = null;
|
||||
Page page = null;
|
||||
User user = null;
|
||||
var editmode = false;
|
||||
var refresh = false;
|
||||
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
||||
var lastsyncdate = DateTime.MinValue;
|
||||
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
||||
_error = "";
|
||||
|
||||
@ -112,8 +112,8 @@
|
||||
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)))
|
||||
// reload the client application from the server if there is a forced reload
|
||||
if (querystring.ContainsKey("reload"))
|
||||
{
|
||||
if (querystring.ContainsKey("reload") && querystring["reload"] == "post")
|
||||
{
|
||||
@ -126,7 +126,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
|
||||
NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", "").Replace("&reload", ""), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -163,31 +163,39 @@
|
||||
else
|
||||
{
|
||||
user = PageState.User;
|
||||
}
|
||||
}
|
||||
|
||||
// process any sync events
|
||||
var sync = await SyncService.GetSyncAsync(lastsyncdate);
|
||||
var sync = await SyncService.GetSyncEventsAsync(lastsyncdate);
|
||||
lastsyncdate = sync.SyncDate;
|
||||
if (sync.SyncEvents.Any())
|
||||
{
|
||||
// reload client application if server was restarted or site runtime/rendermode was modified
|
||||
if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
|
||||
// reload client application if server was restarted
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Host))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// when site information has changed the PageState needs to be refreshed
|
||||
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||
// reload client application if site runtime/rendermode was modified
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||
{
|
||||
refresh = true;
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// when user information has changed the PageState 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))
|
||||
// reload client application if current user auth information has changed
|
||||
if (user != null && sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.User && item.EntityId == user.UserId))
|
||||
{
|
||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||
return;
|
||||
}
|
||||
// refresh PageState when site information has changed
|
||||
if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Refresh && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||
{
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
// get site
|
||||
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||
{
|
||||
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
|
||||
|
@ -70,7 +70,7 @@
|
||||
var elements = (">" + content.Replace("\n", "") + "<").Split("><");
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(element) && !element.Contains("script"))
|
||||
if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("script"))
|
||||
{
|
||||
if (!headcontent.Contains("<" + element + ">"))
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</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>4.0.1</version>
|
||||
<version>4.0.2</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</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>4.0.1</version>
|
||||
<version>4.0.2</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</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>4.0.1</version>
|
||||
<version>4.0.2</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</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>4.0.1</version>
|
||||
<version>4.0.2</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -1,18 +1,52 @@
|
||||
<DynamicComponent Type="@ComponentType" Parameters="@Parameters"></DynamicComponent>
|
||||
@using System.Text.Json;
|
||||
@using System.Text.Json.Nodes;
|
||||
|
||||
@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", ""));
|
||||
}
|
||||
@if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
<DynamicComponent Type="@ComponentType" Parameters="@Parameters"></DynamicComponent>
|
||||
}
|
||||
else
|
||||
{
|
||||
<br /><br /><center>@message</center>
|
||||
}
|
||||
|
||||
@code {
|
||||
Type ComponentType = Type.GetType("Oqtane.App, Oqtane.Client");
|
||||
private IDictionary<string, object> Parameters { get; set; }
|
||||
private string message = "";
|
||||
|
||||
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", ""));
|
||||
|
||||
if (MauiConstants.UseAppSettings)
|
||||
{
|
||||
string file = Path.Combine(FileSystem.Current.AppDataDirectory, "appsettings.json");
|
||||
if (File.Exists(file))
|
||||
{
|
||||
using FileStream stream = File.OpenRead(file);
|
||||
using StreamReader reader = new StreamReader(stream);
|
||||
var content = reader.ReadToEnd();
|
||||
var obj = JsonSerializer.Deserialize<JsonObject>(content)!;
|
||||
if (string.IsNullOrEmpty((string)obj["Url"]) && string.IsNullOrEmpty(MauiConstants.ApiUrl))
|
||||
{
|
||||
message = "You Must Set The Url In Either MauiConstants.cs Or " + file;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(MauiConstants.ApiUrl))
|
||||
{
|
||||
message = "You Must Set The Url In MauiConstants.cs";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
13
Oqtane.Maui/MauiConstants.cs
Normal file
13
Oqtane.Maui/MauiConstants.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
public static class MauiConstants
|
||||
{
|
||||
// the API service url (used as fallback if not set in appsettings.json)
|
||||
public static string ApiUrl = "";
|
||||
//public static string ApiUrl = "http://localhost:44357/"; // for local development (Oqtane.Server must be already running for MAUI client to connect)
|
||||
//public static string apiurl = "http://localhost:44357/sitename/"; // local microsite example
|
||||
//public static string apiurl = "https://www.dnfprojects.com/"; // for testing remote site
|
||||
|
||||
// specify if you wish to allow users to override the url via appsettings.json in the AppDataDirectory
|
||||
public static bool UseAppSettings = true;
|
||||
}
|
@ -6,20 +6,16 @@ using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Oqtane.Maui;
|
||||
|
||||
public static class MauiProgram
|
||||
{
|
||||
// the API service url
|
||||
//static string apiurl = "https://www.dnfprojects.com"; // for testing
|
||||
static string apiurl = "http://localhost:44357"; // for local development (Oqtane.Server must be already running for MAUI client to connect)
|
||||
|
||||
public static MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder
|
||||
.UseMauiApp<App>()
|
||||
builder.UseMauiApp<App>()
|
||||
.ConfigureFonts(fonts =>
|
||||
{
|
||||
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||
@ -28,15 +24,21 @@ public static class MauiProgram
|
||||
builder.Services.AddMauiBlazorWebView();
|
||||
#if DEBUG
|
||||
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||
#endif
|
||||
#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
|
||||
var apiurl = LoadAppSettings();
|
||||
|
||||
// dynamically load client assemblies
|
||||
LoadClientAssemblies(httpClient);
|
||||
if (!string.IsNullOrEmpty(apiurl))
|
||||
{
|
||||
var httpClient = new HttpClient { BaseAddress = new Uri(GetBaseUrl(apiurl)) };
|
||||
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Shared.Constants.MauiUserAgent);
|
||||
httpClient.DefaultRequestHeaders.Add(Shared.Constants.MauiAliasPath, GetUrlPath(apiurl).Replace("/", ""));
|
||||
builder.Services.AddSingleton(httpClient);
|
||||
builder.Services.AddHttpClient(); // IHttpClientFactory for calling remote services via RemoteServiceBase
|
||||
|
||||
// dynamically load client assemblies
|
||||
LoadClientAssemblies(httpClient, apiurl);
|
||||
}
|
||||
|
||||
// register localization services
|
||||
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
@ -60,7 +62,37 @@ public static class MauiProgram
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private static void LoadClientAssemblies(HttpClient http)
|
||||
|
||||
private static string LoadAppSettings()
|
||||
{
|
||||
var url = MauiConstants.ApiUrl;
|
||||
if (MauiConstants.UseAppSettings)
|
||||
{
|
||||
string file = Path.Combine(FileSystem.Current.AppDataDirectory, "appsettings.json");
|
||||
if (File.Exists(file))
|
||||
{
|
||||
using FileStream stream = File.OpenRead(file);
|
||||
using StreamReader reader = new StreamReader(stream);
|
||||
var content = reader.ReadToEnd();
|
||||
var obj = JsonSerializer.Deserialize<JsonObject>(content)!;
|
||||
if (!string.IsNullOrEmpty((string)obj["Url"]))
|
||||
{
|
||||
url = (string)obj["Url"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create template appsettings.json file
|
||||
using (StreamWriter writer = File.CreateText(file))
|
||||
{
|
||||
writer.WriteLine("{ \"Url\": \"\" }");
|
||||
}
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private static void LoadClientAssemblies(HttpClient http, string apiurl)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -84,7 +116,7 @@ public static class MauiProgram
|
||||
if (files.Count() != 0)
|
||||
{
|
||||
// get list of assemblies from server
|
||||
var json = Task.Run(() => http.GetStringAsync("/api/Installation/list")).GetAwaiter().GetResult();
|
||||
var json = Task.Run(() => http.GetStringAsync($"{GetUrlPath(apiurl)}api/Installation/list")).GetAwaiter().GetResult();
|
||||
var assemblies = JsonSerializer.Deserialize<List<string>>(json);
|
||||
|
||||
// determine which assemblies need to be downloaded
|
||||
@ -148,7 +180,7 @@ public static class MauiProgram
|
||||
if (list.Count != 0)
|
||||
{
|
||||
// get assemblies from server
|
||||
var zip = Task.Run(() => http.GetByteArrayAsync("/api/Installation/load?list=" + string.Join(",", list))).GetAwaiter().GetResult();
|
||||
var zip = Task.Run(() => http.GetByteArrayAsync($"{GetUrlPath(apiurl)}api/Installation/load?list=" + string.Join(",", list))).GetAwaiter().GetResult();
|
||||
|
||||
// asemblies and debug symbols are packaged in a zip file
|
||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||
@ -199,7 +231,7 @@ public static class MauiProgram
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Oqtane Error: Loading Client Assemblies {ex}");
|
||||
Debug.WriteLine($"Error Loading Client Assemblies From {apiurl} - {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,4 +277,17 @@ public static class MauiProgram
|
||||
// could not interrogate assembly - likely missing dependencies
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBaseUrl(string url)
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
return uri.Scheme + "://"+ uri.Authority + "/";
|
||||
}
|
||||
|
||||
private static string GetUrlPath(string url)
|
||||
{
|
||||
var path = new Uri(url).AbsolutePath.Substring(1);
|
||||
path = (!string.IsNullOrEmpty(path) && !path.EndsWith("/")) ? path + "/" : path;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -14,7 +14,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||
@ -31,7 +31,7 @@
|
||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>4.0.1</ApplicationDisplayVersion>
|
||||
<ApplicationDisplayVersion>4.0.2</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!-- App Icon -->
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" />
|
||||
|
||||
<!-- Splash Screen -->
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
|
||||
|
@ -1,4 +1,19 @@
|
||||
<?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>
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M994 1922 c-6 -4 -25 -50 -44 -102 -80 -218 -210 -455 -391 -712 -49
|
||||
-70 -100 -151 -114 -180 -105 -226 -53 -507 126 -686 190 -188 468 -234 709
|
||||
-116 104 51 223 170 274 274 84 172 86 367 7 532 -16 35 -72 122 -123 193 -52
|
||||
72 -111 161 -133 198 -21 37 -42 67 -46 67 -3 0 -46 -61 -94 -136 l-88 -136
|
||||
106 -156 c57 -87 110 -172 116 -191 16 -50 13 -150 -6 -207 -23 -69 -110 -155
|
||||
-182 -179 -68 -22 -144 -22 -212 0 -74 25 -159 110 -183 183 -19 57 -22 148
|
||||
-7 200 6 19 116 193 246 387 129 195 235 358 235 363 0 5 -22 56 -49 113 -27
|
||||
57 -64 145 -81 196 -31 88 -46 109 -66 95z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 1008 B |
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>4.0.1</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>4.0.1</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.1/Oqtane.Framework.4.0.1.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.2/Oqtane.Framework.4.0.2.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>4.0.1</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>4.0.1</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>4.0.1</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</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/v4.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.1.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.2.Install.zip" -Force
|
@ -14,7 +14,7 @@ dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
|
||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content" > NUL
|
||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content"
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText,Templates
|
||||
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText
|
||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Modules\*") do (
|
||||
set /A found=0
|
||||
for %%j in (%retain%) do (
|
||||
@ -22,7 +22,7 @@ if "%%~nxi" == "%%j" set /A found=1
|
||||
)
|
||||
if not !found! == 1 rmdir /Q/S "%%i"
|
||||
)
|
||||
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme,Templates
|
||||
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme
|
||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Themes\*") do (
|
||||
set /A found=0
|
||||
for %%j in (%retain%) do (
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.1.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.2.Upgrade.zip" -Force
|
@ -572,7 +572,7 @@ namespace Oqtane.Controllers
|
||||
// validation
|
||||
if (!Enum.TryParse(mode, true, out ResizeMode _)) mode = "crop";
|
||||
if (!Enum.TryParse(position, true, out AnchorPositionMode _)) position = "center";
|
||||
if (!Color.TryParseHex("#" + background, out _)) background = "000000";
|
||||
if (!Color.TryParseHex("#" + background, out _)) background = "transparent";
|
||||
if (!int.TryParse(rotate, out _)) rotate = "0";
|
||||
rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate;
|
||||
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
||||
@ -644,10 +644,23 @@ namespace Oqtane.Controllers
|
||||
Mode = resizemode,
|
||||
Position = anchorpositionmode,
|
||||
Size = new Size(width, height)
|
||||
})
|
||||
.BackgroundColor(Color.ParseHex("#" + background)));
|
||||
}));
|
||||
|
||||
image.Save(imagepath, new PngEncoder());
|
||||
if (background != "transparent")
|
||||
{
|
||||
image.Mutate(x => x
|
||||
.BackgroundColor(Color.ParseHex("#" + background)));
|
||||
}
|
||||
|
||||
PngEncoder encoder = new PngEncoder
|
||||
{
|
||||
ColorType = PngColorType.RgbWithAlpha,
|
||||
TransparentColorMode = PngTransparentColorMode.Preserve,
|
||||
BitDepth = PngBitDepth.Bit8,
|
||||
CompressionLevel = PngCompressionLevel.BestSpeed
|
||||
};
|
||||
|
||||
image.Save(imagepath, encoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -677,7 +690,14 @@ namespace Oqtane.Controllers
|
||||
path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Unable To Create Folder {Folder}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
foreach (Folder folder in _folders.GetFolders(SiteId))
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList))
|
||||
// note that Browse permission is used for this method
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
|
||||
{
|
||||
folders.Add(folder);
|
||||
}
|
||||
@ -87,6 +88,7 @@ namespace Oqtane.Controllers
|
||||
public Folder GetByPath(int siteId, string path)
|
||||
{
|
||||
var folderPath = WebUtility.UrlDecode(path).Replace("\\", "/");
|
||||
folderPath = (folderPath == "/") ? "" : folderPath;
|
||||
if (!folderPath.EndsWith("/") && folderPath != "")
|
||||
{
|
||||
folderPath += "/";
|
||||
|
@ -34,9 +34,9 @@ namespace Oqtane.Controllers
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ILogger<InstallationController> _filelogger;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly IServerStateManager _serverState;
|
||||
|
||||
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger, ITenantManager tenantManager, ServerStateManager serverState)
|
||||
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger, ITenantManager tenantManager, IServerStateManager serverState)
|
||||
{
|
||||
_configManager = configManager;
|
||||
_installationManager = installationManager;
|
||||
@ -119,9 +119,9 @@ namespace Oqtane.Controllers
|
||||
|
||||
private List<ClientAssembly> GetAssemblyList()
|
||||
{
|
||||
int siteId = _tenantManager.GetAlias().SiteId;
|
||||
var siteKey = _tenantManager.GetAlias().SiteKey;
|
||||
|
||||
return _cache.GetOrCreate($"assemblieslist:{siteId}", entry =>
|
||||
return _cache.GetOrCreate($"assemblieslist:{siteKey}", entry =>
|
||||
{
|
||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
var assemblyList = new List<ClientAssembly>();
|
||||
@ -134,7 +134,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
|
||||
// get site assemblies which should be downloaded to client
|
||||
var assemblies = _serverState.GetServerState(siteId).Assemblies;
|
||||
var assemblies = _serverState.GetServerState(siteKey).Assemblies;
|
||||
|
||||
// populate assembly list
|
||||
foreach (var assembly in assemblies)
|
||||
@ -179,9 +179,11 @@ namespace Oqtane.Controllers
|
||||
|
||||
private byte[] GetAssemblies(string list)
|
||||
{
|
||||
var siteKey = _tenantManager.GetAlias().SiteKey;
|
||||
|
||||
if (list == "*")
|
||||
{
|
||||
return _cache.GetOrCreate("assemblies", entry =>
|
||||
return _cache.GetOrCreate($"assemblies:{siteKey}", entry =>
|
||||
{
|
||||
return GetZIP(list);
|
||||
});
|
||||
|
@ -283,23 +283,26 @@ namespace Oqtane.Controllers
|
||||
var templates = new List<Template>();
|
||||
var root = Directory.GetParent(_environment.ContentRootPath);
|
||||
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", Path.DirectorySeparatorChar.ToString());
|
||||
foreach (string directory in Directory.GetDirectories(templatePath))
|
||||
if (Directory.Exists(templatePath))
|
||||
{
|
||||
string name = directory.Replace(templatePath, "");
|
||||
if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
|
||||
foreach (string directory in Directory.GetDirectories(templatePath))
|
||||
{
|
||||
var template = JsonSerializer.Deserialize<Template>(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
|
||||
template.Name = name;
|
||||
template.Location = "";
|
||||
if (template.Type.ToLower() != "internal")
|
||||
string name = directory.Replace(templatePath, "");
|
||||
if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
|
||||
{
|
||||
template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
|
||||
var template = JsonSerializer.Deserialize<Template>(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
|
||||
template.Name = name;
|
||||
template.Location = "";
|
||||
if (template.Type.ToLower() != "internal")
|
||||
{
|
||||
template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
|
||||
}
|
||||
templates.Add(template);
|
||||
}
|
||||
else
|
||||
{
|
||||
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
|
||||
}
|
||||
templates.Add(template);
|
||||
}
|
||||
else
|
||||
{
|
||||
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
|
||||
}
|
||||
}
|
||||
return templates;
|
||||
|
@ -23,10 +23,16 @@ namespace Oqtane.Controllers
|
||||
[HttpGet("{lastSyncDate}")]
|
||||
public Sync Get(string lastSyncDate)
|
||||
{
|
||||
DateTime currentdate = DateTime.UtcNow;
|
||||
DateTime lastdate = DateTime.ParseExact(lastSyncDate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture);
|
||||
if (lastdate == DateTime.MinValue)
|
||||
{
|
||||
lastdate = currentdate;
|
||||
}
|
||||
Sync sync = new Sync
|
||||
{
|
||||
SyncDate = DateTime.UtcNow,
|
||||
SyncEvents = _syncManager.GetSyncEvents(_alias.TenantId, DateTime.ParseExact(lastSyncDate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture))
|
||||
SyncDate = currentdate,
|
||||
SyncEvents = _syncManager.GetSyncEvents(_alias.TenantId, lastdate)
|
||||
};
|
||||
return sync;
|
||||
}
|
||||
|
@ -137,23 +137,26 @@ namespace Oqtane.Controllers
|
||||
var templates = new List<Template>();
|
||||
var root = Directory.GetParent(_environment.ContentRootPath);
|
||||
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", Path.DirectorySeparatorChar.ToString());
|
||||
foreach (string directory in Directory.GetDirectories(templatePath))
|
||||
if (Directory.Exists(templatePath))
|
||||
{
|
||||
string name = directory.Replace(templatePath, "");
|
||||
if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
|
||||
foreach (string directory in Directory.GetDirectories(templatePath))
|
||||
{
|
||||
var template = JsonSerializer.Deserialize<Template>(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
|
||||
template.Name = name;
|
||||
template.Location = "";
|
||||
if (template.Type.ToLower() != "internal")
|
||||
string name = directory.Replace(templatePath, "");
|
||||
if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
|
||||
{
|
||||
template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
|
||||
var template = JsonSerializer.Deserialize<Template>(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
|
||||
template.Name = name;
|
||||
template.Location = "";
|
||||
if (template.Type.ToLower() != "internal")
|
||||
{
|
||||
template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
|
||||
}
|
||||
templates.Add(template);
|
||||
}
|
||||
else
|
||||
{
|
||||
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
|
||||
}
|
||||
templates.Add(template);
|
||||
}
|
||||
else
|
||||
{
|
||||
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
|
||||
}
|
||||
}
|
||||
return templates;
|
||||
|
@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
@ -22,23 +21,17 @@ namespace Oqtane.Controllers
|
||||
public class UserController : Controller
|
||||
{
|
||||
private readonly IUserRepository _users;
|
||||
private readonly UserManager<IdentityUser> _identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly INotificationRepository _notifications;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ISiteRepository _sites;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly IJwtManager _jwtManager;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public UserController(IUserRepository users, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ILogManager logger)
|
||||
public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ILogManager logger)
|
||||
{
|
||||
_users = users;
|
||||
_identityUserManager = identityUserManager;
|
||||
_identitySignInManager = identitySignInManager;
|
||||
_tenantManager = tenantManager;
|
||||
_notifications = notifications;
|
||||
_userManager = userManager;
|
||||
_sites = sites;
|
||||
_userPermissions = userPermissions;
|
||||
|
@ -131,9 +131,8 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
userRole = _userRoles.AddUserRole(userRole);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Reload);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
|
||||
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Refresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -154,7 +153,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
userRole = _userRoles.UpdateUserRole(userRole);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Reload);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole);
|
||||
}
|
||||
else
|
||||
@ -171,25 +170,24 @@ namespace Oqtane.Controllers
|
||||
[Authorize(Policy = $"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public void Delete(int id)
|
||||
{
|
||||
UserRole userrole = _userRoles.GetUserRole(id);
|
||||
if (userrole != null && SiteValid(userrole.Role.SiteId) && RoleValid(userrole.Role.Name))
|
||||
UserRole userRole = _userRoles.GetUserRole(id);
|
||||
if (userRole != null && SiteValid(userRole.Role.SiteId) && RoleValid(userRole.Role.Name))
|
||||
{
|
||||
_userRoles.DeleteUserRole(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userrole.UserRoleId, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Reload);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userRole);
|
||||
|
||||
if (userrole.Role.Name == RoleNames.Host)
|
||||
if (userRole.Role.Name == RoleNames.Host)
|
||||
{
|
||||
// add site specific user roles to preserve user access
|
||||
var role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Registered);
|
||||
userrole = _userRoles.AddUserRole(new UserRole { UserId = userrole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userrole);
|
||||
userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
|
||||
role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Admin);
|
||||
userrole = _userRoles.AddUserRole(new UserRole { UserId = userrole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userrole);
|
||||
userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
|
||||
}
|
||||
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userrole.UserId, SyncEventActions.Refresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
services.AddSingleton<AutoValidateAntiforgeryTokenFilter>();
|
||||
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
|
||||
services.AddSingleton<ServerStateManager>();
|
||||
services.AddSingleton<IServerStateManager, ServerStateManager>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public interface IServerStateManager
|
||||
{
|
||||
ServerState GetServerState(string siteKey);
|
||||
}
|
||||
}
|
@ -40,9 +40,13 @@ namespace Oqtane.Infrastructure
|
||||
public string[] GetInstalledCultures()
|
||||
{
|
||||
var cultures = new List<string>();
|
||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{Constants.ClientId}{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||
{
|
||||
cultures.Add(Path.GetFileName(Path.GetDirectoryName(file)));
|
||||
var culture = Path.GetFileName(Path.GetDirectoryName(file));
|
||||
if (!cultures.Contains(culture))
|
||||
{
|
||||
cultures.Add(culture);
|
||||
}
|
||||
}
|
||||
return cultures.OrderBy(c => c).ToArray();
|
||||
}
|
||||
|
@ -37,20 +37,46 @@ namespace Oqtane.Infrastructure
|
||||
var identity = jwtManager.ValidateToken(token, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""));
|
||||
if (identity != null && identity.Claims.Any())
|
||||
{
|
||||
// create user identity using jwt claims (note the difference in claimtype names)
|
||||
var user = new User
|
||||
var idclaim = "nameid";
|
||||
var nameclaim = "unique_name";
|
||||
var legacynameclaim = "name"; // this was a breaking change in System.IdentityModel.Tokens.Jwt in .NET 7
|
||||
|
||||
// get jwt claims for userid and username
|
||||
var userid = identity.Claims.FirstOrDefault(item => item.Type == idclaim)?.Value;
|
||||
if (userid != null)
|
||||
{
|
||||
UserId = int.Parse(identity.Claims.FirstOrDefault(item => item.Type == "nameid")?.Value),
|
||||
Username = identity.Claims.FirstOrDefault(item => item.Type == "name")?.Value
|
||||
};
|
||||
// jwt already contains the roles - we are reloading to ensure most accurate permissions
|
||||
var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;
|
||||
if (!int.TryParse(userid, out _))
|
||||
{
|
||||
userid = null;
|
||||
}
|
||||
}
|
||||
var username = identity.Claims.FirstOrDefault(item => item.Type == nameclaim)?.Value;
|
||||
if (username == null)
|
||||
{
|
||||
// fallback for legacy clients
|
||||
username = identity.Claims.FirstOrDefault(item => item.Type == legacynameclaim)?.Value;
|
||||
}
|
||||
|
||||
// set claims identity
|
||||
var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList());
|
||||
context.User = new ClaimsPrincipal(claimsidentity);
|
||||
if (userid != null && username != null)
|
||||
{
|
||||
// create user identity
|
||||
var user = new User
|
||||
{
|
||||
UserId = int.Parse(userid),
|
||||
Username = username
|
||||
};
|
||||
|
||||
logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For User {Username}", user.Username);
|
||||
// set claims identity (note jwt already contains the roles - we are reloading to ensure most accurate permissions)
|
||||
var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;
|
||||
var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList());
|
||||
context.User = new ClaimsPrincipal(claimsidentity);
|
||||
|
||||
logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For UserId {UserId} And Username {Username}", user.UserId, user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Log(alias.SiteId, LogLevel.Error, "TokenValidation", Enums.LogFunction.Security, "Token Validated But Could Not Locate UserId Or Username In Claims {Claims}", identity.Claims.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -22,6 +22,7 @@ namespace Oqtane.Infrastructure
|
||||
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
|
||||
string path = context.Request.Path.ToString();
|
||||
|
||||
|
||||
if (config.IsInstalled() && !path.StartsWith("/_blazor"))
|
||||
{
|
||||
// get alias (note that this also sets SiteState.Alias)
|
||||
@ -43,6 +44,14 @@ namespace Oqtane.Infrastructure
|
||||
});
|
||||
context.Items.Add(Constants.HttpContextSiteSettingsKey, sitesettings);
|
||||
|
||||
// handle first request to site
|
||||
var serverState = context.RequestServices.GetService(typeof(IServerStateManager)) as IServerStateManager;
|
||||
if (!serverState.GetServerState(alias.SiteKey).IsInitialized)
|
||||
{
|
||||
var sites = context.RequestServices.GetService(typeof(ISiteRepository)) as ISiteRepository;
|
||||
sites.InitializeSite(alias);
|
||||
}
|
||||
|
||||
// rewrite path by removing alias path prefix from reserved route (api,pages,files) requests for consistent routes
|
||||
if (!string.IsNullOrEmpty(alias.Path))
|
||||
{
|
||||
|
@ -5,9 +5,9 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
public class ServerState
|
||||
{
|
||||
public int SiteId { get; set; }
|
||||
public string SiteKey { get; set; }
|
||||
public List<string> Assemblies { get; set; } = new List<string>();
|
||||
public List<Resource>Scripts { get; set; } = new List<Resource>();
|
||||
public bool IsMigrated { get; set; } = false;
|
||||
public bool IsInitialized { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using Oqtane.Models;
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
// singleton
|
||||
public class ServerStateManager
|
||||
public class ServerStateManager : IServerStateManager
|
||||
{
|
||||
private List<ServerState> _serverStates { get; set; }
|
||||
|
||||
@ -14,36 +14,19 @@ namespace Oqtane.Infrastructure
|
||||
_serverStates = new List<ServerState>();
|
||||
}
|
||||
|
||||
public ServerState GetServerState(int siteId)
|
||||
public ServerState GetServerState(string siteKey)
|
||||
{
|
||||
var serverState = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
|
||||
var serverState = _serverStates.FirstOrDefault(item => item.SiteKey == siteKey);
|
||||
if (serverState == null)
|
||||
{
|
||||
serverState = new ServerState();
|
||||
serverState.SiteId = siteId;
|
||||
serverState.SiteKey = siteKey;
|
||||
serverState.Assemblies = new List<string>();
|
||||
serverState.Scripts = new List<Resource>();
|
||||
return serverState;
|
||||
}
|
||||
else
|
||||
{
|
||||
return serverState;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetServerState(int siteId, ServerState serverState)
|
||||
{
|
||||
var serverstate = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
|
||||
if (serverstate == null)
|
||||
{
|
||||
serverState.SiteId = siteId;
|
||||
serverState.IsInitialized = false;
|
||||
_serverStates.Add(serverState);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverstate.Assemblies = serverState.Assemblies;
|
||||
serverstate.Scripts = serverState.Scripts;
|
||||
}
|
||||
return serverState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,35 +133,6 @@ namespace Oqtane.SiteTemplates
|
||||
}
|
||||
}
|
||||
});
|
||||
_pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Develop",
|
||||
Parent = "",
|
||||
Order = 7,
|
||||
Path = "develop",
|
||||
Icon = "oi oi-wrench",
|
||||
IsNavigation = true,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule> {
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Software Development", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true)
|
||||
},
|
||||
Content = "<p>Oqtane offers a Module Creator which allows you to create new modules to extend the framework with additional capabilities. Simply provide some basic information and the system will scaffold a completely functional module which includes all of the necessary code files and assets to get you up and running as quickly as possible.</p>"
|
||||
},
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleCreator, Oqtane.Client", Title = "Module Creator", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Host, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Host, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo-white.png")))
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Oqtane.Models;
|
||||
@ -57,7 +58,7 @@ namespace Oqtane.Infrastructure
|
||||
alias.BaseUrl = "";
|
||||
if (httpcontext.Request.Headers.ContainsKey("User-Agent") && httpcontext.Request.Headers["User-Agent"] == Shared.Constants.MauiUserAgent)
|
||||
{
|
||||
alias.BaseUrl = alias.Protocol + alias.Name;
|
||||
alias.BaseUrl = alias.Protocol + alias.Name.Replace("/" + alias.Path, "");
|
||||
}
|
||||
_siteState.Alias = alias;
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ namespace Oqtane.Infrastructure
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Content = "<p>The page you requested does not exist.</p>"
|
||||
Content = "<p>The page you requested does not exist or you do not have sufficient rights to view it.</p>"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -130,14 +130,14 @@ namespace Oqtane.Managers
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = alias.Protocol + "://" + alias.Name;
|
||||
string url = alias.Protocol + alias.Name;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
@ -178,7 +178,7 @@ namespace Oqtane.Managers
|
||||
|
||||
user = _users.UpdateUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Reload);
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
||||
}
|
||||
@ -228,6 +228,7 @@ namespace Oqtane.Managers
|
||||
// delete user
|
||||
_users.DeleteUser(userid);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, userid, SyncEventActions.Delete);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, userid, SyncEventActions.Reload);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", userid, result.ToString());
|
||||
}
|
||||
else
|
||||
@ -299,7 +300,7 @@ namespace Oqtane.Managers
|
||||
var alias = _tenantManager.GetAlias();
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nThank You!";
|
||||
@ -348,7 +349,7 @@ namespace Oqtane.Managers
|
||||
var alias = _tenantManager.GetAlias();
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -11,7 +11,7 @@
|
||||
<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/v4.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -3,6 +3,9 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Managers;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
@ -10,9 +13,28 @@ namespace Oqtane.Pages
|
||||
[Authorize]
|
||||
public class LogoutModel : PageModel
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ISyncManager _syncManager;
|
||||
|
||||
public LogoutModel(IUserManager userManager, ISyncManager syncManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_syncManager = syncManager;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(string returnurl)
|
||||
{
|
||||
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
||||
if (HttpContext.User != null)
|
||||
{
|
||||
var alias = HttpContext.GetAlias();
|
||||
var user = _userManager.GetUser(HttpContext.User.Identity.Name, alias.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.User, user.UserId, SyncEventActions.Reload);
|
||||
}
|
||||
|
||||
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
||||
}
|
||||
|
||||
returnurl = (returnurl == null) ? "/" : returnurl;
|
||||
returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl;
|
||||
|
@ -4,7 +4,6 @@ using Oqtane.Shared;
|
||||
using Oqtane.Models;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Oqtane.Repository;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@ -38,10 +37,11 @@ namespace Oqtane.Pages
|
||||
private readonly IVisitorRepository _visitors;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly IThemeRepository _themes;
|
||||
private readonly IServerStateManager _serverState;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ServerStateManager serverState, ILogManager logger)
|
||||
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, IThemeRepository themes, IServerStateManager serverState, ILogManager logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_tenantManager = tenantManager;
|
||||
@ -55,6 +55,7 @@ namespace Oqtane.Pages
|
||||
_visitors = visitors;
|
||||
_aliases = aliases;
|
||||
_settings = settings;
|
||||
_themes = themes;
|
||||
_serverState = serverState;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -113,7 +114,7 @@ namespace Oqtane.Pages
|
||||
}
|
||||
}
|
||||
|
||||
var site = _sites.InitializeSite(alias);
|
||||
var site = _sites.GetSite(alias.SiteId);
|
||||
if (site != null && (!site.IsDeleted || url.Contains("admin/site")) && site.Runtime != "Hybrid")
|
||||
{
|
||||
Route route = new Route(url, alias.Path);
|
||||
@ -167,12 +168,13 @@ namespace Oqtane.Pages
|
||||
}
|
||||
|
||||
// stylesheets
|
||||
var themes = _themes.GetThemes().ToList();
|
||||
var resources = new List<Resource>();
|
||||
if (string.IsNullOrEmpty(page.ThemeType))
|
||||
{
|
||||
page.ThemeType = site.DefaultThemeType;
|
||||
}
|
||||
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||
var theme = themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||
if (theme?.Resources != null)
|
||||
{
|
||||
resources.AddRange(theme.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet).ToList());
|
||||
@ -199,7 +201,7 @@ namespace Oqtane.Pages
|
||||
}
|
||||
HeadResources += ParseScripts(site.HeadContent);
|
||||
BodyResources += ParseScripts(site.BodyContent);
|
||||
var scripts = _serverState.GetServerState(site.SiteId).Scripts;
|
||||
var scripts = _serverState.GetServerState(alias.SiteKey).Scripts;
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
AddScript(script, alias);
|
||||
|
@ -11,7 +11,7 @@ namespace Oqtane.Repository
|
||||
Site GetSite(int siteId);
|
||||
Site GetSite(int siteId, bool tracking);
|
||||
void DeleteSite(int siteId);
|
||||
Site InitializeSite(Alias alias);
|
||||
void InitializeSite(Alias alias);
|
||||
void CreatePages(Site site, List<PageTemplate> pageTemplates, Alias alias);
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ namespace Oqtane.Repository
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private readonly ITenantManager _tenants;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly IServerStateManager _serverState;
|
||||
private readonly string settingprefix = "SiteEnabled:";
|
||||
|
||||
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
|
||||
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, IServerStateManager serverState)
|
||||
{
|
||||
_db = context;
|
||||
_cache = cache;
|
||||
@ -179,6 +179,8 @@ namespace Oqtane.Repository
|
||||
|
||||
if (siteId != -1)
|
||||
{
|
||||
var siteKey = _tenants.GetAlias().SiteKey;
|
||||
|
||||
// get all module definition permissions for site
|
||||
List<Permission> permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
|
||||
|
||||
@ -186,7 +188,7 @@ namespace Oqtane.Repository
|
||||
var settings = _settings.GetSettings(EntityNames.ModuleDefinition).ToList();
|
||||
|
||||
// populate module definition site settings and permissions
|
||||
var serverState = _serverState.GetServerState(siteId);
|
||||
var serverState = _serverState.GetServerState(siteKey);
|
||||
foreach (ModuleDefinition moduledefinition in ModuleDefinitions)
|
||||
{
|
||||
moduledefinition.SiteId = siteId;
|
||||
@ -212,9 +214,9 @@ namespace Oqtane.Repository
|
||||
{
|
||||
foreach (var assembly in moduledefinition.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||
{
|
||||
if (!serverState.Assemblies.Contains(assembly))
|
||||
if (!serverState.Assemblies.Contains(assembly.Trim()))
|
||||
{
|
||||
serverState.Assemblies.Insert(0, assembly);
|
||||
serverState.Assemblies.Insert(0, assembly.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,7 +253,6 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
}
|
||||
_serverState.SetServerState(siteId, serverState);
|
||||
|
||||
// clean up any orphaned permissions
|
||||
var ids = new HashSet<int>(ModuleDefinitions.Select(item => item.ModuleDefinitionId));
|
||||
|
@ -27,13 +27,13 @@ namespace Oqtane.Repository
|
||||
private readonly IThemeRepository _themeRepository;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IConfigurationRoot _config;
|
||||
private readonly ServerStateManager _serverState;
|
||||
private readonly IServerStateManager _serverState;
|
||||
private readonly ILogManager _logger;
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
|
||||
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
|
||||
IConfigurationRoot config, ServerStateManager serverState, ILogManager logger)
|
||||
IConfigurationRoot config, IServerStateManager serverState, ILogManager logger)
|
||||
{
|
||||
_db = context;
|
||||
_roleRepository = roleRepository;
|
||||
@ -95,23 +95,25 @@ namespace Oqtane.Repository
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public Site InitializeSite(Alias alias)
|
||||
public void InitializeSite(Alias alias)
|
||||
{
|
||||
var site = GetSite(alias.SiteId);
|
||||
|
||||
// load themes and module definitions
|
||||
site.Themes = _themeRepository.GetThemes().ToList();
|
||||
var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
|
||||
|
||||
// site migrations
|
||||
var serverstate = _serverState.GetServerState(alias.SiteId);
|
||||
if (!serverstate.IsMigrated)
|
||||
var serverstate = _serverState.GetServerState(alias.SiteKey);
|
||||
if (!serverstate.IsInitialized)
|
||||
{
|
||||
// ensure migrations are only executed once
|
||||
// ensure site initialization is only executed once
|
||||
lock (_lock)
|
||||
{
|
||||
if (!serverstate.IsMigrated)
|
||||
if (!serverstate.IsInitialized)
|
||||
{
|
||||
var site = GetSite(alias.SiteId);
|
||||
|
||||
// initialize theme Assemblies and Scripts
|
||||
site.Themes = _themeRepository.GetThemes().ToList();
|
||||
|
||||
// initialize module Assemblies and Scripts
|
||||
var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
|
||||
|
||||
// execute migrations
|
||||
var version = ProcessSiteMigrations(alias, site);
|
||||
version = ProcessPageTemplates(alias, site, moduleDefinitions, version);
|
||||
if (site.Version != version)
|
||||
@ -119,13 +121,11 @@ namespace Oqtane.Repository
|
||||
site.Version = version;
|
||||
UpdateSite(site);
|
||||
}
|
||||
serverstate.IsMigrated = true;
|
||||
_serverState.SetServerState(alias.SiteId, serverstate);
|
||||
|
||||
serverstate.IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return site;
|
||||
}
|
||||
}
|
||||
|
||||
private string ProcessSiteMigrations(Alias alias, Site site)
|
||||
@ -640,7 +640,7 @@ namespace Oqtane.Repository
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Content = "<p>The page you requested does not exist.</p>"
|
||||
Content = "<p>The page you requested does not exist or you do not have sufficient rights to view it.</p>"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user