Compare commits
81 Commits
revert-487
...
v6.0.1
Author | SHA1 | Date | |
---|---|---|---|
8d7845a44d | |||
3a15e6e5e9 | |||
3b8a51e855 | |||
f2cb34cc35 | |||
723ce62a34 | |||
2c9a2ea021 | |||
2be008d6d1 | |||
7fb51bdd0a | |||
abdbe3694f | |||
bd87e5012f | |||
55e18f2364 | |||
655e84072d | |||
ab5409d5b6 | |||
5a5da6486c | |||
7e99252429 | |||
10e0dcef8b | |||
80c8433aad | |||
5b0ae372f8 | |||
b5a1b529ab | |||
af821dcd9a | |||
10d3c81520 | |||
e3811b453a | |||
ca0fb05baa | |||
2b4b01bf6e | |||
3a1244bddc | |||
b8fd922b19 | |||
03f856025e | |||
45f04d24c3 | |||
2435d610c7 | |||
06572bcd14 | |||
3fab79afc0 | |||
2b7dd3fed5 | |||
d65efed032 | |||
c6896ea936 | |||
b7a41bddec | |||
6fd80c3737 | |||
0aa690b3b1 | |||
6d5bcfc6ed | |||
b60de69fa5 | |||
d991b57d08 | |||
1133d7fcba | |||
6fbf0383bb | |||
0296230219 | |||
dedfbba27a | |||
dc5441da07 | |||
585648b7f3 | |||
cd0ee1c26d | |||
d7a7be5af4 | |||
13e4267c11 | |||
15bc47e3e8 | |||
1a4380dcd7 | |||
5ace34b5cd | |||
f010c0f1fa | |||
2c721ad5dd | |||
8a7c2ce2c2 | |||
b2a7b813de | |||
e85cf04b99 | |||
ab6fa48172 | |||
c81905882f | |||
f0d31c1114 | |||
497b255216 | |||
d96286d771 | |||
2441647d75 | |||
77b780d631 | |||
cdd03bf3d4 | |||
e786c35f7d | |||
e83399acb1 | |||
ffea9e3210 | |||
f71a3a1ce3 | |||
a5ccc23604 | |||
1ed4c8a094 | |||
784f3771b3 | |||
80316824f7 | |||
cbaebb7b7c | |||
97d3764b6e | |||
4c937be884 | |||
c25cce4398 | |||
58c422285a | |||
f2f22f35e8 | |||
15867a7807 | |||
e2c404d8bb |
@ -10,7 +10,7 @@
|
||||
@if (_initialized)
|
||||
{
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<TabStrip Refresh="@_refresh" ActiveTab="@_activetab">
|
||||
<TabStrip Refresh="@_refresh">
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -246,7 +246,6 @@
|
||||
private string _iconresources = "";
|
||||
private DateTime? _effectivedate = null;
|
||||
private DateTime? _expirydate = null;
|
||||
private string _activetab = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@ -317,10 +316,11 @@
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
private async Task ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
@ -331,12 +331,12 @@
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SavePage()
|
||||
{
|
||||
_activetab = "Settings";
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
@ -347,6 +347,7 @@
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
|
||||
@ -397,12 +398,14 @@
|
||||
if (_pages.Any(item => item.Path == page.Path))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -470,6 +473,7 @@
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
|
||||
}
|
||||
@ -477,11 +481,13 @@
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
@if (_page.UserId == null)
|
||||
{
|
||||
<TabStrip Refresh="@_refresh" ActiveTab="@_activetab">
|
||||
<TabStrip Refresh="@_refresh">
|
||||
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -30,16 +30,16 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||
@foreach (Page page in _pages)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId)
|
||||
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
|
||||
<option value="-1"><@Localizer["SiteRoot"]></option>
|
||||
@foreach (Page page in _pages)
|
||||
{
|
||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId)
|
||||
{
|
||||
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
|
||||
}
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -230,10 +230,10 @@
|
||||
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
|
||||
<Pager Items="_pageModules">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["ModuleTitle"]</th>
|
||||
<th>@Localizer["ModuleDefinition"]</th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["ModuleTitle"]</th>
|
||||
<th>@Localizer["ModuleDefinition"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="Settings" Text="Edit" Path="@_actualpath" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
|
||||
@ -356,7 +356,6 @@
|
||||
private string _iconresources = "";
|
||||
private DateTime? _effectivedate = null;
|
||||
private DateTime? _expirydate = null;
|
||||
private string _activetab = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@ -480,10 +479,11 @@
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
private async Task ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
@ -495,6 +495,7 @@
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,7 +514,7 @@
|
||||
builder.OpenComponent(0, _themeSettingsType);
|
||||
builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary);
|
||||
builder.AddComponentReferenceCapture(2, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
|
||||
|
||||
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
@ -523,7 +524,6 @@
|
||||
|
||||
private async Task SavePage()
|
||||
{
|
||||
_activetab = "Settings";
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
@ -533,6 +533,7 @@
|
||||
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
|
||||
@ -583,12 +584,14 @@
|
||||
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -672,18 +675,21 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
await ScrollToPageTop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(p.Autocomplete))
|
||||
@ -149,22 +149,26 @@
|
||||
{
|
||||
@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
|
||||
{
|
||||
<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
|
||||
{
|
||||
@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
|
||||
{
|
||||
<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)
|
||||
{
|
||||
<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
|
||||
{
|
||||
<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
|
||||
{
|
||||
@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
|
||||
{
|
||||
<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>
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,11 +226,11 @@
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["From"]</th>
|
||||
<th>@Localizer["Subject"]</th>
|
||||
<th>@Localizer["Received"]</th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["From"]</th>
|
||||
<th>@Localizer["Subject"]</th>
|
||||
<th>@Localizer["Received"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" /></td>
|
||||
@ -230,13 +238,13 @@
|
||||
|
||||
@if (context.IsRead)
|
||||
{
|
||||
<td>@context.FromDisplayName</td>
|
||||
<td>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
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>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
@ -281,11 +289,11 @@
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Header>
|
||||
<th style="width: 1px;"></th>
|
||||
<th style="width: 1px;"></th>
|
||||
<th>@Localizer["To"]</th>
|
||||
<th>@Localizer["Subject"]</th>
|
||||
<th>@Localizer["Sent"]</th>
|
||||
<th style="width: 1px;"></th>
|
||||
<th style="width: 1px;"></th>
|
||||
<th>@Localizer["To"]</th>
|
||||
<th>@Localizer["Subject"]</th>
|
||||
<th>@Localizer["Sent"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" /></td>
|
||||
@ -363,7 +371,7 @@
|
||||
private File photo = null;
|
||||
private string _ImageFiles = string.Empty;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private Dictionary<string, string> userSettings;
|
||||
private string category = string.Empty;
|
||||
|
||||
private string filter = "to";
|
||||
@ -411,9 +419,9 @@
|
||||
photo = null;
|
||||
}
|
||||
|
||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
userSettings = PageState.User.Settings;
|
||||
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;
|
||||
|
||||
await LoadNotificationsAsync();
|
||||
@ -440,7 +448,7 @@
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
@ -483,7 +491,7 @@
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
|
||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||
@ -554,7 +562,7 @@
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
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))
|
||||
{
|
||||
@ -586,7 +594,7 @@
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
{
|
||||
var value = (string)e.Value;
|
||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||
}
|
||||
|
||||
private async Task Delete(Notification Notification)
|
||||
|
@ -128,7 +128,7 @@
|
||||
createdon = notification.CreatedOn.ToString();
|
||||
body = notification.Body;
|
||||
|
||||
if (title == "From")
|
||||
if (title == "From" && !notification.IsRead)
|
||||
{
|
||||
notification.IsRead = true;
|
||||
notification = await NotificationService.UpdateNotificationAsync(notification);
|
||||
|
@ -81,11 +81,11 @@
|
||||
{
|
||||
@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
|
||||
{
|
||||
<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>
|
||||
|
@ -89,8 +89,8 @@
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
{
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
|
||||
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
@ -110,11 +110,11 @@
|
||||
{
|
||||
@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
|
||||
{
|
||||
<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>
|
||||
@ -132,7 +132,7 @@
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
|
||||
}
|
||||
|
||||
@code {
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private int userid;
|
||||
@ -148,7 +148,7 @@
|
||||
private string lastipaddress;
|
||||
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private Dictionary<string, string> userSettings;
|
||||
private string category = string.Empty;
|
||||
|
||||
private string createdby;
|
||||
@ -181,7 +181,7 @@
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
|
||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||
userSettings = user.Settings;
|
||||
createdby = user.CreatedBy;
|
||||
createdon = user.CreatedOn;
|
||||
modifiedby = user.ModifiedBy;
|
||||
@ -202,7 +202,7 @@
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
@ -232,7 +232,7 @@
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId);
|
||||
await logger.LogInformation("User Saved {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
@ -266,7 +266,7 @@
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
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))
|
||||
{
|
||||
@ -293,7 +293,7 @@
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
{
|
||||
var value = (string)e.Value;
|
||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
|
@ -30,8 +30,6 @@ else
|
||||
[Parameter]
|
||||
public SecurityAccessLevel? Security { get; set; } // optional - can be used to specify SecurityAccessLevel
|
||||
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
@ -8,9 +8,18 @@
|
||||
@foreach (TabPanel tabPanel in _tabPanels)
|
||||
{
|
||||
<li class="nav-item" @key="tabPanel.Name">
|
||||
<a class="nav-link @(tabPanel.IsActive ? "active" : "")" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true" @onclick="() => SetActiveTab(tabPanel.Name)">
|
||||
@tabPanel.DisplayHeading()
|
||||
</a>
|
||||
@if (tabPanel.Name.ToLower() == ActiveTab.ToLower())
|
||||
{
|
||||
<a class="nav-link active" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||
@tabPanel.DisplayHeading()
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||
@tabPanel.DisplayHeading()
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@ -50,43 +59,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
if (PageState.QueryString.ContainsKey("tab"))
|
||||
{
|
||||
ActiveTab = PageState.QueryString["tab"];
|
||||
}
|
||||
|
||||
if (_tabPanels == null || Refresh)
|
||||
{
|
||||
_tabPanels = new List<TabPanel>();
|
||||
}
|
||||
|
||||
// Ensure the active tab is valid and exists
|
||||
if (!string.IsNullOrEmpty(ActiveTab) && _tabPanels.Any())
|
||||
{
|
||||
var activeTabExists = _tabPanels.Any(tp => tp.Name.Equals(ActiveTab, StringComparison.OrdinalIgnoreCase));
|
||||
if (!activeTabExists)
|
||||
{
|
||||
ActiveTab = _tabPanels[0].Name;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the active tab in the UI
|
||||
UpdateActiveTab();
|
||||
}
|
||||
|
||||
private void UpdateActiveTab()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ActiveTab) && _tabPanels != null)
|
||||
{
|
||||
foreach (var tabPanel in _tabPanels)
|
||||
{
|
||||
tabPanel.IsActive = tabPanel.Name.Equals(ActiveTab, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddTabPanel(TabPanel tabPanel)
|
||||
@ -94,20 +76,12 @@
|
||||
if (!_tabPanels.Exists(item => item.Name == tabPanel.Name) && IsAuthorized(tabPanel))
|
||||
{
|
||||
_tabPanels.Add(tabPanel);
|
||||
if (string.IsNullOrEmpty(ActiveTab))
|
||||
{
|
||||
ActiveTab = tabPanel.Name;
|
||||
}
|
||||
UpdateActiveTab();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetActiveTab(string tabName)
|
||||
{
|
||||
ActiveTab = tabName;
|
||||
UpdateActiveTab();
|
||||
StateHasChanged();
|
||||
if (string.IsNullOrEmpty(ActiveTab))
|
||||
{
|
||||
ActiveTab = tabPanel.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAuthorized(TabPanel tabPanel)
|
||||
|
@ -98,17 +98,17 @@ namespace Oqtane.Modules
|
||||
var inline = 0;
|
||||
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))
|
||||
{
|
||||
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
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,4 +267,4 @@
|
||||
<data name="ExpiryDate.Text" xml:space="preserve">
|
||||
<value>Expiry Date: </value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
@ -297,4 +297,4 @@
|
||||
<data name="ExpiryDate.Text" xml:space="preserve">
|
||||
<value>Expiry Date: </value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
@ -435,4 +435,7 @@
|
||||
<data name="Functionality" xml:space="preserve">
|
||||
<value>Functionality</value>
|
||||
</data>
|
||||
<data name="System" xml:space="preserve">
|
||||
<value>System</value>
|
||||
</data>
|
||||
</root>
|
@ -37,11 +37,8 @@
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css",
|
||||
Integrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==",
|
||||
CrossOrigin = "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 },
|
||||
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css", "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==", "anonymous"),
|
||||
new Stylesheet(ThemePath() + "Theme.css"),
|
||||
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<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>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
@ -59,9 +59,15 @@
|
||||
[Parameter]
|
||||
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 bool _canViewAdminDashboard = false;
|
||||
private bool _showEditMode = false;
|
||||
private bool _showEditMode = false; // internal state (not the same as ShowEditMode parameter)
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
|
@ -15,7 +15,6 @@
|
||||
@inject ILogService LoggingService
|
||||
@inject IStringLocalizer<ControlPanelInteractive> Localizer
|
||||
@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">
|
||||
<span class="oi oi-cog"></span>
|
||||
|
@ -17,11 +17,9 @@ namespace Oqtane.Themes.OqtaneTheme
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
// 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",
|
||||
Integrity = "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==",
|
||||
CrossOrigin = "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 }
|
||||
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css", "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==", "anonymous"),
|
||||
new Stylesheet("~/Theme.css"),
|
||||
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -62,17 +62,17 @@ namespace Oqtane.Themes
|
||||
var inline = 0;
|
||||
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))
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
case "HeadContent":
|
||||
var content = RemoveScripts(SiteState.Properties.HeadContent) + "\n";
|
||||
var content = FormatScripts(SiteState.Properties.HeadContent) + "\n";
|
||||
if (content != _content)
|
||||
{
|
||||
_content = content;
|
||||
@ -51,15 +51,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
while (index >= 0)
|
||||
{
|
||||
headcontent = headcontent.Remove(index, headcontent.IndexOf("</script>") + 9 - index);
|
||||
index = headcontent.IndexOf("<script");
|
||||
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");
|
||||
}
|
||||
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;
|
||||
|
@ -117,12 +117,17 @@ namespace Oqtane.UI
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
_jsRuntime.InvokeVoidAsync(
|
||||
"Oqtane.Interop.includeScript",
|
||||
id, src, integrity, crossorigin, type, content, location);
|
||||
id, src, integrity, crossorigin, type, content, location, dataAttributes);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
RenderFragment DynamicComponent { get; set; }
|
||||
|
||||
private string lastPagePath = "";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
// handle page redirection
|
||||
@ -26,7 +28,7 @@
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// set page title
|
||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||
{
|
||||
@ -90,8 +92,16 @@
|
||||
|
||||
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"))
|
||||
{
|
||||
await InjectScripts(PageState.Page.HeadContent, ResourceLocation.Head);
|
||||
@ -100,6 +110,7 @@
|
||||
{
|
||||
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
|
||||
}
|
||||
lastPagePath = PageState.Page.Path;
|
||||
}
|
||||
|
||||
// style sheets
|
||||
@ -176,18 +187,20 @@
|
||||
if (!string.IsNullOrEmpty(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
|
||||
{
|
||||
// inline script must have an id attribute
|
||||
if (id == "")
|
||||
if (dataAttributes == null || !dataAttributes.ContainsKey("data-reload") || dataAttributes["data-reload"] != "false")
|
||||
{
|
||||
count += 1;
|
||||
id = $"page{PageState.Page.PageId}-script{count}";
|
||||
if (id == "")
|
||||
{
|
||||
count += 1;
|
||||
id = $"page{PageState.Page.PageId}-script{count}";
|
||||
}
|
||||
var pos = script.IndexOf(">") + 1;
|
||||
await interop.IncludeScript(id, "", "", "", type, script.Substring(pos, script.IndexOf("</script>") - pos), location.ToString().ToLower(), dataAttributes);
|
||||
}
|
||||
index = script.IndexOf(">") + 1;
|
||||
await interop.IncludeScript(id, "", "", "", "", script.Substring(index, script.IndexOf("</script>") - index), location.ToString().ToLower());
|
||||
}
|
||||
index = content.IndexOf("<script", index + 1);
|
||||
}
|
||||
|
@ -29,12 +29,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001;NU1608</NoWarn>
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
@ -36,7 +36,6 @@ if "%%~nxi" == "%%j" set /A found=1
|
||||
)
|
||||
if not !found! == 1 rmdir /Q/S "%%i"
|
||||
)
|
||||
del "..\Oqtane.Server\bin\Release\net9.0\publish\Oqtane.Server.staticwebassets.endpoints.json"
|
||||
del "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.json"
|
||||
ren "..\Oqtane.Server\bin\Release\net9.0\publish\appsettings.release.json" "appsettings.json"
|
||||
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
|
||||
|
@ -76,6 +76,10 @@
|
||||
|
||||
@((MarkupString)_scripts)
|
||||
@((MarkupString)_bodyResources)
|
||||
@if (_renderMode == RenderModes.Static)
|
||||
{
|
||||
<page-script src="./js/reload.js"></page-script>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -188,9 +192,6 @@
|
||||
_scripts += CreateScrollPositionScript();
|
||||
}
|
||||
|
||||
_headResources += ParseScripts(site.HeadContent);
|
||||
_bodyResources += ParseScripts(site.BodyContent);
|
||||
|
||||
// set culture if not specified
|
||||
string cultureCookie = Context.Request.Cookies[Shared.CookieRequestCultureProvider.DefaultCookieName];
|
||||
if (cultureCookie == null)
|
||||
@ -510,26 +511,10 @@
|
||||
"</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)
|
||||
{
|
||||
var script = CreateScript(resource, alias);
|
||||
if (resource.Location == Shared.ResourceLocation.Head)
|
||||
if (resource.Location == Shared.ResourceLocation.Head && !resource.Reload)
|
||||
{
|
||||
if (!_headResources.Contains(script))
|
||||
{
|
||||
@ -547,36 +532,29 @@
|
||||
|
||||
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 dataAttributes = "";
|
||||
if (resource.DataAttributes != null && resource.DataAttributes.Count > 0)
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||
return "<script" +
|
||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||
((resource.ES6Module) ? " type=\"module\"" : "") +
|
||||
" src=\"" + url + "\"></script>"; // src at end of element due to enhanced navigation patch algorithm
|
||||
}
|
||||
else
|
||||
{
|
||||
// use custom element which can execute script on every page transition
|
||||
@if (string.IsNullOrEmpty(resource.Integrity) && string.IsNullOrEmpty(resource.CrossOrigin))
|
||||
foreach (var attribute in resource.DataAttributes)
|
||||
{
|
||||
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>";
|
||||
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.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||
((!string.IsNullOrEmpty(dataAttributes)) ? dataAttributes : "") +
|
||||
"></script>";
|
||||
}
|
||||
else
|
||||
{
|
||||
// inline script
|
||||
return "<script>" + resource.Content + "</script>";
|
||||
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 (!User.IsInRole(RoleNames.Admin))
|
||||
if (!User.IsInRole(RoleNames.Admin) && notification.FromUserId != null)
|
||||
{
|
||||
// content must be HTML encoded for non-admins to prevent HTML injection
|
||||
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
||||
@ -223,7 +223,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
private bool IsAuthorized(int? userid)
|
||||
{
|
||||
bool authorized = true;
|
||||
bool authorized = false;
|
||||
if (userid != null)
|
||||
{
|
||||
authorized = (_userPermissions.GetUser(User).UserId == userid);
|
||||
|
@ -9,7 +9,7 @@ using System.Net;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
|
@ -269,11 +269,7 @@ namespace Oqtane.Controllers
|
||||
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, permissionName);
|
||||
break;
|
||||
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;
|
||||
case EntityNames.Visitor:
|
||||
authorized = User.IsInRole(RoleNames.Admin);
|
||||
@ -319,7 +315,7 @@ namespace Oqtane.Controllers
|
||||
filter = !_userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, PermissionNames.Edit);
|
||||
break;
|
||||
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;
|
||||
case EntityNames.Visitor:
|
||||
if (!User.IsInRole(RoleNames.Admin))
|
||||
|
@ -28,9 +28,10 @@ namespace Oqtane.Controllers
|
||||
private readonly IJwtManager _jwtManager;
|
||||
private readonly IFileRepository _files;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly ISyncManager _syncManager;
|
||||
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;
|
||||
_tenantManager = tenantManager;
|
||||
@ -40,6 +41,7 @@ namespace Oqtane.Controllers
|
||||
_jwtManager = jwtManager;
|
||||
_files = files;
|
||||
_settings = settings;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -145,20 +147,7 @@ namespace Oqtane.Controllers
|
||||
filtered.DeletedBy = user.DeletedBy;
|
||||
filtered.DeletedOn = user.DeletedOn;
|
||||
filtered.IsDeleted = user.IsDeleted;
|
||||
}
|
||||
|
||||
// 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);
|
||||
filtered.Settings = user.Settings; // include all settings
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,6 +255,7 @@ namespace Oqtane.Controllers
|
||||
if (_userPermissions.GetUser(User).UserId == user.UserId)
|
||||
{
|
||||
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 : "");
|
||||
}
|
||||
}
|
||||
@ -279,6 +269,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
await _userManager.LogoutUserEverywhere(user);
|
||||
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 : "");
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using Oqtane.Security;
|
||||
using System;
|
||||
using Oqtane.Modules.Admin.Roles;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -40,24 +39,34 @@ namespace Oqtane.Controllers
|
||||
public IEnumerable<UserRole> Get(string siteid, string userid = null, string rolename = null)
|
||||
{
|
||||
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))
|
||||
{
|
||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||
if (userid != null)
|
||||
if (IsAuthorized(UserId, rolename))
|
||||
{
|
||||
int UserId = int.TryParse(userid, out UserId) ? UserId : -1;
|
||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||
var userroles = _userRoles.GetUserRoles(SiteId).ToList();
|
||||
if (UserId != -1)
|
||||
{
|
||||
userroles = userroles.Where(item => item.UserId == UserId).ToList();
|
||||
}
|
||||
if (rolename != null)
|
||||
{
|
||||
userroles = userroles.Where(item => item.Role.Name == rolename).ToList();
|
||||
}
|
||||
var user = _userPermissions.GetUser();
|
||||
for (int i = 0; i < userroles.Count(); i++)
|
||||
{
|
||||
userroles[i] = Filter(userroles[i], user.UserId);
|
||||
}
|
||||
return userroles.OrderBy(u => u.User.DisplayName);
|
||||
|
||||
}
|
||||
if (rolename != null)
|
||||
else
|
||||
{
|
||||
userroles = userroles.Where(item => item.Role.Name == rolename).ToList();
|
||||
_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;
|
||||
}
|
||||
var user = _userPermissions.GetUser();
|
||||
for (int i = 0; i < userroles.Count(); i++)
|
||||
{
|
||||
userroles[i] = Filter(userroles[i], user.UserId);
|
||||
}
|
||||
return userroles.OrderBy(u => u.User.DisplayName);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -73,7 +82,7 @@ namespace Oqtane.Controllers
|
||||
public UserRole Get(int 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);
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
// clone object to avoid mutating cache
|
||||
UserRole filtered = null;
|
||||
|
||||
if (userrole != null)
|
||||
{
|
||||
userrole.User.Password = "";
|
||||
userrole.User.IsAuthenticated = false;
|
||||
userrole.User.TwoFactorCode = "";
|
||||
userrole.User.TwoFactorExpiry = null;
|
||||
filtered = new UserRole();
|
||||
|
||||
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 = "";
|
||||
userrole.User.PhotoFileId = null;
|
||||
userrole.User.LastLoginOn = DateTime.MinValue;
|
||||
userrole.User.LastIPAddress = "";
|
||||
userrole.User.Roles = "";
|
||||
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;
|
||||
filtered.User.Email = userrole.User.Email;
|
||||
filtered.User.PhotoFileId = userrole.User.PhotoFileId;
|
||||
filtered.User.LastLoginOn = userrole.User.LastLoginOn;
|
||||
filtered.User.LastIPAddress = userrole.User.LastIPAddress;
|
||||
filtered.User.CreatedOn = userrole.User.CreatedOn;
|
||||
}
|
||||
}
|
||||
return userrole;
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace Oqtane.Infrastructure
|
||||
if (userid != null && username != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user);
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -17,12 +17,14 @@ namespace Oqtane.Infrastructure
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
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;
|
||||
_environment = environment;
|
||||
_configManager = configManager;
|
||||
_filelogger = filelogger;
|
||||
}
|
||||
|
||||
public void Upgrade(Tenant tenant, string version)
|
||||
@ -91,7 +93,7 @@ namespace Oqtane.Infrastructure
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 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}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,7 +111,7 @@ namespace Oqtane.Infrastructure
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 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}"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +279,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
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}"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,7 +315,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
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}"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +359,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
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}"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +386,7 @@ namespace Oqtane.Infrastructure
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 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}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -487,7 +489,7 @@ namespace Oqtane.Infrastructure
|
||||
catch (Exception ex)
|
||||
{
|
||||
// error deleting asesmbly
|
||||
Debug.WriteLine($"Oqtane Error: 6.0.1 Upgrade Error Removing {assembly} - {ex}");
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"Oqtane Error: 6.0.1 Upgrade Error Removing {assembly} - {ex}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -374,6 +374,8 @@ namespace Oqtane.Managers
|
||||
_users.UpdateUser(user);
|
||||
_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)
|
||||
{
|
||||
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
||||
|
@ -19,6 +19,7 @@
|
||||
<DefineConstants>$(DefineConstants);OQTANE;OQTANE3</DefineConstants>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
||||
<CompressionEnabled>false</CompressionEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="wwwroot\Modules\Templates\**" />
|
||||
|
@ -35,6 +35,7 @@ namespace Oqtane.Pages
|
||||
{
|
||||
await _userManager.LogoutUserEverywhere(user);
|
||||
}
|
||||
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, "Logout");
|
||||
_syncManager.AddSyncEvent(alias, EntityNames.User, user.UserId, SyncEventActions.Reload);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -31,7 +32,47 @@ namespace Oqtane.Repository
|
||||
{
|
||||
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)
|
||||
|
@ -127,7 +127,6 @@ namespace Oqtane.Services
|
||||
.ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + setting.SettingValue);
|
||||
site.Pages.Add(page);
|
||||
}
|
||||
site.Pages = GetPagesHierarchy(site.Pages);
|
||||
|
||||
// framework modules
|
||||
var modules = GetPageModules(site.SiteId);
|
||||
|
@ -20,10 +20,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<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="bash $(ProjectDir)debug.sh" />
|
||||
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd" />
|
||||
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh" />
|
||||
<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 $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||
<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 $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
</Project>
|
@ -1,32 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>[Owner].Module.[Module]</id>
|
||||
<version>1.0.0</version>
|
||||
<authors>[Owner]</authors>
|
||||
<owners>[Owner]</owners>
|
||||
<title>[Module]</title>
|
||||
<description>[Description]</description>
|
||||
<copyright>[Owner]</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane module</tags>
|
||||
<releaseNotes></releaseNotes>
|
||||
<summary></summary>
|
||||
<dependencies>
|
||||
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\net9.0\[Owner].Module.[Module].Client.Oqtane.dll" target="lib\net9.0" />
|
||||
<file src="..\Client\bin\Release\net9.0\[Owner].Module.[Module].Client.Oqtane.pdb" target="lib\net9.0" />
|
||||
<file src="..\Server\bin\Release\net9.0\[Owner].Module.[Module].Server.Oqtane.dll" target="lib\net9.0" />
|
||||
<file src="..\Server\bin\Release\net9.0\[Owner].Module.[Module].Server.Oqtane.pdb" target="lib\net9.0" />
|
||||
<file src="..\Shared\bin\Release\net9.0\[Owner].Module.[Module].Shared.Oqtane.dll" target="lib\net9.0" />
|
||||
<file src="..\Shared\bin\Release\net9.0\[Owner].Module.[Module].Shared.Oqtane.pdb" target="lib\net9.0" />
|
||||
<file src="..\Server\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
||||
<metadata>
|
||||
<id>$projectname$</id>
|
||||
<version>1.0.0</version>
|
||||
<authors>[Owner]</authors>
|
||||
<owners>[Owner]</owners>
|
||||
<title>[Module]</title>
|
||||
<description>[Description]</description>
|
||||
<copyright>[Owner]</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane module</tags>
|
||||
<releaseNotes></releaseNotes>
|
||||
<summary></summary>
|
||||
<dependencies>
|
||||
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.dll" target="lib\$targetframework$" />
|
||||
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.dll" target="lib\$targetframework$" />
|
||||
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.dll" target="lib\$targetframework$" />
|
||||
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Server\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1,7 +1,11 @@
|
||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Module.[Module].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Module.[Module].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
||||
XCOPY "..\Server\bin\Debug\net9.0\[Owner].Module.[Module].Server.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
||||
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 "..\Shared\bin\Debug\net9.0\[Owner].Module.[Module].Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
||||
XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
||||
@echo off
|
||||
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 "..\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
|
@ -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/"
|
||||
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/"
|
||||
cp -f "../Server/bin/Debug/net9.0/[Owner].Module.[Module].Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net9.0/"
|
||||
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 -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/"
|
||||
#!/bin/bash
|
||||
|
||||
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 -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"
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].Module.[Module].nuspec
|
||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\" /Y
|
||||
@echo off
|
||||
set TargetFramework=%1
|
||||
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
|
||||
cp -f "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\"
|
||||
TargetFramework=$1
|
||||
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>
|
||||
|
||||
<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="bash $(ProjectDir)debug.sh" />
|
||||
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd" />
|
||||
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh" />
|
||||
<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 $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||
<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 $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
@ -1,28 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>[Owner].Theme.[Theme]</id>
|
||||
<version>1.0.0</version>
|
||||
<authors>[Owner]</authors>
|
||||
<owners>[Owner]</owners>
|
||||
<title>[Theme]</title>
|
||||
<description>[Description]</description>
|
||||
<copyright>[Owner]</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane theme</tags>
|
||||
<releaseNotes></releaseNotes>
|
||||
<summary></summary>
|
||||
<dependencies>
|
||||
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\net9.0\[Owner].Theme.[Theme].Client.Oqtane.dll" target="lib\net9.0" />
|
||||
<file src="..\Client\bin\Release\net9.0\[Owner].Theme.[Theme].Client.Oqtane.pdb" target="lib\net9.0" />
|
||||
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
||||
<metadata>
|
||||
<id>$projectname$</id>
|
||||
<version>1.0.0</version>
|
||||
<authors>[Owner]</authors>
|
||||
<owners>[Owner]</owners>
|
||||
<title>[Theme]</title>
|
||||
<description>[Description]</description>
|
||||
<copyright>[Owner]</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane theme</tags>
|
||||
<releaseNotes></releaseNotes>
|
||||
<summary></summary>
|
||||
<dependencies>
|
||||
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\$targetframework$\$projectname$.Client.Oqtane.dll" target="lib\$targetframework$" />
|
||||
<file src="..\Client\bin\Release\$targetframework$\$projectname$.Client.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1,3 +1,7 @@
|
||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Theme.[Theme].Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
||||
XCOPY "..\Client\bin\Debug\net9.0\[Owner].Theme.[Theme].Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\net9.0\" /Y
|
||||
XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
||||
@echo off
|
||||
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
|
@ -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/"
|
||||
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/"
|
||||
#!/bin/bash
|
||||
|
||||
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"
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack [Owner].Theme.[Theme].nuspec
|
||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Packages\" /Y
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\wwwroot\Packages\" /Y
|
@ -1,2 +1,5 @@
|
||||
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack [Owner].Theme.[Theme].nuspec
|
||||
cp -f "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\"
|
||||
TargetFramework=$1
|
||||
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);
|
||||
}
|
||||
},
|
||||
includeScript: function (id, src, integrity, crossorigin, type, content, location) {
|
||||
includeScript: function (id, src, integrity, crossorigin, type, content, location, dataAttributes) {
|
||||
var script;
|
||||
if (src !== "") {
|
||||
script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]");
|
||||
}
|
||||
else {
|
||||
script = document.getElementById(id);
|
||||
if (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) {
|
||||
script.remove();
|
||||
@ -152,37 +161,36 @@ Oqtane.Interop = {
|
||||
else {
|
||||
script.innerHTML = content;
|
||||
}
|
||||
script.async = false;
|
||||
this.addScript(script, location)
|
||||
.then(() => {
|
||||
if (src !== "") {
|
||||
console.log(src + ' loaded');
|
||||
}
|
||||
else {
|
||||
console.log(id + ' loaded');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (src !== "") {
|
||||
console.error(src + ' failed');
|
||||
}
|
||||
else {
|
||||
console.error(id + ' failed');
|
||||
}
|
||||
});
|
||||
if (dataAttributes !== null) {
|
||||
for (var key in dataAttributes) {
|
||||
script.setAttribute(key, dataAttributes[key]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.addScript(script, location);
|
||||
} catch (error) {
|
||||
if (src !== "") {
|
||||
console.error("Failed to load external script: ${src}", error);
|
||||
} else {
|
||||
console.error("Failed to load inline script: ${content}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addScript: function (script, location) {
|
||||
if (location === 'head') {
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
if (location === 'body') {
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
script.async = false;
|
||||
script.defer = false;
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
script.onload = res();
|
||||
script.onerror = rej();
|
||||
script.onload = () => resolve();
|
||||
script.onerror = (error) => reject(error);
|
||||
|
||||
if (location === 'head') {
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
});
|
||||
},
|
||||
includeScripts: async function (scripts) {
|
||||
@ -222,10 +230,10 @@ Oqtane.Interop = {
|
||||
if (scripts[s].crossorigin !== '') {
|
||||
element.crossOrigin = scripts[s].crossorigin;
|
||||
}
|
||||
if (scripts[s].es6module === true) {
|
||||
element.type = "module";
|
||||
if (scripts[s].type !== '') {
|
||||
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) {
|
||||
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 |
@ -144,25 +144,25 @@ namespace Oqtane.Models
|
||||
{
|
||||
FromUserId = from.UserId;
|
||||
FromDisplayName = from.DisplayName;
|
||||
FromEmail = from.Email;
|
||||
FromEmail = from.Email ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
FromUserId = null;
|
||||
FromDisplayName = fromDisplayName;
|
||||
FromEmail = fromEmail;
|
||||
FromEmail = fromEmail ?? "";
|
||||
}
|
||||
if (to != null)
|
||||
{
|
||||
ToUserId = to.UserId;
|
||||
ToDisplayName = to.DisplayName;
|
||||
ToEmail = to.Email;
|
||||
ToEmail = to.Email ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
ToUserId = null;
|
||||
ToDisplayName = toDisplayName;
|
||||
ToEmail = toEmail;
|
||||
ToEmail = toEmail ?? "";
|
||||
}
|
||||
Subject = subject;
|
||||
Body = body;
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Shared;
|
||||
|
||||
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>
|
||||
/// Integrity checks to increase the security of resources accessed. Especially common in CDN resources.
|
||||
/// </summary>
|
||||
@ -52,11 +59,6 @@ namespace Oqtane.Models
|
||||
/// </summary>
|
||||
public ResourceLocation Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For Scripts this allows type="module" registrations - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
public bool ES6Module { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows specification of inline script - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
@ -72,6 +74,11 @@ namespace Oqtane.Models
|
||||
/// </summary>
|
||||
public bool Reload { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cusotm data-* attributes for scripts - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
public Dictionary<string, string> DataAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The namespace of the component that declared the resource - only used in SiteRouter
|
||||
/// </summary>
|
||||
@ -82,14 +89,22 @@ namespace Oqtane.Models
|
||||
var resource = new Resource();
|
||||
resource.ResourceType = ResourceType;
|
||||
resource.Url = Url;
|
||||
resource.Type = Type;
|
||||
resource.Integrity = Integrity;
|
||||
resource.CrossOrigin = CrossOrigin;
|
||||
resource.Bundle = Bundle;
|
||||
resource.Location = Location;
|
||||
resource.ES6Module = ES6Module;
|
||||
resource.Content = Content;
|
||||
resource.RenderMode = RenderMode;
|
||||
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.Namespace = name;
|
||||
return resource;
|
||||
@ -97,5 +112,18 @@ namespace Oqtane.Models
|
||||
|
||||
[Obsolete("ResourceDeclaration is deprecated", false)]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user