commit
3a15e6e5e9
24
CONTRIBUTING.md
Normal file
24
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Contributing to Oqtane
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
We track all of our issues on Github. If you want to contribute, everything starts with an issue. If you don't have an issue yet, you can add one. Then a core contributor will tag it as either an enhancement [ENH] or a bug [BUG]. Tagged issues are open for contribution.
|
||||||
|
|
||||||
|
## Use GitHub-flow process
|
||||||
|
- Make a comment on the issue that you intend to work on it and read all the comments to gain a full understanding.
|
||||||
|
- Fork the repository
|
||||||
|
- Create a new branch and update your comment on the issue with a llink to the branch
|
||||||
|
- Make your changes and commit them
|
||||||
|
- Push to the branch
|
||||||
|
- Create a pull request
|
||||||
|
|
||||||
|
## Reporting Bugs
|
||||||
|
|
||||||
|
- Check if the issue has already been reported.
|
||||||
|
- Open a new issue if it hasn’t been reported.
|
||||||
|
|
||||||
|
## Requesting Features
|
||||||
|
|
||||||
|
- Use the feature request template in the Issues tab.
|
||||||
|
|
||||||
|
Thank you for contributing!
|
|
@ -12,7 +12,7 @@
|
||||||
@if (_initialized)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
<TabStrip>
|
<TabStrip ActiveTab="@_activetab">
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||||
@if (_containers != null)
|
@if (_containers != null)
|
||||||
{
|
{
|
||||||
|
@ -162,6 +162,7 @@
|
||||||
private DateTime? _effectivedate = null;
|
private DateTime? _effectivedate = null;
|
||||||
private DateTime? _expirydate = null;
|
private DateTime? _expirydate = null;
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
|
private string _activetab = "";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
@ -241,6 +242,7 @@
|
||||||
|
|
||||||
private async Task SaveModule()
|
private async Task SaveModule()
|
||||||
{
|
{
|
||||||
|
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
|
@ -300,11 +302,13 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_activetab = "Settings";
|
||||||
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_activetab = "Settings";
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,10 +316,11 @@
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeChanged(ChangeEventArgs e)
|
private async Task ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
|
@ -330,6 +331,7 @@
|
||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,6 +347,7 @@
|
||||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
||||||
|
@ -395,12 +398,14 @@
|
||||||
if (_pages.Any(item => item.Path == page.Path))
|
if (_pages.Any(item => item.Path == page.Path))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,6 +473,7 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -475,11 +481,13 @@
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -479,10 +479,11 @@
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThemeChanged(ChangeEventArgs e)
|
private async Task ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
|
@ -494,6 +495,7 @@
|
||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,6 +533,7 @@
|
||||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||||
|
@ -581,12 +584,14 @@
|
||||||
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
||||||
{
|
{
|
||||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,17 +676,20 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
|
await ScrollToPageTop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -481,6 +481,11 @@
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (PageState.QueryString.ContainsKey("updated"))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||||
|
}
|
||||||
|
|
||||||
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||||
if (site != null)
|
if (site != null)
|
||||||
{
|
{
|
||||||
|
@ -736,7 +741,7 @@
|
||||||
|
|
||||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl(), true); // reload
|
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "updated=true"), true); // reload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -149,22 +149,26 @@
|
||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,22 +178,26 @@
|
||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@if (p.IsRequired)
|
@if (p.IsRequired)
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"
|
||||||
|
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,13 +238,13 @@
|
||||||
|
|
||||||
@if (context.IsRead)
|
@if (context.IsRead)
|
||||||
{
|
{
|
||||||
<td>@context.FromDisplayName</td>
|
<td>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</td>
|
||||||
<td>@context.Subject</td>
|
<td>@context.Subject</td>
|
||||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<td><b>@context.FromDisplayName</b></td>
|
<td><b>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</b></td>
|
||||||
<td><b>@context.Subject</b></td>
|
<td><b>@context.Subject</b></td>
|
||||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||||
}
|
}
|
||||||
|
@ -363,7 +371,7 @@
|
||||||
private File photo = null;
|
private File photo = null;
|
||||||
private string _ImageFiles = string.Empty;
|
private string _ImageFiles = string.Empty;
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> userSettings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string filter = "to";
|
private string filter = "to";
|
||||||
|
@ -411,9 +419,9 @@
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
userSettings = PageState.User.Settings;
|
||||||
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
_ImageFiles = SettingService.GetSetting(userSettings, "ImageFiles", Constants.ImageFiles);
|
||||||
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||||
|
|
||||||
await LoadNotificationsAsync();
|
await LoadNotificationsAsync();
|
||||||
|
@ -440,7 +448,7 @@
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
{
|
{
|
||||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||||
if (value.Contains("]"))
|
if (value.Contains("]"))
|
||||||
{
|
{
|
||||||
value = value.Substring(value.IndexOf("]") + 1);
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
@ -483,7 +491,7 @@
|
||||||
user = await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId);
|
||||||
await logger.LogInformation("User Profile Saved");
|
await logger.LogInformation("User Profile Saved");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||||
|
@ -554,7 +562,7 @@
|
||||||
var value = GetProfileValue(profile.Name, string.Empty);
|
var value = GetProfileValue(profile.Name, string.Empty);
|
||||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||||
{
|
{
|
||||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
|
||||||
}
|
}
|
||||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
|
@ -586,7 +594,7 @@
|
||||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||||
{
|
{
|
||||||
var value = (string)e.Value;
|
var value = (string)e.Value;
|
||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Delete(Notification Notification)
|
private async Task Delete(Notification Notification)
|
||||||
|
|
|
@ -128,7 +128,7 @@
|
||||||
createdon = notification.CreatedOn.ToString();
|
createdon = notification.CreatedOn.ToString();
|
||||||
body = notification.Body;
|
body = notification.Body;
|
||||||
|
|
||||||
if (title == "From")
|
if (title == "From" && !notification.IsRead)
|
||||||
{
|
{
|
||||||
notification.IsRead = true;
|
notification.IsRead = true;
|
||||||
notification = await NotificationService.UpdateNotificationAsync(notification);
|
notification = await NotificationService.UpdateNotificationAsync(notification);
|
||||||
|
|
|
@ -81,11 +81,11 @@
|
||||||
{
|
{
|
||||||
@if (p.Rows == 1)
|
@if (p.Rows == 1)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -110,11 +110,11 @@
|
||||||
{
|
{
|
||||||
@if (p.Rows == 1)
|
@if (p.Rows == 1)
|
||||||
{
|
{
|
||||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
private string lastipaddress;
|
private string lastipaddress;
|
||||||
|
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> userSettings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
private string createdby;
|
private string createdby;
|
||||||
|
@ -181,7 +181,7 @@
|
||||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||||
lastipaddress = user.LastIPAddress;
|
lastipaddress = user.LastIPAddress;
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
userSettings = user.Settings;
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
modifiedby = user.ModifiedBy;
|
modifiedby = user.ModifiedBy;
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
{
|
{
|
||||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||||
if (value.Contains("]"))
|
if (value.Contains("]"))
|
||||||
{
|
{
|
||||||
value = value.Substring(value.IndexOf("]") + 1);
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
@ -232,7 +232,7 @@
|
||||||
user = await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId);
|
||||||
await logger.LogInformation("User Saved {User}", user);
|
await logger.LogInformation("User Saved {User}", user);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
var value = GetProfileValue(profile.Name, string.Empty);
|
var value = GetProfileValue(profile.Name, string.Empty);
|
||||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||||
{
|
{
|
||||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
|
||||||
}
|
}
|
||||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
|
@ -293,7 +293,7 @@
|
||||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||||
{
|
{
|
||||||
var value = (string)e.Value;
|
var value = (string)e.Value;
|
||||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TogglePassword()
|
private void TogglePassword()
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
|
|
||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -200,7 +200,7 @@
|
||||||
{
|
{
|
||||||
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -248,7 +248,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
|
||||||
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -368,6 +368,12 @@
|
||||||
[SupplyParameterFromForm(FormName = "PagerForm")]
|
[SupplyParameterFromForm(FormName = "PagerForm")]
|
||||||
public string _Search { get => ""; set => _search = value; }
|
public string _Search { get => ""; set => _search = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accepted values are Start or Center or End. The default value is Center
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string PaginationAlignment { get; set; } = "center"; // Alignment of the Page Numbering start, center, end
|
||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
|
|
|
@ -277,7 +277,7 @@
|
||||||
{
|
{
|
||||||
// include CSS theme
|
// include CSS theme
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
await interop.IncludeLink("", "stylesheet", $"css/quill/quill.{_theme}.css", "text/css", "", "", "");
|
await interop.IncludeLink("", "stylesheet", $"{PageState?.Alias.BaseUrl}/css/quill/quill.{_theme}.css", "text/css", "", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
|
|
@ -36,14 +36,7 @@ else
|
||||||
|
|
||||||
Parent.AddTabPanel((TabPanel)this);
|
Parent.AddTabPanel((TabPanel)this);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Heading))
|
Heading = string.IsNullOrEmpty(Heading) ? Localize(nameof(Name), Name) : Localize(nameof(Heading), Heading);
|
||||||
{
|
|
||||||
Heading = Localize(nameof(Name), Name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Heading = Localize(nameof(Heading), Heading);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DisplayHeading()
|
public string DisplayHeading()
|
||||||
|
|
|
@ -98,17 +98,17 @@ namespace Oqtane.Modules
|
||||||
var inline = 0;
|
var inline = 0;
|
||||||
foreach (Resource resource in resources)
|
foreach (Resource resource in resources)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
if ((string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive) && !resource.Reload)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(resource.Url))
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inline += 1;
|
inline += 1;
|
||||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
|
|
@ -435,4 +435,7 @@
|
||||||
<data name="Functionality" xml:space="preserve">
|
<data name="Functionality" xml:space="preserve">
|
||||||
<value>Functionality</value>
|
<value>Functionality</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="System" xml:space="preserve">
|
||||||
|
<value>System</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
@ -151,6 +151,6 @@
|
||||||
<value>You Cannot Perform A System Update In A Development Environment</value>
|
<value>You Cannot Perform A System Update In A Development Environment</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Disclaimer.Text" xml:space="preserve">
|
<data name="Disclaimer.Text" xml:space="preserve">
|
||||||
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process. Also Note That The System Update Capability Is Not Recommended When Using Microsoft Azure Due To The Limitations Of That Environment. </value>
|
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process. Also Note That The System Update Capability Is Not Recommended When Using Microsoft Azure Due To Environmental Limitations.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -37,11 +37,8 @@
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
// obtained from https://cdnjs.com/libraries
|
// obtained from https://cdnjs.com/libraries
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css",
|
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css", "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==", "anonymous"),
|
||||||
Integrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==",
|
new Stylesheet(ThemePath() + "Theme.css"),
|
||||||
CrossOrigin = "anonymous" },
|
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
|
||||||
new Resource { ResourceType = ResourceType.Script, Url = Constants.BootstrapScriptUrl, Integrity = Constants.BootstrapScriptIntegrity, CrossOrigin = "anonymous", Location = ResourceLocation.Body },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<LanguageSwitcher ButtonClass="@ButtonClass" DropdownAlignment="@LanguageDropdownAlignment" />
|
<LanguageSwitcher ButtonClass="@ButtonClass" DropdownAlignment="@LanguageDropdownAlignment" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
@if (ShowEditMode && (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))))
|
||||||
{
|
{
|
||||||
<form method="post" class="app-form-inline" @formname="EditModeForm" @onsubmit="@(async () => await ToggleEditMode(PageState.EditMode))" data-enhance>
|
<form method="post" class="app-form-inline" @formname="EditModeForm" @onsubmit="@(async () => await ToggleEditMode(PageState.EditMode))" data-enhance>
|
||||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||||
|
@ -59,9 +59,15 @@
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string LanguageDropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
public string LanguageDropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ability to hide the Edit Mode toggle button
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowEditMode { get; set; } = true;
|
||||||
|
|
||||||
private PageState _pageState;
|
private PageState _pageState;
|
||||||
private bool _canViewAdminDashboard = false;
|
private bool _canViewAdminDashboard = false;
|
||||||
private bool _showEditMode = false;
|
private bool _showEditMode = false; // internal state (not the same as ShowEditMode parameter)
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
@inject ILogService LoggingService
|
@inject ILogService LoggingService
|
||||||
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
@inject IServiceProvider ServiceProvider
|
|
||||||
|
|
||||||
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
|
||||||
<span class="oi oi-cog"></span>
|
<span class="oi oi-cog"></span>
|
||||||
|
|
|
@ -17,11 +17,9 @@ namespace Oqtane.Themes.OqtaneTheme
|
||||||
Resources = new List<Resource>()
|
Resources = new List<Resource>()
|
||||||
{
|
{
|
||||||
// obtained from https://cdnjs.com/libraries
|
// obtained from https://cdnjs.com/libraries
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css",
|
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css", "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==", "anonymous"),
|
||||||
Integrity = "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==",
|
new Stylesheet("~/Theme.css"),
|
||||||
CrossOrigin = "anonymous" },
|
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
|
|
||||||
new Resource { ResourceType = ResourceType.Script, Url = Constants.BootstrapScriptUrl, Integrity = Constants.BootstrapScriptIntegrity, CrossOrigin = "anonymous", Location = ResourceLocation.Body }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,17 +62,17 @@ namespace Oqtane.Themes
|
||||||
var inline = 0;
|
var inline = 0;
|
||||||
foreach (Resource resource in resources)
|
foreach (Resource resource in resources)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
if ((string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive) && !resource.Reload)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(resource.Url))
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inline += 1;
|
inline += 1;
|
||||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "HeadContent":
|
case "HeadContent":
|
||||||
var content = RemoveScripts(SiteState.Properties.HeadContent) + "\n";
|
var content = FormatScripts(SiteState.Properties.HeadContent) + "\n";
|
||||||
if (content != _content)
|
if (content != _content)
|
||||||
{
|
{
|
||||||
_content = content;
|
_content = content;
|
||||||
|
@ -51,16 +51,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string RemoveScripts(string headcontent)
|
private string FormatScripts(string headcontent)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(headcontent) && RenderMode == RenderModes.Interactive)
|
if (!string.IsNullOrEmpty(headcontent))
|
||||||
{
|
{
|
||||||
var index = headcontent.IndexOf("<script");
|
var index = headcontent.IndexOf("<script");
|
||||||
while (index >= 0)
|
while (index >= 0)
|
||||||
{
|
{
|
||||||
headcontent = headcontent.Remove(index, headcontent.IndexOf("</script>") + 9 - index);
|
var script = headcontent.Substring(index, headcontent.IndexOf("</script>", index) - index + 9);
|
||||||
|
if (RenderMode == RenderModes.Interactive)
|
||||||
|
{
|
||||||
|
// remove scripts when interactive rendering
|
||||||
|
headcontent = headcontent.Remove(index, script.Length);
|
||||||
index = headcontent.IndexOf("<script");
|
index = headcontent.IndexOf("<script");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!script.Contains("><") && !script.Contains("data-reload"))
|
||||||
|
{
|
||||||
|
// add data-reload attribute to inline script
|
||||||
|
headcontent = headcontent.Replace(script, script.Replace("<script", "<script data-reload=\"true\""));
|
||||||
|
}
|
||||||
|
index = headcontent.IndexOf("<script", index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return headcontent;
|
return headcontent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,12 +117,17 @@ namespace Oqtane.UI
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location)
|
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location)
|
||||||
|
{
|
||||||
|
return IncludeScript(id, src, integrity, crossorigin, type, content, location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location, Dictionary<string, string> dataAttributes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.includeScript",
|
"Oqtane.Interop.includeScript",
|
||||||
id, src, integrity, crossorigin, type, content, location);
|
id, src, integrity, crossorigin, type, content, location, dataAttributes);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
RenderFragment DynamicComponent { get; set; }
|
RenderFragment DynamicComponent { get; set; }
|
||||||
|
|
||||||
|
private string lastPagePath = "";
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
// handle page redirection
|
// handle page redirection
|
||||||
|
@ -90,8 +92,16 @@
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (!firstRender)
|
if (!firstRender && PageState.Page.Path != lastPagePath)
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrEmpty(PageState.Site.HeadContent) && PageState.Site.HeadContent.Contains("<script"))
|
||||||
|
{
|
||||||
|
await InjectScripts(PageState.Site.HeadContent, ResourceLocation.Head);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(PageState.Site.BodyContent) && PageState.Site.BodyContent.Contains("<script"))
|
||||||
|
{
|
||||||
|
await InjectScripts(PageState.Site.BodyContent, ResourceLocation.Body);
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
||||||
{
|
{
|
||||||
await InjectScripts(PageState.Page.HeadContent, ResourceLocation.Head);
|
await InjectScripts(PageState.Page.HeadContent, ResourceLocation.Head);
|
||||||
|
@ -100,6 +110,7 @@
|
||||||
{
|
{
|
||||||
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
|
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
|
||||||
}
|
}
|
||||||
|
lastPagePath = PageState.Page.Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// style sheets
|
// style sheets
|
||||||
|
@ -176,18 +187,20 @@
|
||||||
if (!string.IsNullOrEmpty(src))
|
if (!string.IsNullOrEmpty(src))
|
||||||
{
|
{
|
||||||
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
|
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
|
||||||
scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower(), dataAttributes = dataAttributes });
|
scripts.Add(new { href = src, type = type, bundle = "", integrity = integrity, crossorigin = crossorigin, location = location.ToString().ToLower(), dataAttributes = dataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// inline script must have an id attribute
|
if (dataAttributes == null || !dataAttributes.ContainsKey("data-reload") || dataAttributes["data-reload"] != "false")
|
||||||
|
{
|
||||||
if (id == "")
|
if (id == "")
|
||||||
{
|
{
|
||||||
count += 1;
|
count += 1;
|
||||||
id = $"page{PageState.Page.PageId}-script{count}";
|
id = $"page{PageState.Page.PageId}-script{count}";
|
||||||
}
|
}
|
||||||
index = script.IndexOf(">") + 1;
|
var pos = script.IndexOf(">") + 1;
|
||||||
await interop.IncludeScript(id, "", "", "", "", script.Substring(index, script.IndexOf("</script>") - index), location.ToString().ToLower());
|
await interop.IncludeScript(id, "", "", "", type, script.Substring(pos, script.IndexOf("</script>") - pos), location.ToString().ToLower(), dataAttributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
index = content.IndexOf("<script", index + 1);
|
index = content.IndexOf("<script", index + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -29,14 +29,13 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<NoWarn>1701;1702;EF1001;AD0001;NU1608</NoWarn>
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.0-rc.2" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
||||||
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
<ApplicationId>com.oqtane.maui</ApplicationId>
|
<ApplicationId>com.oqtane.maui</ApplicationId>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>6.0.0</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>6.0.1</ApplicationDisplayVersion>
|
||||||
<ApplicationVersion>1</ApplicationVersion>
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>6.0.0</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,14 +12,14 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Client\bin\Release\net8.0\Oqtane.Client.dll" target="lib\net9.0" />
|
<file src="..\Oqtane.Client\bin\Release\net9.0\Oqtane.Client.dll" target="lib\net9.0" />
|
||||||
<file src="..\Oqtane.Client\bin\Release\net8.0\Oqtane.Client.pdb" target="lib\net9.0" />
|
<file src="..\Oqtane.Client\bin\Release\net9.0\Oqtane.Client.pdb" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
<file src="readme.md" target="" />
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>6.0.0</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -11,8 +11,8 @@
|
||||||
<copyright>.NET Foundation</copyright>
|
<copyright>.NET Foundation</copyright>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.0.0/Oqtane.Framework.6.0.0.Upgrade.zip</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.0.1/Oqtane.Framework.6.0.1.Upgrade.zip</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>6.0.0</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,14 +12,14 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Server.dll" target="lib\net9.0" />
|
<file src="..\Oqtane.Server\bin\Release\net9.0\Oqtane.Server.dll" target="lib\net9.0" />
|
||||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Server.pdb" target="lib\net9.0" />
|
<file src="..\Oqtane.Server\bin\Release\net9.0\Oqtane.Server.pdb" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
<file src="readme.md" target="" />
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>6.0.0</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,14 +12,14 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Shared\bin\Release\net8.0\Oqtane.Shared.dll" target="lib\net9.0" />
|
<file src="..\Oqtane.Shared\bin\Release\net9.0\Oqtane.Shared.dll" target="lib\net9.0" />
|
||||||
<file src="..\Oqtane.Shared\bin\Release\net8.0\Oqtane.Shared.pdb" target="lib\net9.0" />
|
<file src="..\Oqtane.Shared\bin\Release\net9.0\Oqtane.Shared.pdb" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
<file src="readme.md" target="" />
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<package>
|
<package>
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>6.0.0</version>
|
<version>6.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
|
@ -12,13 +12,13 @@
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</releaseNotes>
|
||||||
<readme>readme.md</readme>
|
<readme>readme.md</readme>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Oqtane.Updater\bin\Release\net8.0\publish\*.*" target="lib\net9.0" />
|
<file src="..\Oqtane.Updater\bin\Release\net9.0\publish\*.*" target="lib\net9.0" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
<file src="readme.md" target="" />
|
<file src="readme.md" target="" />
|
||||||
</files>
|
</files>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.0.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.1.Install.zip" -Force
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.0.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.0.1.Upgrade.zip" -Force
|
||||||
|
|
|
@ -76,6 +76,10 @@
|
||||||
|
|
||||||
@((MarkupString)_scripts)
|
@((MarkupString)_scripts)
|
||||||
@((MarkupString)_bodyResources)
|
@((MarkupString)_bodyResources)
|
||||||
|
@if (_renderMode == RenderModes.Static)
|
||||||
|
{
|
||||||
|
<page-script src="./js/reload.js"></page-script>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -188,9 +192,6 @@
|
||||||
_scripts += CreateScrollPositionScript();
|
_scripts += CreateScrollPositionScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
_headResources += ParseScripts(site.HeadContent);
|
|
||||||
_bodyResources += ParseScripts(site.BodyContent);
|
|
||||||
|
|
||||||
// set culture if not specified
|
// set culture if not specified
|
||||||
string cultureCookie = Context.Request.Cookies[Shared.CookieRequestCultureProvider.DefaultCookieName];
|
string cultureCookie = Context.Request.Cookies[Shared.CookieRequestCultureProvider.DefaultCookieName];
|
||||||
if (cultureCookie == null)
|
if (cultureCookie == null)
|
||||||
|
@ -510,26 +511,10 @@
|
||||||
"</script>" + Environment.NewLine;
|
"</script>" + Environment.NewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseScripts(string content)
|
|
||||||
{
|
|
||||||
var scripts = "";
|
|
||||||
// in interactive render mode, parse scripts from content and inject into page
|
|
||||||
if (_renderMode == RenderModes.Interactive && !string.IsNullOrEmpty(content))
|
|
||||||
{
|
|
||||||
var index = content.IndexOf("<script");
|
|
||||||
while (index >= 0)
|
|
||||||
{
|
|
||||||
scripts += content.Substring(index, content.IndexOf("</script>", index) + 9 - index);
|
|
||||||
index = content.IndexOf("<script", index + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddScript(Resource resource, Alias alias)
|
private void AddScript(Resource resource, Alias alias)
|
||||||
{
|
{
|
||||||
var script = CreateScript(resource, alias);
|
var script = CreateScript(resource, alias);
|
||||||
if (resource.Location == Shared.ResourceLocation.Head)
|
if (resource.Location == Shared.ResourceLocation.Head && !resource.Reload)
|
||||||
{
|
{
|
||||||
if (!_headResources.Contains(script))
|
if (!_headResources.Contains(script))
|
||||||
{
|
{
|
||||||
|
@ -546,38 +531,31 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateScript(Resource resource, Alias alias)
|
private string CreateScript(Resource resource, Alias alias)
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(resource.Url))
|
|
||||||
{
|
{
|
||||||
if (!resource.Reload)
|
if (!resource.Reload)
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||||
return "<script" +
|
|
||||||
|
var dataAttributes = "";
|
||||||
|
if (resource.DataAttributes != null && resource.DataAttributes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var attribute in resource.DataAttributes)
|
||||||
|
{
|
||||||
|
dataAttributes += " " + attribute.Key + "=\"" + attribute.Value + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<script src=\"" + url + "\"" +
|
||||||
|
((!string.IsNullOrEmpty(resource.Type)) ? " type=\"" + resource.Type + "\"" : "") +
|
||||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||||
((resource.ES6Module) ? " type=\"module\"" : "") +
|
((!string.IsNullOrEmpty(dataAttributes)) ? dataAttributes : "") +
|
||||||
" src=\"" + url + "\"></script>"; // src at end of element due to enhanced navigation patch algorithm
|
"></script>";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
// use custom element which can execute script on every page transition
|
|
||||||
@if (string.IsNullOrEmpty(resource.Integrity) && string.IsNullOrEmpty(resource.CrossOrigin))
|
|
||||||
{
|
{
|
||||||
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// use modulepreload for external resources
|
|
||||||
return "<link rel=\"modulepreload\" href=\"" + resource.Url + "\" integrity=\"" + resource.Integrity + "\" crossorigin=\"" + resource.CrossOrigin + "\" />\n" +
|
|
||||||
"<page-script src=\"" + resource.Url + "\"></page-script>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// inline script
|
|
||||||
return "<script>" + resource.Content + "</script>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetLocalizationCookie(string cookieValue)
|
private void SetLocalizationCookie(string cookieValue)
|
||||||
|
|
|
@ -183,7 +183,7 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && notification.NotificationId == id && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && notification.NotificationId == id && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
||||||
{
|
{
|
||||||
if (!User.IsInRole(RoleNames.Admin))
|
if (!User.IsInRole(RoleNames.Admin) && notification.FromUserId != null)
|
||||||
{
|
{
|
||||||
// content must be HTML encoded for non-admins to prevent HTML injection
|
// content must be HTML encoded for non-admins to prevent HTML injection
|
||||||
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
||||||
|
@ -223,7 +223,7 @@ namespace Oqtane.Controllers
|
||||||
|
|
||||||
private bool IsAuthorized(int? userid)
|
private bool IsAuthorized(int? userid)
|
||||||
{
|
{
|
||||||
bool authorized = true;
|
bool authorized = false;
|
||||||
if (userid != null)
|
if (userid != null)
|
||||||
{
|
{
|
||||||
authorized = (_userPermissions.GetUser(User).UserId == userid);
|
authorized = (_userPermissions.GetUser(User).UserId == userid);
|
||||||
|
|
|
@ -9,7 +9,7 @@ using System.Net;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using System.IO;
|
using System;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
|
|
|
@ -269,11 +269,7 @@ namespace Oqtane.Controllers
|
||||||
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, permissionName);
|
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, permissionName);
|
||||||
break;
|
break;
|
||||||
case EntityNames.User:
|
case EntityNames.User:
|
||||||
authorized = true;
|
|
||||||
if (permissionName == PermissionNames.Edit)
|
|
||||||
{
|
|
||||||
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
|
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case EntityNames.Visitor:
|
case EntityNames.Visitor:
|
||||||
authorized = User.IsInRole(RoleNames.Admin);
|
authorized = User.IsInRole(RoleNames.Admin);
|
||||||
|
@ -319,7 +315,7 @@ namespace Oqtane.Controllers
|
||||||
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, PermissionNames.Edit);
|
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, PermissionNames.Edit);
|
||||||
break;
|
break;
|
||||||
case EntityNames.User:
|
case EntityNames.User:
|
||||||
filter = !User.IsInRole(RoleNames.Admin) && _userPermissions.GetUser(User).UserId != entityId;
|
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) && _userPermissions.GetUser(User).UserId != entityId;
|
||||||
break;
|
break;
|
||||||
case EntityNames.Visitor:
|
case EntityNames.Visitor:
|
||||||
if (!User.IsInRole(RoleNames.Admin))
|
if (!User.IsInRole(RoleNames.Admin))
|
||||||
|
|
|
@ -28,9 +28,10 @@ namespace Oqtane.Controllers
|
||||||
private readonly IJwtManager _jwtManager;
|
private readonly IJwtManager _jwtManager;
|
||||||
private readonly IFileRepository _files;
|
private readonly IFileRepository _files;
|
||||||
private readonly ISettingRepository _settings;
|
private readonly ISettingRepository _settings;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
|
|
||||||
public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, IFileRepository files, ISettingRepository settings, ILogManager logger)
|
public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, IFileRepository files, ISettingRepository settings, ISyncManager syncManager, ILogManager logger)
|
||||||
{
|
{
|
||||||
_users = users;
|
_users = users;
|
||||||
_tenantManager = tenantManager;
|
_tenantManager = tenantManager;
|
||||||
|
@ -40,6 +41,7 @@ namespace Oqtane.Controllers
|
||||||
_jwtManager = jwtManager;
|
_jwtManager = jwtManager;
|
||||||
_files = files;
|
_files = files;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +138,7 @@ namespace Oqtane.Controllers
|
||||||
filtered.PhotoFileId = user.PhotoFileId;
|
filtered.PhotoFileId = user.PhotoFileId;
|
||||||
filtered.LastLoginOn = user.LastLoginOn;
|
filtered.LastLoginOn = user.LastLoginOn;
|
||||||
filtered.LastIPAddress = user.LastIPAddress;
|
filtered.LastIPAddress = user.LastIPAddress;
|
||||||
filtered.TwoFactorRequired = false;
|
filtered.TwoFactorRequired = user.TwoFactorRequired;
|
||||||
filtered.Roles = user.Roles;
|
filtered.Roles = user.Roles;
|
||||||
filtered.CreatedBy = user.CreatedBy;
|
filtered.CreatedBy = user.CreatedBy;
|
||||||
filtered.CreatedOn = user.CreatedOn;
|
filtered.CreatedOn = user.CreatedOn;
|
||||||
|
@ -145,20 +147,7 @@ namespace Oqtane.Controllers
|
||||||
filtered.DeletedBy = user.DeletedBy;
|
filtered.DeletedBy = user.DeletedBy;
|
||||||
filtered.DeletedOn = user.DeletedOn;
|
filtered.DeletedOn = user.DeletedOn;
|
||||||
filtered.IsDeleted = user.IsDeleted;
|
filtered.IsDeleted = user.IsDeleted;
|
||||||
}
|
filtered.Settings = user.Settings; // include all settings
|
||||||
|
|
||||||
// if authenticated user is accessing their own user account
|
|
||||||
if (_userPermissions.GetUser(User).UserId == user.UserId)
|
|
||||||
{
|
|
||||||
// include all settings
|
|
||||||
filtered.Settings = user.Settings;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// include only public settings
|
|
||||||
filtered.Settings = _settings.GetSettings(EntityNames.User, user.UserId)
|
|
||||||
.Where(item => !item.IsPrivate)
|
|
||||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +255,7 @@ namespace Oqtane.Controllers
|
||||||
if (_userPermissions.GetUser(User).UserId == user.UserId)
|
if (_userPermissions.GetUser(User).UserId == user.UserId)
|
||||||
{
|
{
|
||||||
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
||||||
|
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, "Logout");
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", (user != null) ? user.Username : "");
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", (user != null) ? user.Username : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,6 +269,7 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
await _userManager.LogoutUserEverywhere(user);
|
await _userManager.LogoutUserEverywhere(user);
|
||||||
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
|
||||||
|
_syncManager.AddSyncEvent(_tenantManager.GetAlias(), EntityNames.User, user.UserId, "Logout");
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout Everywhere {Username}", (user != null) ? user.Username : "");
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout Everywhere {Username}", (user != null) ? user.Username : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using System;
|
using System;
|
||||||
using Oqtane.Modules.Admin.Roles;
|
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
|
@ -40,12 +39,14 @@ namespace Oqtane.Controllers
|
||||||
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
||||||
{
|
{
|
||||||
int SiteId;
|
int SiteId;
|
||||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && (userid != null || rolename != null))
|
int UserId = -1;
|
||||||
|
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && (userid != null && int.TryParse(userid, out UserId) || rolename != null))
|
||||||
|
{
|
||||||
|
if (IsAuthorized(UserId, rolename))
|
||||||
{
|
{
|
||||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||||
if (userid != null)
|
if (UserId != -1)
|
||||||
{
|
{
|
||||||
int UserId = int.TryParse(userid, out UserId) ? UserId : -1;
|
|
||||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||||
}
|
}
|
||||||
if (rolename != null)
|
if (rolename != null)
|
||||||
|
@ -58,6 +59,14 @@ namespace Oqtane.Controllers
|
||||||
userroles[i] = Filter(userroles[i], user.UserId);
|
userroles[i] = Filter(userroles[i], user.UserId);
|
||||||
}
|
}
|
||||||
return userroles.OrderBy(u => u.User.DisplayName);
|
return userroles.OrderBy(u => u.User.DisplayName);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt For Site {SiteId} User {UserId} Role {RoleName}", siteid, userid, rolename);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -73,7 +82,7 @@ namespace Oqtane.Controllers
|
||||||
public UserRole Get(int id)
|
public UserRole Get(int id)
|
||||||
{
|
{
|
||||||
var userrole = _userRoles.GetUserRole(id);
|
var userrole = _userRoles.GetUserRole(id);
|
||||||
if (userrole != null && SiteValid(userrole.Role.SiteId))
|
if (userrole != null && SiteValid(userrole.Role.SiteId) && IsAuthorized(userrole.UserId, userrole.Role.Name))
|
||||||
{
|
{
|
||||||
return Filter(userrole, _userPermissions.GetUser().UserId);
|
return Filter(userrole, _userPermissions.GetUser().UserId);
|
||||||
}
|
}
|
||||||
|
@ -92,33 +101,57 @@ namespace Oqtane.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsAuthorized(int userId, string roleName)
|
||||||
|
{
|
||||||
|
bool authorized = true;
|
||||||
|
if (userId != -1)
|
||||||
|
{
|
||||||
|
authorized = _userPermissions.GetUser(User).UserId == userId;
|
||||||
|
}
|
||||||
|
if (authorized && !string.IsNullOrEmpty(roleName))
|
||||||
|
{
|
||||||
|
authorized = User.IsInRole(roleName);
|
||||||
|
}
|
||||||
|
return authorized;
|
||||||
|
}
|
||||||
|
|
||||||
private UserRole Filter(UserRole userrole, int userid)
|
private UserRole Filter(UserRole userrole, int userid)
|
||||||
{
|
{
|
||||||
|
// clone object to avoid mutating cache
|
||||||
|
UserRole filtered = null;
|
||||||
|
|
||||||
if (userrole != null)
|
if (userrole != null)
|
||||||
{
|
{
|
||||||
userrole.User.Password = "";
|
filtered = new UserRole();
|
||||||
userrole.User.IsAuthenticated = false;
|
|
||||||
userrole.User.TwoFactorCode = "";
|
|
||||||
userrole.User.TwoFactorExpiry = null;
|
|
||||||
|
|
||||||
if (!_userPermissions.IsAuthorized(User, userrole.User.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) && userid != userrole.User.UserId)
|
// public properties
|
||||||
|
filtered.UserRoleId = userrole.UserRoleId;
|
||||||
|
filtered.UserId = userrole.UserId;
|
||||||
|
filtered.RoleId = userrole.RoleId;
|
||||||
|
|
||||||
|
filtered.User = new User();
|
||||||
|
filtered.User.SiteId = userrole.User.SiteId;
|
||||||
|
filtered.User.UserId = userrole.User.UserId;
|
||||||
|
filtered.User.Username = userrole.User.Username;
|
||||||
|
filtered.User.DisplayName = userrole.User.DisplayName;
|
||||||
|
|
||||||
|
filtered.Role = new Role();
|
||||||
|
filtered.Role.SiteId = userrole.Role.SiteId;
|
||||||
|
filtered.Role.RoleId = userrole.Role.RoleId;
|
||||||
|
filtered.Role.Name = userrole.Role.Name;
|
||||||
|
|
||||||
|
// include private properties if administrator
|
||||||
|
if (_userPermissions.IsAuthorized(User, filtered.User.SiteId, EntityNames.UserRole, -1, PermissionNames.Write, RoleNames.Admin))
|
||||||
{
|
{
|
||||||
userrole.User.Email = "";
|
filtered.User.Email = userrole.User.Email;
|
||||||
userrole.User.PhotoFileId = null;
|
filtered.User.PhotoFileId = userrole.User.PhotoFileId;
|
||||||
userrole.User.LastLoginOn = DateTime.MinValue;
|
filtered.User.LastLoginOn = userrole.User.LastLoginOn;
|
||||||
userrole.User.LastIPAddress = "";
|
filtered.User.LastIPAddress = userrole.User.LastIPAddress;
|
||||||
userrole.User.Roles = "";
|
filtered.User.CreatedOn = userrole.User.CreatedOn;
|
||||||
userrole.User.CreatedBy = "";
|
|
||||||
userrole.User.CreatedOn = DateTime.MinValue;
|
|
||||||
userrole.User.ModifiedBy = "";
|
|
||||||
userrole.User.ModifiedOn = DateTime.MinValue;
|
|
||||||
userrole.User.DeletedBy = "";
|
|
||||||
userrole.User.DeletedOn = DateTime.MinValue;
|
|
||||||
userrole.User.IsDeleted = false;
|
|
||||||
userrole.User.TwoFactorRequired = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return userrole;
|
|
||||||
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/<controller>
|
// POST api/<controller>
|
||||||
|
|
|
@ -645,6 +645,9 @@ namespace Oqtane.Extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _syncManager = httpContext.RequestServices.GetRequiredService<ISyncManager>();
|
||||||
|
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, "Login");
|
||||||
|
|
||||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} From IP Address {IPAddress} Using Provider {Provider}", user.Username, httpContext.Connection.RemoteIpAddress.ToString(), providerName);
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} From IP Address {IPAddress} Using Provider {Provider}", user.Username, httpContext.Connection.RemoteIpAddress.ToString(), providerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ namespace Oqtane.Infrastructure
|
||||||
if (userid != null && username != null)
|
if (userid != null && username != null)
|
||||||
{
|
{
|
||||||
var _users = context.RequestServices.GetService(typeof(IUserManager)) as IUserManager;
|
var _users = context.RequestServices.GetService(typeof(IUserManager)) as IUserManager;
|
||||||
var user = _users.GetUser(userid, alias.SiteId); // cached
|
var user = _users.GetUser(int.Parse(userid), alias.SiteId); // cached
|
||||||
if (user != null && !user.IsDeleted)
|
if (user != null && !user.IsDeleted)
|
||||||
{
|
{
|
||||||
var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user);
|
var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
@ -17,12 +17,14 @@ namespace Oqtane.Infrastructure
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IWebHostEnvironment _environment;
|
||||||
private readonly IConfigManager _configManager;
|
private readonly IConfigManager _configManager;
|
||||||
|
private readonly ILogger<UpgradeManager> _filelogger;
|
||||||
|
|
||||||
public UpgradeManager(IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IConfigManager configManager)
|
public UpgradeManager(IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IConfigManager configManager, ILogger<UpgradeManager> filelogger)
|
||||||
{
|
{
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
_configManager = configManager;
|
_configManager = configManager;
|
||||||
|
_filelogger = filelogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Upgrade(Tenant tenant, string version)
|
public void Upgrade(Tenant tenant, string version)
|
||||||
|
@ -69,6 +71,9 @@ namespace Oqtane.Infrastructure
|
||||||
case "5.2.1":
|
case "5.2.1":
|
||||||
Upgrade_5_2_1(tenant, scope);
|
Upgrade_5_2_1(tenant, scope);
|
||||||
break;
|
break;
|
||||||
|
case "6.0.1":
|
||||||
|
Upgrade_6_0_1(tenant, scope);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +93,7 @@ namespace Oqtane.Infrastructure
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// error deleting directory
|
// error deleting directory
|
||||||
Debug.WriteLine($"Oqtane Error: Error In 2.0.2 Upgrade Logic - {ex}");
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: Error In 2.0.2 Upgrade Logic - {ex}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +111,7 @@ namespace Oqtane.Infrastructure
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// error populating guid
|
// error populating guid
|
||||||
Debug.WriteLine($"Oqtane Error: Error In 2.0.2 Upgrade Logic - {ex}");
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: Error In 2.0.2 Upgrade Logic - {ex}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +279,7 @@ namespace Oqtane.Infrastructure
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Oqtane Error: Error In 3.2.0 Upgrade Logic - {ex}");
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: Error In 3.2.0 Upgrade Logic - {ex}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +315,7 @@ namespace Oqtane.Infrastructure
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Oqtane Error: Error In 3.2.1 Upgrade Logic - {ex}");
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: Error In 3.2.1 Upgrade Logic - {ex}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +359,7 @@ namespace Oqtane.Infrastructure
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Oqtane Error: Error In 3.3.0 Upgrade Logic - {ex}");
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: Error In 3.3.0 Upgrade Logic - {ex}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +376,7 @@ namespace Oqtane.Infrastructure
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// delete legacy Views assemblies which will cause startup errors due to missing HostModel
|
// delete legacy Views assemblies which will cause startup errors due to missing HostModel
|
||||||
// note that the following files will be deleted however the framework has already started up so a restart will be required
|
// note that the following files will be deleted however the framework has already started up so another restart will be required
|
||||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
var filepath = Path.Combine(binFolder, "Oqtane.Server.Views.dll");
|
var filepath = Path.Combine(binFolder, "Oqtane.Server.Views.dll");
|
||||||
if (System.IO.File.Exists(filepath)) System.IO.File.Delete(filepath);
|
if (System.IO.File.Exists(filepath)) System.IO.File.Delete(filepath);
|
||||||
|
@ -381,7 +386,7 @@ namespace Oqtane.Infrastructure
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// error deleting file
|
// error deleting file
|
||||||
Debug.WriteLine($"Oqtane Error: Error In 5.1.0 Upgrade Logic - {ex}");
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: Error In 5.1.0 Upgrade Logic - {ex}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,6 +446,54 @@ namespace Oqtane.Infrastructure
|
||||||
AddPagesToSites(scope, tenant, pageTemplates);
|
AddPagesToSites(scope, tenant, pageTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Upgrade_6_0_1(Tenant tenant, IServiceScope scope)
|
||||||
|
{
|
||||||
|
// assemblies which have been relocated to the bin/refs folder in .NET 9
|
||||||
|
string[] assemblies = {
|
||||||
|
"Microsoft.AspNetCore.Authorization.dll",
|
||||||
|
"Microsoft.AspNetCore.Components.Authorization.dll",
|
||||||
|
"Microsoft.AspNetCore.Components.dll",
|
||||||
|
"Microsoft.AspNetCore.Components.Forms.dll",
|
||||||
|
"Microsoft.AspNetCore.Components.Web.dll",
|
||||||
|
"Microsoft.AspNetCore.Cryptography.Internal.dll",
|
||||||
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation.dll",
|
||||||
|
"Microsoft.AspNetCore.Metadata.dll",
|
||||||
|
"Microsoft.Extensions.Caching.Memory.dll",
|
||||||
|
"Microsoft.Extensions.Configuration.Binder.dll",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions.dll",
|
||||||
|
"Microsoft.Extensions.Configuration.Json.dll",
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions.dll",
|
||||||
|
"Microsoft.Extensions.DependencyInjection.dll",
|
||||||
|
"Microsoft.Extensions.Diagnostics.Abstractions.dll",
|
||||||
|
"Microsoft.Extensions.Diagnostics.dll",
|
||||||
|
"Microsoft.Extensions.Http.dll",
|
||||||
|
"Microsoft.Extensions.Identity.Core.dll",
|
||||||
|
"Microsoft.Extensions.Identity.Stores.dll",
|
||||||
|
"Microsoft.Extensions.Localization.Abstractions.dll",
|
||||||
|
"Microsoft.Extensions.Localization.dll",
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions.dll",
|
||||||
|
"Microsoft.Extensions.Logging.dll",
|
||||||
|
"Microsoft.Extensions.Options.dll",
|
||||||
|
"Microsoft.JSInterop.dll",
|
||||||
|
"System.Text.Json.dll"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
var filepath = Path.Combine(binFolder, assembly);
|
||||||
|
if (System.IO.File.Exists(filepath)) System.IO.File.Delete(filepath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// error deleting asesmbly
|
||||||
|
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: 6.0.1 Upgrade Error Removing {assembly} - {ex}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
||||||
{
|
{
|
||||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||||
|
|
|
@ -339,20 +339,20 @@ namespace Oqtane.Managers
|
||||||
user = _users.GetUser(user.Username);
|
user = _users.GetUser(user.Username);
|
||||||
if (!user.IsDeleted)
|
if (!user.IsDeleted)
|
||||||
{
|
{
|
||||||
if (user.TwoFactorRequired)
|
var alias = _tenantManager.GetAlias();
|
||||||
|
var twoFactorSetting = _settings.GetSetting(EntityNames.Site, alias.SiteId, "LoginOptions:TwoFactor")?.SettingValue ?? "false";
|
||||||
|
var twoFactorRequired = twoFactorSetting == "required" || user.TwoFactorRequired;
|
||||||
|
if (twoFactorRequired)
|
||||||
{
|
{
|
||||||
var token = await _identityUserManager.GenerateTwoFactorTokenAsync(identityuser, "Email");
|
var token = await _identityUserManager.GenerateTwoFactorTokenAsync(identityuser, "Email");
|
||||||
user.TwoFactorCode = token;
|
user.TwoFactorCode = token;
|
||||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
var alias = _tenantManager.GetAlias();
|
|
||||||
string url = alias.Protocol + alias.Name;
|
|
||||||
string siteName = _sites.GetSite(alias.SiteId).Name;
|
string siteName = _sites.GetSite(alias.SiteId).Name;
|
||||||
string subject = _localizer["TwoFactorEmailSubject"];
|
string subject = _localizer["TwoFactorEmailSubject"];
|
||||||
subject = subject.Replace("[SiteName]", siteName);
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
string body = _localizer["TwoFactorEmailBody"].Value;
|
string body = _localizer["TwoFactorEmailBody"].Value;
|
||||||
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
body = body.Replace("[URL]", url);
|
|
||||||
body = body.Replace("[SiteName]", siteName);
|
body = body.Replace("[SiteName]", siteName);
|
||||||
body = body.Replace("[Token]", token);
|
body = body.Replace("[Token]", token);
|
||||||
var notification = new Notification(alias.SiteId, user, subject, body);
|
var notification = new Notification(alias.SiteId, user, subject, body);
|
||||||
|
@ -374,6 +374,8 @@ namespace Oqtane.Managers
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful For {Username} From IP Address {IPAddress}", user.Username, LastIPAddress);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful For {Username} From IP Address {IPAddress}", user.Username, LastIPAddress);
|
||||||
|
|
||||||
|
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, "Login");
|
||||||
|
|
||||||
if (setCookie)
|
if (setCookie)
|
||||||
{
|
{
|
||||||
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
||||||
|
|
32
Oqtane.Server/Migrations/Tenant/06000101_AddLanguageName.cs
Normal file
32
Oqtane.Server/Migrations/Tenant/06000101_AddLanguageName.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Oqtane.Databases.Interfaces;
|
||||||
|
using Oqtane.Migrations.EntityBuilders;
|
||||||
|
using Oqtane.Repository;
|
||||||
|
|
||||||
|
namespace Oqtane.Migrations.Tenant
|
||||||
|
{
|
||||||
|
[DbContext(typeof(TenantDBContext))]
|
||||||
|
[Migration("Tenant.06.00.01.01")]
|
||||||
|
public class AddLanguageName : MultiDatabaseMigration
|
||||||
|
{
|
||||||
|
public AddLanguageName(IDatabase database) : base(database)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
// Name column was removed in 5.2.4 however SQLite does not support column removal so it had to be restored
|
||||||
|
if (ActiveDatabase.Name != "Sqlite")
|
||||||
|
{
|
||||||
|
var languageEntityBuilder = new LanguageEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
languageEntityBuilder.AddStringColumn("Name", 100, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
<DefineConstants>$(DefineConstants);OQTANE;OQTANE3</DefineConstants>
|
<DefineConstants>$(DefineConstants);OQTANE;OQTANE3</DefineConstants>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
||||||
|
<CompressionEnabled>false</CompressionEnabled>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="wwwroot\Modules\Templates\**" />
|
<Compile Remove="wwwroot\Modules\Templates\**" />
|
||||||
|
|
|
@ -35,6 +35,7 @@ namespace Oqtane.Pages
|
||||||
{
|
{
|
||||||
await _userManager.LogoutUserEverywhere(user);
|
await _userManager.LogoutUserEverywhere(user);
|
||||||
}
|
}
|
||||||
|
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, "Logout");
|
||||||
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, SyncEventActions.Reload);
|
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, SyncEventActions.Reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace Oqtane.Repository
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(l => l.IsDefault = false);
|
.ForEach(l => l.IsDefault = false);
|
||||||
}
|
}
|
||||||
|
language.Name = ""; // stored in database but not used (SQLite limitation)
|
||||||
db.Language.Add(language);
|
db.Language.Add(language);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ namespace Oqtane.Repository
|
||||||
.ForEach(l => l.IsDefault = false);
|
.ForEach(l => l.IsDefault = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
language.Name = ""; // stored in database but not used (SQLite limitation)
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -31,7 +32,47 @@ namespace Oqtane.Repository
|
||||||
{
|
{
|
||||||
page.PermissionList = permissions.Where(item => item.EntityId == page.PageId).ToList();
|
page.PermissionList = permissions.Where(item => item.EntityId == page.PageId).ToList();
|
||||||
}
|
}
|
||||||
return pages;
|
return GetPagesHierarchy(pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Page> GetPagesHierarchy(List<Page> pages)
|
||||||
|
{
|
||||||
|
List<Page> hierarchy = new List<Page>();
|
||||||
|
Action<List<Page>, Page> getPath = null;
|
||||||
|
getPath = (pageList, page) =>
|
||||||
|
{
|
||||||
|
IEnumerable<Page> children;
|
||||||
|
int level;
|
||||||
|
if (page == null)
|
||||||
|
{
|
||||||
|
level = -1;
|
||||||
|
children = pages.Where(item => item.ParentId == null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
level = page.Level;
|
||||||
|
children = pages.Where(item => item.ParentId == page.PageId);
|
||||||
|
}
|
||||||
|
foreach (Page child in children)
|
||||||
|
{
|
||||||
|
child.Level = level + 1;
|
||||||
|
child.HasChildren = pages.Any(item => item.ParentId == child.PageId && !item.IsDeleted && item.IsNavigation);
|
||||||
|
hierarchy.Add(child);
|
||||||
|
getPath(pageList, child);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pages = pages.OrderBy(item => item.Order).ToList();
|
||||||
|
getPath(pages, null);
|
||||||
|
|
||||||
|
// add any non-hierarchical items to the end of the list
|
||||||
|
foreach (Page page in pages)
|
||||||
|
{
|
||||||
|
if (hierarchy.Find(item => item.PageId == page.PageId) == null)
|
||||||
|
{
|
||||||
|
hierarchy.Add(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page AddPage(Page page)
|
public Page AddPage(Page page)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
@ -118,7 +118,7 @@
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ForgotPasswordEmailBody" xml:space="preserve">
|
<data name="ForgotPasswordEmailBody" xml:space="preserve">
|
||||||
<value>Dear [UserDisplayName]<br><br>You recently requested to reset your password. Please use the link below to complete the process: <b><a href="[URL]"><br><br>Click here to Reset Password</a></b><br><br>Please 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.<br><br>If you did not request to reset your password you can safely ignore this message.<br><br>Thank You!<br>[SiteName] team</value>
|
<value>Dear [UserDisplayName]<br><br>You recently requested to reset your password. Please use the link below to complete the process: <b><a href="[URL]"><br><br>Click here to Reset Password</a></b><br><br>Please 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.<br><br>If you did not request to reset your password you can safely ignore this message.<br><br>Thank You!<br>[SiteName] Team</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ForgotPasswordEmailSubject" xml:space="preserve">
|
<data name="ForgotPasswordEmailSubject" xml:space="preserve">
|
||||||
<value>Password Reset Notification Sent For [SiteName]</value>
|
<value>Password Reset Notification Sent For [SiteName]</value>
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
<value>User Account Notification for [SiteName]</value>
|
<value>User Account Notification for [SiteName]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TwoFactorEmailBody" xml:space="preserve">
|
<data name="TwoFactorEmailBody" xml:space="preserve">
|
||||||
<value>Dear [UserDisplayName] + ",<br><br>You requested a secure verification code to log in to your account. Please enter the secure verification code on the site:<br><br><b>[Token] </b><br><br>Please note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the [Alias].<br><br>Thank You!<br>[SiteName] Team"</value>
|
<value>Dear [UserDisplayName],<br><br>You requested a secure verification code to log in to your account. Please enter the secure verification code on the site:<br><br><b>[Token] </b><br><br>Please note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the site.<br><br>Thank You!<br>[SiteName] Team</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TwoFactorEmailSubject" xml:space="preserve">
|
<data name="TwoFactorEmailSubject" xml:space="preserve">
|
||||||
<value>User Verification Code for [SiteName]</value>
|
<value>User Verification Code for [SiteName]</value>
|
||||||
|
|
|
@ -127,7 +127,6 @@ namespace Oqtane.Services
|
||||||
.ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + setting.SettingValue);
|
.ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + setting.SettingValue);
|
||||||
site.Pages.Add(page);
|
site.Pages.Add(page);
|
||||||
}
|
}
|
||||||
site.Pages = GetPagesHierarchy(site.Pages);
|
|
||||||
|
|
||||||
// framework modules
|
// framework modules
|
||||||
var modules = GetPageModules(site.SiteId);
|
var modules = GetPageModules(site.SiteId);
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="debug.cmd" />
|
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="debug.cmd $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="bash $(ProjectDir)debug.sh" />
|
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="bash $(ProjectDir)debug.sh $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd" />
|
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh" />
|
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>[Owner].Module.[Module]</id>
|
<id>$projectname$</id>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
<authors>[Owner]</authors>
|
<authors>[Owner]</authors>
|
||||||
<owners>[Owner]</owners>
|
<owners>[Owner]</owners>
|
||||||
|
@ -20,12 +20,12 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Client\bin\Release\net9.0\[Owner].Module.[Module].Client.Oqtane.dll" target="lib\net9.0" />
|
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.dll" target="lib\$targetframework$" />
|
||||||
<file src="..\Client\bin\Release\net9.0\[Owner].Module.[Module].Client.Oqtane.pdb" target="lib\net9.0" />
|
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.pdb" target="lib\$targetframework$" />
|
||||||
<file src="..\Server\bin\Release\net9.0\[Owner].Module.[Module].Server.Oqtane.dll" target="lib\net9.0" />
|
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.dll" target="lib\$targetframework$" />
|
||||||
<file src="..\Server\bin\Release\net9.0\[Owner].Module.[Module].Server.Oqtane.pdb" target="lib\net9.0" />
|
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.pdb" target="lib\$targetframework$" />
|
||||||
<file src="..\Shared\bin\Release\net9.0\[Owner].Module.[Module].Shared.Oqtane.dll" target="lib\net9.0" />
|
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.dll" target="lib\$targetframework$" />
|
||||||
<file src="..\Shared\bin\Release\net9.0\[Owner].Module.[Module].Shared.Oqtane.pdb" target="lib\net9.0" />
|
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.pdb" target="lib\$targetframework$" />
|
||||||
<file src="..\Server\wwwroot\**\*.*" target="wwwroot" />
|
<file src="..\Server\wwwroot\**\*.*" target="wwwroot" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
</files>
|
</files>
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Module.[Module].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
@echo off
|
||||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Module.[Module].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
set TargetFramework=%1
|
||||||
XCOPY "..\Server\bin\Debug\net9.0\[Owner].Module.[Module].Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
set ProjectName=%2
|
||||||
XCOPY "..\Server\bin\Debug\net9.0\[Owner].Module.[Module].Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
|
||||||
XCOPY "..\Shared\bin\Debug\net9.0\[Owner].Module.[Module].Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
XCOPY "..\Shared\bin\Debug\net9.0\[Owner].Module.[Module].Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
|
XCOPY "..\Server\bin\Debug\%TargetFramework%\%ProjectName%.Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
|
XCOPY "..\Server\bin\Debug\%TargetFramework%\%ProjectName%.Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
|
XCOPY "..\Shared\bin\Debug\%TargetFramework%\%ProjectName%.Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
|
XCOPY "..\Shared\bin\Debug\%TargetFramework%\%ProjectName%.Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
|
@ -1,7 +1,12 @@
|
||||||
cp -f "../Client/bin/Debug/net9.0/[Owner].Module.[Module].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
#!/bin/bash
|
||||||
cp -f "../Client/bin/Debug/net9.0/[Owner].Module.[Module].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
|
||||||
cp -f "../Server/bin/Debug/net9.0/[Owner].Module.[Module].Server.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
TargetFramework=$1
|
||||||
cp -f "../Server/bin/Debug/net9.0/[Owner].Module.[Module].Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
ProjectName=$2
|
||||||
cp -f "../Shared/bin/Debug/net9.0/[Owner].Module.[Module].Shared.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
|
||||||
cp -f "../Shared/bin/Debug/net9.0/[Owner].Module.[Module].Shared.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.dll" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
cp -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/"
|
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -f "../Server/bin/Debug/$TargetFramework/$ProjectName$.Server.Oqtane.dll" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -f "../Server/bin/Debug/$TargetFramework/$ProjectName$.Server.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -f "../Shared/bin/Debug/$TargetFramework/$ProjectName$.Shared.Oqtane.dll" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -f "../Shared/bin/Debug/$TargetFramework/$ProjectName$.Shared.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -rf "../Server/wwwroot/"* "../../[RootFolder]/Oqtane.Server/wwwroot/"
|
|
@ -1,4 +1,7 @@
|
||||||
del "*.nupkg"
|
@echo off
|
||||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].Module.[Module].nuspec
|
set TargetFramework=%1
|
||||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\" /Y
|
set ProjectName=%2
|
||||||
|
|
||||||
|
del "*.nupkg"
|
||||||
|
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||||
|
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\" /Y
|
|
@ -1,2 +1,5 @@
|
||||||
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack [Owner].Module.[Module].nuspec
|
TargetFramework=$1
|
||||||
cp -f "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\"
|
ProjectName=$2
|
||||||
|
|
||||||
|
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||||
|
cp -f "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\"
|
|
@ -18,10 +18,10 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="debug.cmd" />
|
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="debug.cmd $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="bash $(ProjectDir)debug.sh" />
|
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="bash $(ProjectDir)debug.sh $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd" />
|
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh" />
|
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>[Owner].Theme.[Theme]</id>
|
<id>$projectname$</id>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
<authors>[Owner]</authors>
|
<authors>[Owner]</authors>
|
||||||
<owners>[Owner]</owners>
|
<owners>[Owner]</owners>
|
||||||
|
@ -20,8 +20,8 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="..\Client\bin\Release\net9.0\[Owner].Theme.[Theme].Client.Oqtane.dll" target="lib\net9.0" />
|
<file src="..\Client\bin\Release\$targetframework$\$projectname$.Client.Oqtane.dll" target="lib\$targetframework$" />
|
||||||
<file src="..\Client\bin\Release\net9.0\[Owner].Theme.[Theme].Client.Oqtane.pdb" target="lib\net9.0" />
|
<file src="..\Client\bin\Release\$targetframework$\$projectname$.Client.Oqtane.pdb" target="lib\$targetframework$" />
|
||||||
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
|
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
|
||||||
<file src="icon.png" target="" />
|
<file src="icon.png" target="" />
|
||||||
</files>
|
</files>
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Theme.[Theme].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
@echo off
|
||||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Theme.[Theme].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
set TargetFramework=%1
|
||||||
|
set ProjectName=%2
|
||||||
|
|
||||||
|
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
|
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||||
XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
|
@ -1,3 +1,8 @@
|
||||||
cp -f "../Client/bin/Debug/net9.0/[Owner].Theme.[Theme].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
#!/bin/bash
|
||||||
cp -f "../Client/bin/Debug/net9.0/[Owner].Theme.[Theme].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
|
||||||
cp -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/"
|
TargetFramework=$1
|
||||||
|
ProjectName=$2
|
||||||
|
|
||||||
|
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.dll" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||||
|
cp -rf "../Server/wwwroot/"* "../../[RootFolder]/Oqtane.Server/wwwroot/"
|
|
@ -1,3 +1,7 @@
|
||||||
|
@echo off
|
||||||
|
set TargetFramework=%1
|
||||||
|
set ProjectName=%2
|
||||||
|
|
||||||
del "*.nupkg"
|
del "*.nupkg"
|
||||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].Theme.[Theme].nuspec
|
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Packages\" /Y
|
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Packages\" /Y
|
|
@ -1,2 +1,5 @@
|
||||||
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack [Owner].Theme.[Theme].nuspec
|
TargetFramework=$1
|
||||||
cp -f "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\"
|
ProjectName=$2
|
||||||
|
|
||||||
|
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||||
|
cp -f "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\"
|
|
@ -120,13 +120,22 @@ Oqtane.Interop = {
|
||||||
this.includeLink(links[i].id, links[i].rel, links[i].href, links[i].type, links[i].integrity, links[i].crossorigin, links[i].insertbefore);
|
this.includeLink(links[i].id, links[i].rel, links[i].href, links[i].type, links[i].integrity, links[i].crossorigin, links[i].insertbefore);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
includeScript: function (id, src, integrity, crossorigin, type, content, location) {
|
includeScript: function (id, src, integrity, crossorigin, type, content, location, dataAttributes) {
|
||||||
var script;
|
var script;
|
||||||
if (src !== "") {
|
if (src !== "") {
|
||||||
script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]");
|
script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (id !== "") {
|
||||||
script = document.getElementById(id);
|
script = document.getElementById(id);
|
||||||
|
} else {
|
||||||
|
const scripts = document.querySelectorAll("script:not([src])");
|
||||||
|
for (let i = 0; i < scripts.length; i++) {
|
||||||
|
if (scripts[i].textContent.includes(content)) {
|
||||||
|
script = scripts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (script !== null) {
|
if (script !== null) {
|
||||||
script.remove();
|
script.remove();
|
||||||
|
@ -152,37 +161,36 @@ Oqtane.Interop = {
|
||||||
else {
|
else {
|
||||||
script.innerHTML = content;
|
script.innerHTML = content;
|
||||||
}
|
}
|
||||||
script.async = false;
|
if (dataAttributes !== null) {
|
||||||
this.addScript(script, location)
|
for (var key in dataAttributes) {
|
||||||
.then(() => {
|
script.setAttribute(key, dataAttributes[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.addScript(script, location);
|
||||||
|
} catch (error) {
|
||||||
if (src !== "") {
|
if (src !== "") {
|
||||||
console.log(src + ' loaded');
|
console.error("Failed to load external script: ${src}", error);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load inline script: ${content}", error);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
console.log(id + ' loaded');
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
if (src !== "") {
|
|
||||||
console.error(src + ' failed');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.error(id + ' failed');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addScript: function (script, location) {
|
addScript: function (script, location) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
script.async = false;
|
||||||
|
script.defer = false;
|
||||||
|
|
||||||
|
script.onload = () => resolve();
|
||||||
|
script.onerror = (error) => reject(error);
|
||||||
|
|
||||||
if (location === 'head') {
|
if (location === 'head') {
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
}
|
} else {
|
||||||
if (location === 'body') {
|
|
||||||
document.body.appendChild(script);
|
document.body.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((res, rej) => {
|
|
||||||
script.onload = res();
|
|
||||||
script.onerror = rej();
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
includeScripts: async function (scripts) {
|
includeScripts: async function (scripts) {
|
||||||
|
@ -222,10 +230,10 @@ Oqtane.Interop = {
|
||||||
if (scripts[s].crossorigin !== '') {
|
if (scripts[s].crossorigin !== '') {
|
||||||
element.crossOrigin = scripts[s].crossorigin;
|
element.crossOrigin = scripts[s].crossorigin;
|
||||||
}
|
}
|
||||||
if (scripts[s].es6module === true) {
|
if (scripts[s].type !== '') {
|
||||||
element.type = "module";
|
element.type = scripts[s].type;
|
||||||
}
|
}
|
||||||
if (typeof scripts[s].dataAttributes !== "undefined" && scripts[s].dataAttributes !== null) {
|
if (scripts[s].dataAttributes !== null) {
|
||||||
for (var key in scripts[s].dataAttributes) {
|
for (var key in scripts[s].dataAttributes) {
|
||||||
element.setAttribute(key, scripts[s].dataAttributes[key]);
|
element.setAttribute(key, scripts[s].dataAttributes[key]);
|
||||||
}
|
}
|
||||||
|
|
80
Oqtane.Server/wwwroot/js/reload.js
Normal file
80
Oqtane.Server/wwwroot/js/reload.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
const scriptInfoBySrc = new Map();
|
||||||
|
|
||||||
|
function getKey(script) {
|
||||||
|
if (script.hasAttribute("src") && script.src !== "") {
|
||||||
|
return script.src;
|
||||||
|
} else {
|
||||||
|
return script.innerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onUpdate() {
|
||||||
|
let timestamp = Date.now();
|
||||||
|
let enhancedNavigation = scriptInfoBySrc.size !== 0;
|
||||||
|
|
||||||
|
// iterate over all script elements in page
|
||||||
|
const scripts = document.getElementsByTagName("script");
|
||||||
|
for (const script of Array.from(scripts)) {
|
||||||
|
let key = getKey(script);
|
||||||
|
let scriptInfo = scriptInfoBySrc.get(key);
|
||||||
|
if (!scriptInfo) {
|
||||||
|
// new script added
|
||||||
|
scriptInfo = { timestamp: timestamp };
|
||||||
|
scriptInfoBySrc.set(key, scriptInfo);
|
||||||
|
if (enhancedNavigation) {
|
||||||
|
reloadScript(script);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// existing script
|
||||||
|
scriptInfo.timestamp = timestamp;
|
||||||
|
if (script.hasAttribute("data-reload") && script.getAttribute("data-reload") === "true") {
|
||||||
|
reloadScript(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove scripts that are no longer referenced
|
||||||
|
for (const [key, scriptInfo] of scriptInfoBySrc) {
|
||||||
|
if (scriptInfo.timestamp !== timestamp) {
|
||||||
|
scriptInfoBySrc.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadScript(script) {
|
||||||
|
try {
|
||||||
|
replaceScript(script);
|
||||||
|
} catch (error) {
|
||||||
|
if (script.hasAttribute("src") && script.src !== "") {
|
||||||
|
console.error("Failed to load external script: ${script.src}", error);
|
||||||
|
} else {
|
||||||
|
console.error("Failed to load inline script: ${script.innerHtml}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceScript(script) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var newScript = document.createElement("script");
|
||||||
|
|
||||||
|
// replicate attributes and content
|
||||||
|
for (let i = 0; i < script.attributes.length; i++) {
|
||||||
|
newScript.setAttribute(script.attributes[i].name, script.attributes[i].value);
|
||||||
|
}
|
||||||
|
newScript.innerHTML = script.innerHTML;
|
||||||
|
|
||||||
|
// dynamically injected scripts cannot be async or deferred
|
||||||
|
newScript.async = false;
|
||||||
|
newScript.defer = false;
|
||||||
|
|
||||||
|
newScript.onload = () => resolve();
|
||||||
|
newScript.onerror = (error) => reject(error);
|
||||||
|
|
||||||
|
// remove existing script
|
||||||
|
script.remove();
|
||||||
|
|
||||||
|
// replace with new script to force reload in Blazor
|
||||||
|
document.head.appendChild(newScript);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 72 KiB |
|
@ -56,7 +56,9 @@ namespace Oqtane.Models
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deprecated - not used
|
/// Deprecated
|
||||||
|
/// Note that this property still exists in the database because columns cannot be dropped in SQLite
|
||||||
|
/// Therefore the property must be retained/mapped even though the framework no longer uses it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? IsDeleted { get; set; }
|
public bool? IsDeleted { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,9 @@ namespace Oqtane.Models
|
||||||
public bool IsSystem { get; set; }
|
public bool IsSystem { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deprecated - not used
|
/// Deprecated
|
||||||
|
/// Note that this property still exists in the database because columns cannot be dropped in SQLite
|
||||||
|
/// Therefore the property must be retained/mapped even though the framework no longer uses it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? IsDeleted { get; set; }
|
public bool? IsDeleted { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ namespace Oqtane.Models
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsDefault { get; set; }
|
public bool IsDefault { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Language Name - corresponds to <see cref="Culture.DisplayName"/>, _not_ <see cref="Culture.Name"/>
|
/// Language Name - corresponds to <see cref="Culture.DisplayName"/>, _not_ <see cref="Culture.Name"/>
|
||||||
|
/// Note that this property still exists in the database because columns cannot be dropped in SQLite
|
||||||
|
/// Therefore the property must be retained/mapped even though the framework populates it from the Culture API
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -144,25 +144,25 @@ namespace Oqtane.Models
|
||||||
{
|
{
|
||||||
FromUserId = from.UserId;
|
FromUserId = from.UserId;
|
||||||
FromDisplayName = from.DisplayName;
|
FromDisplayName = from.DisplayName;
|
||||||
FromEmail = from.Email;
|
FromEmail = from.Email ?? "";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FromUserId = null;
|
FromUserId = null;
|
||||||
FromDisplayName = fromDisplayName;
|
FromDisplayName = fromDisplayName;
|
||||||
FromEmail = fromEmail;
|
FromEmail = fromEmail ?? "";
|
||||||
}
|
}
|
||||||
if (to != null)
|
if (to != null)
|
||||||
{
|
{
|
||||||
ToUserId = to.UserId;
|
ToUserId = to.UserId;
|
||||||
ToDisplayName = to.DisplayName;
|
ToDisplayName = to.DisplayName;
|
||||||
ToEmail = to.Email;
|
ToEmail = to.Email ?? "";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ToUserId = null;
|
ToUserId = null;
|
||||||
ToDisplayName = toDisplayName;
|
ToDisplayName = toDisplayName;
|
||||||
ToEmail = toEmail;
|
ToEmail = toEmail ?? "";
|
||||||
}
|
}
|
||||||
Subject = subject;
|
Subject = subject;
|
||||||
Body = body;
|
Body = body;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Models
|
namespace Oqtane.Models
|
||||||
|
@ -27,6 +29,11 @@ namespace Oqtane.Models
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For Scripts this allows type to be specified - not applicable to Stylesheets
|
||||||
|
/// </summary>
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Integrity checks to increase the security of resources accessed. Especially common in CDN resources.
|
/// Integrity checks to increase the security of resources accessed. Especially common in CDN resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -52,11 +59,6 @@ namespace Oqtane.Models
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ResourceLocation Location { get; set; }
|
public ResourceLocation Location { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For Scripts this allows type="module" registrations - not applicable to Stylesheets
|
|
||||||
/// </summary>
|
|
||||||
public bool ES6Module { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows specification of inline script - not applicable to Stylesheets
|
/// Allows specification of inline script - not applicable to Stylesheets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -72,6 +74,11 @@ namespace Oqtane.Models
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Reload { get; set; }
|
public bool Reload { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cusotm data-* attributes for scripts - not applicable to Stylesheets
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> DataAttributes { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The namespace of the component that declared the resource - only used in SiteRouter
|
/// The namespace of the component that declared the resource - only used in SiteRouter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -82,14 +89,22 @@ namespace Oqtane.Models
|
||||||
var resource = new Resource();
|
var resource = new Resource();
|
||||||
resource.ResourceType = ResourceType;
|
resource.ResourceType = ResourceType;
|
||||||
resource.Url = Url;
|
resource.Url = Url;
|
||||||
|
resource.Type = Type;
|
||||||
resource.Integrity = Integrity;
|
resource.Integrity = Integrity;
|
||||||
resource.CrossOrigin = CrossOrigin;
|
resource.CrossOrigin = CrossOrigin;
|
||||||
resource.Bundle = Bundle;
|
resource.Bundle = Bundle;
|
||||||
resource.Location = Location;
|
resource.Location = Location;
|
||||||
resource.ES6Module = ES6Module;
|
|
||||||
resource.Content = Content;
|
resource.Content = Content;
|
||||||
resource.RenderMode = RenderMode;
|
resource.RenderMode = RenderMode;
|
||||||
resource.Reload = Reload;
|
resource.Reload = Reload;
|
||||||
|
resource.DataAttributes = new Dictionary<string, string>();
|
||||||
|
if (DataAttributes != null && DataAttributes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var kvp in DataAttributes)
|
||||||
|
{
|
||||||
|
resource.DataAttributes.Add(kvp.Key, kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
resource.Level = level;
|
resource.Level = level;
|
||||||
resource.Namespace = name;
|
resource.Namespace = name;
|
||||||
return resource;
|
return resource;
|
||||||
|
@ -97,5 +112,18 @@ namespace Oqtane.Models
|
||||||
|
|
||||||
[Obsolete("ResourceDeclaration is deprecated", false)]
|
[Obsolete("ResourceDeclaration is deprecated", false)]
|
||||||
public ResourceDeclaration Declaration { get; set; }
|
public ResourceDeclaration Declaration { get; set; }
|
||||||
|
|
||||||
|
[Obsolete("ES6Module is deprecated. Use Type property instead for scripts.", false)]
|
||||||
|
public bool ES6Module
|
||||||
|
{
|
||||||
|
get => (Type == "module");
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
Type = "module";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
Oqtane.Shared/Models/Script.cs
Normal file
53
Oqtane.Shared/Models/Script.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Script inherits from Resource and offers constructors with parameters specific to Scripts
|
||||||
|
/// </summary>
|
||||||
|
public class Script : Resource
|
||||||
|
{
|
||||||
|
private void SetDefaults()
|
||||||
|
{
|
||||||
|
this.ResourceType = ResourceType.Script;
|
||||||
|
this.Location = ResourceLocation.Body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Script(string Src)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Src;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Script(string Content, string Type)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Content = Content;
|
||||||
|
this.Type = Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Script(string Src, string Integrity, string CrossOrigin)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Src;
|
||||||
|
this.Integrity = Integrity;
|
||||||
|
this.CrossOrigin = CrossOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Script(string Src, string Integrity, string CrossOrigin, string Type, string Content, ResourceLocation Location, string Bundle, bool Reload, Dictionary<string, string> DataAttributes, string RenderMode)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Src;
|
||||||
|
this.Integrity = Integrity;
|
||||||
|
this.CrossOrigin = CrossOrigin;
|
||||||
|
this.Type = Type;
|
||||||
|
this.Content = Content;
|
||||||
|
this.Location = Location;
|
||||||
|
this.Bundle = Bundle;
|
||||||
|
this.Reload = Reload;
|
||||||
|
this.DataAttributes = DataAttributes;
|
||||||
|
this.RenderMode = RenderMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
Oqtane.Shared/Models/Stylesheet.cs
Normal file
30
Oqtane.Shared/Models/Stylesheet.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stylesheet inherits from Resource and offers constructors with parameters specific to Stylesheets
|
||||||
|
/// </summary>
|
||||||
|
public class Stylesheet : Resource
|
||||||
|
{
|
||||||
|
private void SetDefaults()
|
||||||
|
{
|
||||||
|
this.ResourceType = ResourceType.Stylesheet;
|
||||||
|
this.Location = ResourceLocation.Head;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stylesheet(string Href)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Href;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stylesheet(string Href, string Integrity, string CrossOrigin)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Href;
|
||||||
|
this.Integrity = Integrity;
|
||||||
|
this.CrossOrigin = CrossOrigin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
|
|
@ -4,8 +4,8 @@ namespace Oqtane.Shared
|
||||||
{
|
{
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public static readonly string Version = "6.0.0";
|
public static readonly string Version = "6.0.1";
|
||||||
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0";
|
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1";
|
||||||
public const string PackageId = "Oqtane.Framework";
|
public const string PackageId = "Oqtane.Framework";
|
||||||
public const string ClientId = "Oqtane.Client";
|
public const string ClientId = "Oqtane.Client";
|
||||||
public const string UpdaterPackageId = "Oqtane.Updater";
|
public const string UpdaterPackageId = "Oqtane.Updater";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>6.0.0</Version>
|
<Version>6.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
|
28
README.md
28
README.md
|
@ -1,26 +1,28 @@
|
||||||
# Latest Release
|
|
||||||
|
|
||||||
[5.2.4](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4) was released on October 17, 2024 and is a maintenance release including 51 pull requests by 7 different contributors, pushing the total number of project commits all-time to over 5900. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
|
|
||||||
|
|
||||||
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json)
|
|
||||||
|
|
||||||
# Oqtane Framework
|
# Oqtane Framework
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).
|
Oqtane is an open source Content Management System (CMS) and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on modern .NET.
|
||||||
|
|
||||||
Oqtane is being developed based on some fundamental principles which are outlined in the [Oqtane Philosophy](https://www.oqtane.org/blog/!/20/oqtane-philosophy).
|
Oqtane allows you to "Build Applications, Not Infrastructure" which means that you can focus your investment on solving your unique business challenges rather than wasting time and effort on building general infrastructure.
|
||||||
|
|
||||||
Please note that this project is owned by the .NET Foundation and is governed by the **[.NET Foundation Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)**
|
Oqtane is "Rocket Fuel for Blazor" as it provides powerful capabilities to accelerate your Blazor development experience, providing scalable services and a composable UI which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).
|
||||||
|
|
||||||
# Getting Started (Version 5.x)
|
Oqtane is being developed based on some fundamental principles which are outlined in the [Oqtane Philosophy](https://www.oqtane.org/blog/!/20/oqtane-philosophy). This project is an official member of the .NET Foundation and is governed by the **[.NET Foundation Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)**
|
||||||
|
|
||||||
|
# Latest Release
|
||||||
|
|
||||||
|
[6.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0) was released on November 14, 2024 and is a major release including 39 pull requests by 6 different contributors, pushing the total number of project commits all-time to over 6000. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
|
||||||
|
|
||||||
|
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json)
|
||||||
|
|
||||||
|
# Getting Started (Version 6.x)
|
||||||
|
|
||||||
**Installing using source code from the Dev/Master branch:**
|
**Installing using source code from the Dev/Master branch:**
|
||||||
|
|
||||||
- Install **[.NET 8.0.10 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)**.
|
- Install **[.NET 9.0.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0)**.
|
||||||
|
|
||||||
- Install the latest edition (v17.9 or higher) of [Visual Studio 2022](https://visualstudio.microsoft.com/downloads) with the **ASP.NET and web development** workload enabled. Oqtane works with ALL editions of Visual Studio from Community to Enterprise. If you wish to use LocalDB for development ( not a requirement as Oqtane supports SQLite, mySQL, and PostgreSQL ) you must also install the **Data storage and processing**.
|
- Install the latest edition (v17.12 or higher) of [Visual Studio 2022](https://visualstudio.microsoft.com/downloads) with the **ASP.NET and web development** workload enabled. Oqtane works with ALL editions of Visual Studio from Community to Enterprise. If you wish to use LocalDB for development ( not a requirement as Oqtane supports SQLite, mySQL, and PostgreSQL ) you must also install the **Data storage and processing**.
|
||||||
|
|
||||||
- Clone (or download) the Oqtane Master or Dev branch source code to your local system.
|
- Clone (or download) the Oqtane Master or Dev branch source code to your local system.
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ Connect with other developers, get support, and share ideas by joining the Oqtan
|
||||||
# Roadmap
|
# Roadmap
|
||||||
This project is open source, and therefore is a work in progress...
|
This project is open source, and therefore is a work in progress...
|
||||||
|
|
||||||
6.0.0 (Nov 13, 2024)
|
[6.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v6.0.0) (Nov 14, 2024)
|
||||||
- [x] Migration to .NET 9
|
- [x] Migration to .NET 9
|
||||||
|
|
||||||
[5.2.4](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4) (Oct 17, 2024)
|
[5.2.4](https://github.com/oqtane/oqtane.framework/releases/tag/v5.2.4) (Oct 17, 2024)
|
||||||
|
|
15
SECURITY.md
Normal file
15
SECURITY.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
We make every effort to ensure rapid and thorough analysis of reported issues and, where appropriate, provide workarounds and updated application releases to fix them. If you identify a potential security vulnerability please report it via [the GitHub feature for reporting a security vulnerability](https://github.com/oqtane/oqtane.framework/security/advisories/new).
|
||||||
|
|
||||||
|
All submitted information is viewed only by members of the Oqtane Security Team, and will not be discussed outside the Team without the permission of the person/company who reported the issue. Each confirmed issue is assigned a severity level (critical, moderate, or low) corresponding to its potential impact on an Oqtane installation.
|
||||||
|
|
||||||
|
* **Critical** means the issue can be exploited by a remote attacker to gain access to data or functionality. All critical issue security bulletins include a recommended workaround or fix that should be applied as soon as possible.
|
||||||
|
* **Moderate** means the issue can compromise data or functionality on a portal/website only if some other condition is met (e.g. a particular module or a user within a particular role is required). Moderate issue security bulletins typically include recommended actions to resolve the issue.
|
||||||
|
* **Low** means the issue is very difficult to exploit or has a limited potential impact.
|
||||||
|
|
||||||
|
Once an issue has been resolved via a public release of Oqtane, the release notes on GitHub are updated to reflect that security bulletins exist for the release. We strongly suggest using the "Watch" option on GitHub for "Releases" at a minimum to receive notifications of updated Oqtane releases.
|
||||||
|
|
||||||
|
As a general policy, Oqtane does not issue Hot Fix releases to prior versions of the software. If a remediation is possible via configuration it shall be noted as applicable in the posted bulletins.
|
Loading…
Reference in New Issue
Block a user