-
Language:
+
Language:
@@ -350,7 +350,7 @@ else
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
- NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
+ NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
}
}
diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor
index 91e4d0e7..e2623a02 100644
--- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor
+++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor
@@ -10,100 +10,32 @@
@inject IStringLocalizer
SharedLocalizer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Date/Time:
+
Date/Time:
-
Category:
+
Category:
@@ -111,7 +43,7 @@
@if (_pageName != string.Empty)
{
-
Page:
+
Page:
@@ -120,7 +52,7 @@
@if (_moduleTitle != string.Empty)
{
-
Module:
+
Module:
@@ -129,26 +61,26 @@
@if (_username != string.Empty)
{
}
-
Message:
+
Message:
@@ -156,24 +88,25 @@
@if (!string.IsNullOrEmpty(_exception))
{
-
Exception:
-
}
-
Properties:
+
Properties:
@SharedLocalizer["Cancel"]
diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor
index 920ec709..311e73b0 100644
--- a/Oqtane.Client/Modules/Admin/Logs/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor
@@ -51,11 +51,11 @@ else
{
-
- @Localizer["Date"]
- @Localizer["Level"]
- @Localizer["Feature"]
- @Localizer["Function"]
+
+ @Localizer["Date"]
+ @Localizer["Level"]
+ @Localizer["Feature"]
+ @Localizer["Function"]
diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs b/Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs
index 81b82a6d..7eb2f372 100644
--- a/Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs
+++ b/Oqtane.Client/Modules/Admin/ModuleCreator/ModuleInfo.cs
@@ -1,7 +1,9 @@
-using Oqtane.Models;
+using Oqtane.Documentation;
+using Oqtane.Models;
namespace Oqtane.Modules.Admin.ModuleCreator
{
+ [PrivateApi("Mark this as private, since it's not very useful in the public docs")]
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor
index 1f194dba..57d6fa26 100644
--- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor
+++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor
@@ -22,6 +22,7 @@ else
@SharedLocalizer["Name"]
@SharedLocalizer["Version"]
+ @Localizer["InUse"]
@SharedLocalizer["Expires"]
@@ -35,6 +36,16 @@ else
@context.Name
@context.Version
+
+ @if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition.ModuleDefinitionId == context.ModuleDefinitionId).Count() > 0)
+ {
+ @SharedLocalizer["Yes"]
+ }
+ else
+ {
+ @SharedLocalizer["No"]
+ }
+
@((MarkupString)PurchaseLink(context.PackageName))
diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor
index d3cbfbac..5f2a8536 100644
--- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor
+++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor
@@ -3,13 +3,14 @@
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IPageService PageService
+@inject IPageModuleService PageModuleService
@inject IThemeService ThemeService
@inject IStringLocalizer Localizer
@inject IStringLocalizer SharedLocalizer
-
-
Allow User Registration?
-
-
- @SharedLocalizer["Yes"]
- @SharedLocalizer["No"]
-
-
-
Is Deleted?
@@ -240,243 +231,246 @@
}
@code {
- private ElementReference form;
- private bool validated = false;
- private bool _initialized = false;
- private List
_themeList;
- private List _themes = new List();
- private List _containers = new List();
- private string _name = string.Empty;
- private List _aliasList;
- private string _urls = string.Empty;
- private string _runtime = "";
- private string _prerender = "";
- private int _logofileid = -1;
- private FileManager _logofilemanager;
- private int _faviconfileid = -1;
- private FileManager _faviconfilemanager;
- private string _themetype = "-";
- private string _containertype = "-";
- private string _admincontainertype = "-";
- private string _allowregistration;
- private string _smtphost = string.Empty;
- private string _smtpport = string.Empty;
- private string _smtpssl = "False";
- private string _smtpusername = string.Empty;
- private string _smtppassword = string.Empty;
- private string _smtpsender = string.Empty;
- private string _pwaisenabled;
- private int _pwaappiconfileid = -1;
- private FileManager _pwaappiconfilemanager;
- private int _pwasplashiconfileid = -1;
- private FileManager _pwasplashiconfilemanager;
- private string _tenant = string.Empty;
- private string _database = string.Empty;
- private string _connectionstring = string.Empty;
- private string _createdby;
- private DateTime _createdon;
- private string _modifiedby;
- private DateTime _modifiedon;
- private string _deletedby;
- private DateTime? _deletedon;
- private string _isdeleted;
+ private ElementReference form;
+ private bool validated = false;
+ private bool _initialized = false;
+ private List _themeList;
+ private List _themes = new List();
+ private List _containers = new List();
+ private string _name = string.Empty;
+ private List _aliasList;
+ private string _urls = string.Empty;
+ private string _runtime = "";
+ private string _prerender = "";
+ private int _logofileid = -1;
+ private FileManager _logofilemanager;
+ private int _faviconfileid = -1;
+ private FileManager _faviconfilemanager;
+ private string _themetype = "-";
+ private string _containertype = "-";
+ private string _admincontainertype = "-";
+ private string _smtphost = string.Empty;
+ private string _smtpport = string.Empty;
+ private string _smtpssl = "False";
+ private string _smtpusername = string.Empty;
+ private string _smtppassword = string.Empty;
+ private string _smtpsender = string.Empty;
+ private string _pwaisenabled;
+ private int _pwaappiconfileid = -1;
+ private FileManager _pwaappiconfilemanager;
+ private int _pwasplashiconfileid = -1;
+ private FileManager _pwasplashiconfilemanager;
+ private string _tenant = string.Empty;
+ private string _database = string.Empty;
+ private string _connectionstring = string.Empty;
+ private string _createdby;
+ private DateTime _createdon;
+ private string _modifiedby;
+ private DateTime _modifiedon;
+ private string _deletedby;
+ private DateTime? _deletedon;
+ private string _isdeleted;
- public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
- protected override async Task OnInitializedAsync()
- {
- try
- {
- _themeList = await ThemeService.GetThemesAsync();
- Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
- if (site != null)
- {
- _name = site.Name;
- _runtime = site.Runtime;
- _prerender = site.RenderMode.Replace(_runtime, "");
- _allowregistration = site.AllowRegistration.ToString();
- _isdeleted = site.IsDeleted.ToString();
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ _themeList = await ThemeService.GetThemesAsync();
+ Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
+ if (site != null)
+ {
+ _name = site.Name;
+ _runtime = site.Runtime;
+ _prerender = site.RenderMode.Replace(_runtime, "");
+ _isdeleted = site.IsDeleted.ToString();
- if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
- {
- _aliasList = await AliasService.GetAliasesAsync();
- foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
- {
- _urls += alias.Name + ",";
- }
- _urls = _urls.Substring(0, _urls.Length - 1);
+ if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
+ {
+ _aliasList = await AliasService.GetAliasesAsync();
+ foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
+ {
+ _urls += alias.Name + ",";
+ }
+ _urls = _urls.Substring(0, _urls.Length - 1);
- }
+ }
- if (site.LogoFileId != null)
- {
- _logofileid = site.LogoFileId.Value;
- }
+ if (site.LogoFileId != null)
+ {
+ _logofileid = site.LogoFileId.Value;
+ }
- if (site.FaviconFileId != null)
- {
- _faviconfileid = site.FaviconFileId.Value;
- }
+ if (site.FaviconFileId != null)
+ {
+ _faviconfileid = site.FaviconFileId.Value;
+ }
- _themes = ThemeService.GetThemeControls(_themeList);
- _themetype = (!string.IsNullOrEmpty(site.DefaultThemeType)) ? site.DefaultThemeType : Constants.DefaultTheme;
- _containers = ThemeService.GetContainerControls(_themeList, _themetype);
- _containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
- _admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
+ _themes = ThemeService.GetThemeControls(_themeList);
+ _themetype = (!string.IsNullOrEmpty(site.DefaultThemeType)) ? site.DefaultThemeType : Constants.DefaultTheme;
+ _containers = ThemeService.GetContainerControls(_themeList, _themetype);
+ _containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
+ _admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
- var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
- _smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
- _smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
- _smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
- _smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
- _smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
- _smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
+ _pwaisenabled = site.PwaIsEnabled.ToString();
+ if (site.PwaAppIconFileId != null)
+ {
+ _pwaappiconfileid = site.PwaAppIconFileId.Value;
+ }
+ if (site.PwaSplashIconFileId != null)
+ {
+ _pwasplashiconfileid = site.PwaSplashIconFileId.Value;
+ }
+
+ var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
+ _smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
+ _smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
+ _smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
+ _smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
+ _smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
+ _smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
- _pwaisenabled = site.PwaIsEnabled.ToString();
+ if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
+ {
+ var tenants = await TenantService.GetTenantsAsync();
+ var _databases = await DatabaseService.GetDatabasesAsync();
+ var tenant = tenants.Find(item => item.TenantId == site.TenantId);
+ if (tenant != null)
+ {
+ _tenant = tenant.Name;
+ _database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
+ _connectionstring = tenant.DBConnectionString;
+ }
+ }
- if (site.PwaAppIconFileId != null)
- {
- _pwaappiconfileid = site.PwaAppIconFileId.Value;
- }
+ _createdby = site.CreatedBy;
+ _createdon = site.CreatedOn;
+ _modifiedby = site.ModifiedBy;
+ _modifiedon = site.ModifiedOn;
+ _deletedby = site.DeletedBy;
+ _deletedon = site.DeletedOn;
- if (site.PwaSplashIconFileId != null)
- {
- _pwasplashiconfileid = site.PwaSplashIconFileId.Value;
- }
+ _initialized = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
+ AddModuleMessage(ex.Message, MessageType.Error);
+ }
+ }
- _pwaisenabled = site.PwaIsEnabled.ToString();
- if (site.PwaAppIconFileId != null)
- {
- _pwaappiconfileid = site.PwaAppIconFileId.Value;
- }
- if (site.PwaSplashIconFileId != null)
- {
- _pwasplashiconfileid = site.PwaSplashIconFileId.Value;
- }
+ private async void ThemeChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ _themetype = (string)e.Value;
+ if (_themetype != "-")
+ {
+ _containers = ThemeService.GetContainerControls(_themeList, _themetype);
+ }
+ else
+ {
+ _containers = new List();
+ }
+ _containertype = "-";
+ _admincontainertype = Constants.DefaultAdminContainer;
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
+ AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
+ }
+ }
- if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
- {
- var tenants = await TenantService.GetTenantsAsync();
- var _databases = await DatabaseService.GetDatabasesAsync();
- var tenant = tenants.Find(item => item.TenantId == site.TenantId);
- if (tenant != null)
- {
- _tenant = tenant.Name;
- _database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
- _connectionstring = tenant.DBConnectionString;
- }
- }
+ private async Task SaveSite()
+ {
+ validated = true;
+ var interop = new Interop(JSRuntime);
+ if (await interop.FormValid(form))
+ {
+ try
+ {
+ if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
+ {
+ var unique = true;
+ if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
+ {
+ foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
+ {
+ unique = false;
+ }
+ }
+ }
- _createdby = site.CreatedBy;
- _createdon = site.CreatedOn;
- _modifiedby = site.ModifiedBy;
- _modifiedon = site.ModifiedOn;
- _deletedby = site.DeletedBy;
- _deletedon = site.DeletedOn;
+ if (unique)
+ {
+ var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
+ if (site != null)
+ {
+ bool refresh = false;
+ bool reload = false;
- _initialized = true;
- }
- }
- catch (Exception ex)
- {
- await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
- AddModuleMessage(ex.Message, MessageType.Error);
- }
- }
+ site.Name = _name;
+ if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
+ {
+ if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
+ {
+ site.Runtime = _runtime;
+ site.RenderMode = _runtime + _prerender;
+ refresh = true;
+ reload = true; // needs to be reloaded on server
+ }
+ }
+ site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
- private async void ThemeChanged(ChangeEventArgs e)
- {
- try
- {
- _themetype = (string)e.Value;
- if (_themetype != "-")
- {
- _containers = ThemeService.GetContainerControls(_themeList, _themetype);
- }
- else
- {
- _containers = new List();
- }
- _containertype = "-";
- _admincontainertype = Constants.DefaultAdminContainer;
- StateHasChanged();
- }
- catch (Exception ex)
- {
- await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
- AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
- }
- }
+ site.LogoFileId = null;
+ var logofileid = _logofilemanager.GetFileId();
+ if (logofileid != -1)
+ {
+ site.LogoFileId = logofileid;
+ }
+ int? faviconFieldId = _faviconfilemanager.GetFileId();
+ if (faviconFieldId == -1) faviconFieldId = null;
+ if (site.FaviconFileId != faviconFieldId)
+ {
+ site.FaviconFileId = faviconFieldId;
+ reload = true; // needs to be reloaded on server
+ }
+ if (site.DefaultThemeType != _themetype)
+ {
+ site.DefaultThemeType = _themetype;
+ refresh = true; // needs to be refreshed on client
+ }
+ if (site.DefaultContainerType != _containertype)
+ {
+ site.DefaultContainerType = _containertype;
+ refresh = true; // needs to be refreshed on client
+ }
+ site.AdminContainerType = _admincontainertype;
- private async Task SaveSite()
- {
- validated = true;
- var interop = new Interop(JSRuntime);
- if (await interop.FormValid(form))
- {
- try
- {
- if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
- {
- var unique = true;
- if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
- {
- foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
- {
- if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
- {
- unique = false;
- }
- }
- }
-
- if (unique)
- {
- var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
- if (site != null)
- {
- bool reload = false;
- bool refresh = (site.DefaultThemeType != _themetype || site.DefaultContainerType != _containertype);
-
- site.Name = _name;
- if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
- {
- if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
- {
- site.Runtime = _runtime;
- site.RenderMode = _runtime + _prerender;
- refresh = true;
- reload = true;
- }
- }
- site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
- site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
-
- site.LogoFileId = null;
- var logofileid = _logofilemanager.GetFileId();
- if (logofileid != -1)
- {
- site.LogoFileId = logofileid;
- }
- var faviconFieldId = _faviconfilemanager.GetFileId();
- if (faviconFieldId != -1)
- {
- site.FaviconFileId = faviconFieldId;
- }
- site.DefaultThemeType = _themetype;
- site.DefaultContainerType = _containertype;
- site.AdminContainerType = _admincontainertype;
-
- site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled));
- var pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
- if (pwaappiconfileid != -1)
+ if (site.PwaIsEnabled.ToString() != _pwaisenabled)
+ {
+ site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
+ reload = true; // needs to be reloaded on server
+ }
+ int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
+ if (pwaappiconfileid == -1) pwaappiconfileid = null;
+ if (site.PwaAppIconFileId != pwaappiconfileid)
{
site.PwaAppIconFileId = pwaappiconfileid;
+ reload = true; // needs to be reloaded on server
}
- var pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
- if (pwasplashiconfileid != -1)
+ int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
+ if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
+ if (site.PwaSplashIconFileId != pwasplashiconfileid)
{
site.PwaSplashIconFileId = pwasplashiconfileid;
+ reload = true; // needs to be reloaded on server
}
site = await SiteService.UpdateSiteAsync(site);
@@ -518,7 +512,7 @@
if (refresh)
{
- NavigationManager.NavigateTo(NavigateUrl(), reload); // refresh to show new theme or container
+ NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
}
else
{
diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor
index f6d829a4..09d7946d 100644
--- a/Oqtane.Client/Modules/Admin/Sites/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor
@@ -52,6 +52,11 @@ else
private void Edit(string name)
{
+ if (name.Equals("*"))
+ {
+ var uri = new Uri(NavigationManager.Uri);
+ name = uri.Authority;
+ }
NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload");
}
diff --git a/Oqtane.Client/Modules/Admin/UrlMappings/Add.razor b/Oqtane.Client/Modules/Admin/UrlMappings/Add.razor
new file mode 100644
index 00000000..efde8b66
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/UrlMappings/Add.razor
@@ -0,0 +1,71 @@
+@namespace Oqtane.Modules.Admin.UrlMappings
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IUrlMappingService UrlMappingService
+@inject IStringLocalizer Localizer
+@inject IStringLocalizer SharedLocalizer
+
+
+
+@code {
+ private ElementReference form;
+ private bool validated = false;
+
+ private string _url = string.Empty;
+ private string _mappedurl = string.Empty;
+
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
+
+ private async Task SaveUrlMapping()
+ {
+ validated = true;
+ var interop = new Interop(JSRuntime);
+ if (await interop.FormValid(form))
+ {
+ var route = new Route(_url, PageState.Alias.Path);
+ var url = route.SiteUrl + "/" + route.PagePath;
+
+ var urlmapping = new UrlMapping();
+ urlmapping.SiteId = PageState.Site.SiteId;
+ urlmapping.Url = url;
+ urlmapping.MappedUrl = _mappedurl;
+ urlmapping.Requests = 0;
+ urlmapping.CreatedOn = DateTime.UtcNow;
+ urlmapping.RequestedOn = DateTime.UtcNow;
+
+ try
+ {
+ urlmapping = await UrlMappingService.AddUrlMappingAsync(urlmapping);
+ await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
+ AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/UrlMappings/Edit.razor b/Oqtane.Client/Modules/Admin/UrlMappings/Edit.razor
new file mode 100644
index 00000000..1ef1b79a
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/UrlMappings/Edit.razor
@@ -0,0 +1,83 @@
+@namespace Oqtane.Modules.Admin.UrlMappings
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IUrlMappingService UrlMappingService
+@inject IStringLocalizer Localizer
+@inject IStringLocalizer SharedLocalizer
+
+
+
+@code {
+ private ElementReference form;
+ private bool validated = false;
+
+ private int _urlmappingid;
+ private string _url = string.Empty;
+ private string _mappedurl = string.Empty;
+
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ _urlmappingid = Int32.Parse(PageState.QueryString["id"]);
+ var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
+ if (urlmapping != null)
+ {
+ _url = urlmapping.Url;
+ _mappedurl = urlmapping.MappedUrl;
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading UrlMapping {UrlMappingId} {Error}", _urlmappingid, ex.Message);
+ AddModuleMessage(Localizer["Error.LoadUrlMapping"], MessageType.Error);
+ }
+ }
+
+ private async Task SaveUrlMapping()
+ {
+ validated = true;
+ var interop = new Interop(JSRuntime);
+ if (await interop.FormValid(form))
+ {
+ var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
+ urlmapping.MappedUrl = _mappedurl;
+
+ try
+ {
+ urlmapping = await UrlMappingService.UpdateUrlMappingAsync(urlmapping);
+ await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
+ AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor b/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor
new file mode 100644
index 00000000..327f7a37
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/UrlMappings/Index.razor
@@ -0,0 +1,140 @@
+@namespace Oqtane.Modules.Admin.UrlMappings
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IUrlMappingService UrlMappingService
+@inject ISiteService SiteService
+@inject IStringLocalizer Localizer
+@inject IStringLocalizer SharedLocalizer
+
+@if (_urlMappings == null)
+{
+ @SharedLocalizer["Loading"]
+}
+else
+{
+
+
+
+
+
+
+ MappedChanged(e))">
+ @Localizer["Mapped"]
+ @Localizer["Broken"]
+
+
+
+
+
+
+
+
+
+ @Localizer["Url"]
+ @Localizer["Requests"]
+ @Localizer["Requested"]
+
+
+
+
+
+ @context.Url
+ @if (_mapped)
+ {
+ @((MarkupString)" >> ")@context.MappedUrl
+ }
+
+ @context.Requests
+ @context.RequestedOn
+
+
+
+
+
+
+
Capture Broken Urls?
+
+
+ @SharedLocalizer["Yes"]
+ @SharedLocalizer["No"]
+
+
+
+
+
+ @SharedLocalizer["Save"]
+
+
+}
+
+@code {
+ private bool _mapped = true;
+ private List _urlMappings;
+ private string _capturebrokenurls;
+
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
+
+ protected override async Task OnParametersSetAsync()
+ {
+ await GetUrlMappings();
+ _capturebrokenurls = PageState.Site.CaptureBrokenUrls.ToString();
+ }
+
+ private async void MappedChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ _mapped = bool.Parse(e.Value.ToString());
+ await GetUrlMappings();
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error On TypeChanged");
+ }
+ }
+
+ private void BrowseUrl(string url)
+ {
+ NavigationManager.NavigateTo(url, true);
+ }
+
+ private async Task DeleteUrlMapping(UrlMapping urlMapping)
+ {
+ try
+ {
+ await UrlMappingService.DeleteUrlMappingAsync(urlMapping.UrlMappingId);
+ await logger.LogInformation("UrlMapping Deleted {UrlMapping}", urlMapping);
+ await GetUrlMappings();
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Deleting UrlMapping {UrlMapping} {Error}", urlMapping, ex.Message);
+ AddModuleMessage(Localizer["Error.DeleteUrlMapping"], MessageType.Error);
+ }
+ }
+
+ private async Task GetUrlMappings()
+ {
+ _urlMappings = await UrlMappingService.GetUrlMappingsAsync(PageState.Site.SiteId, _mapped);
+ }
+
+ private async Task SaveSiteSettings()
+ {
+ try
+ {
+ var site = PageState.Site;
+ site.CaptureBrokenUrls = bool.Parse(_capturebrokenurls);
+ await SiteService.UpdateSiteAsync(site);
+ AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
+ AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor
index a661c325..3f138c34 100644
--- a/Oqtane.Client/Modules/Admin/Users/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Users/Index.razor
@@ -3,6 +3,7 @@
@inject IUserRoleService UserRoleService
@inject IUserService UserService
@inject ISettingService SettingService
+@inject ISiteService SiteService
@inject IStringLocalizer Localizer
@inject IStringLocalizer SharedLocalizer
@@ -14,45 +15,65 @@
}
else
{
-
-
-
-
-
-
-
- @SharedLocalizer["Search"]
-
-
-
-
-
-
-
-
- @SharedLocalizer["Name"]
-
-
-
-
-
-
-
-
-
-
-
- @context.User.DisplayName
-
-
+
+
+
+
+
+
+
+
+
+ @SharedLocalizer["Search"]
+
+
+
+
+
+
+
+
+ @SharedLocalizer["Name"]
+
+
+
+
+
+
+
+
+
+
+
+ @context.User.DisplayName
+
+
+
+
+
+
+
Allow User Registration?
+
+
+ @SharedLocalizer["Yes"]
+ @SharedLocalizer["No"]
+
+
+
+
+
+ @SharedLocalizer["Save"]
+
+
}
@code {
private List allroles;
private List userroles;
private string _search;
+ private string _allowregistration;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@@ -61,6 +82,7 @@ else
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
await LoadSettingsAsync();
userroles = Search(_search);
+ _allowregistration = PageState.Site.AllowRegistration.ToString();
}
private List Search(string search)
@@ -122,4 +144,20 @@ else
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
}
+ private async Task SaveSiteSettings()
+ {
+ try
+ {
+ var site = PageState.Site;
+ site.AllowRegistration = bool.Parse(_allowregistration);
+ await SiteService.UpdateSiteAsync(site);
+ AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
+ AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
+ }
+ }
+
}
diff --git a/Oqtane.Client/Modules/Admin/Visitors/Detail.razor b/Oqtane.Client/Modules/Admin/Visitors/Detail.razor
new file mode 100644
index 00000000..d1adf75e
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Visitors/Detail.razor
@@ -0,0 +1,123 @@
+@namespace Oqtane.Modules.Admin.Visitors
+@using System.Globalization
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IVisitorService VisitorService
+@inject IUserService UserService
+@inject IStringLocalizer Localizer
+@inject IStringLocalizer SharedLocalizer
+
+
+
+
+
+
+
+ @if (_user != string.Empty)
+ {
+
+ }
+
+
+
+
+
+ @SharedLocalizer["Cancel"]
+
+@code {
+ private int _visitorId;
+ private string _ip = string.Empty;
+ private string _language = string.Empty;
+ private string _useragent = string.Empty;
+ private string _url = string.Empty;
+ private string _referrer = string.Empty;
+ private string _user = string.Empty;
+ private string _visits = string.Empty;
+ private string _visited = string.Empty;
+ private string _created = string.Empty;
+
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ _visitorId = Int32.Parse(PageState.QueryString["id"]);
+ var visitor = await VisitorService.GetVisitorAsync(_visitorId);
+ if (visitor != null)
+ {
+ _ip = visitor.IPAddress;
+ _language = visitor.Language;
+ _useragent = visitor.UserAgent;
+ _url = visitor.Url;
+ _referrer = visitor.Referrer;
+ _visits = visitor.Visits.ToString();
+ _visited = visitor.VisitedOn.ToString(CultureInfo.CurrentCulture);
+ _created = visitor.CreatedOn.ToString(CultureInfo.CurrentCulture);
+
+ if (visitor.UserId != null)
+ {
+ var user = await UserService.GetUserAsync(visitor.UserId.Value, PageState.Site.SiteId);
+ if (user != null)
+ {
+ _user = user.DisplayName;
+ }
+ }
+ }
+ else
+ {
+ AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Visitor {VisitorId} {Error}", _visitorId, ex.Message);
+ AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
+ }
+ }
+ }
diff --git a/Oqtane.Client/Modules/Admin/Visitors/Index.razor b/Oqtane.Client/Modules/Admin/Visitors/Index.razor
new file mode 100644
index 00000000..d09d8e0d
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Visitors/Index.razor
@@ -0,0 +1,144 @@
+@namespace Oqtane.Modules.Admin.Visitors
+@inherits ModuleBase
+@inject IVisitorService VisitorService
+@inject ISiteService SiteService
+@inject IStringLocalizer Localizer
+@inject IStringLocalizer SharedLocalizer
+
+@if (_visitors == null)
+{
+ @SharedLocalizer["Loading"]
+}
+else
+{
+
+
+
+
+
+ TypeChanged(e))">
+ @Localizer["AllVisitors"]
+ @Localizer["UsersOnly"]
+
+
+
+ DateChanged(e))">
+ @Localizer["PastDay"]
+ @Localizer["PastWeek"]
+ @Localizer["PastMonth"]
+
+
+
+
+
+
+
+
+ @Localizer["IP"]
+ @Localizer["User"]
+ @Localizer["Language"]
+ @Localizer["Visits"]
+ @Localizer["Visited"]
+ @Localizer["Created"]
+
+
+
+ @context.IPAddress
+
+ @if (context.UserId != null)
+ {
+ @context.User.DisplayName
+ }
+
+ @context.Language
+ @context.Visits
+ @context.VisitedOn
+ @context.CreatedOn
+
+
+
+
+
+
+
Visitor Tracking Enabled?
+
+
+ @SharedLocalizer["Yes"]
+ @SharedLocalizer["No"]
+
+
+
+
+
+ @SharedLocalizer["Save"]
+
+
+}
+
+@code {
+ private bool _users = false;
+ private int _days = 1;
+ private List _visitors;
+ private string _visitortracking;
+
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
+
+ protected override async Task OnParametersSetAsync()
+ {
+ await GetVisitors();
+ _visitortracking = PageState.Site.VisitorTracking.ToString();
+ }
+
+ private async void TypeChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ _users = bool.Parse(e.Value.ToString());
+ await GetVisitors();
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error On TypeChanged");
+ }
+ }
+
+ private async void DateChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ _days = int.Parse(e.Value.ToString());
+ await GetVisitors();
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error On DateChanged");
+ }
+ }
+
+ private async Task GetVisitors()
+ {
+ _visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
+ if (_users)
+ {
+ _visitors = _visitors.Where(item => item.UserId != null).ToList();
+ }
+ }
+
+ private async Task SaveSiteSettings()
+ {
+ try
+ {
+ var site = PageState.Site;
+ site.VisitorTracking = bool.Parse(_visitortracking);
+ await SiteService.UpdateSiteAsync(site);
+ AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
+ AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor
index 5cda1386..4e21b91a 100644
--- a/Oqtane.Client/Modules/Controls/ActionDialog.razor
+++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor
@@ -17,7 +17,7 @@
@@ -30,16 +30,17 @@
{
if (Disabled)
{
- @((MarkupString)_iconSpan) @Text
+ @((MarkupString)_iconSpan) @Text
}
else
{
- @((MarkupString)_iconSpan) @Text
+ @((MarkupString)_iconSpan) @Text
}
}
@code {
private bool _visible = false;
+ private string _permissions = string.Empty;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@@ -59,6 +60,9 @@
[Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
+ [Parameter]
+ public string Permissions { get; set; } // optional - can be used to specify a permission string
+
[Parameter]
public string Class { get; set; } // optional
@@ -105,6 +109,7 @@
Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message);
+ _permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_authorized = IsAuthorized();
}
@@ -138,10 +143,10 @@
authorized = true;
break;
case SecurityAccessLevel.View:
- authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, ModuleState.Permissions);
+ authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, _permissions);
break;
case SecurityAccessLevel.Edit:
- authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions);
+ authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, _permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor
index cf2cd1df..0ba0dfbe 100644
--- a/Oqtane.Client/Modules/Controls/ActionLink.razor
+++ b/Oqtane.Client/Modules/Controls/ActionLink.razor
@@ -6,101 +6,118 @@
{
if (Disabled)
{
- @((MarkupString)_iconSpan) @_text
- }
- else
- {
- @((MarkupString)_iconSpan) @_text
- }
+ @((MarkupString)_iconSpan) @_text
+ }
+ else
+ {
+ if (OnClick == null)
+ {
+ @((MarkupString)_iconSpan) @_text
+ }
+ else
+ {
+ @((MarkupString)_iconSpan) @_text
+ }
+ }
}
@code {
- private string _text = string.Empty;
- private string _url = string.Empty;
- private string _parameters = string.Empty;
- private string _classname = "btn btn-primary";
- private string _style = string.Empty;
- private bool _editmode = false;
- private bool _authorized = false;
- private string _iconSpan = string.Empty;
+ private string _text = string.Empty;
+ private string _parameters = string.Empty;
+ private string _url = string.Empty;
+ private string _permissions = string.Empty;
+ private bool _editmode = false;
+ private bool _authorized = false;
+ private string _classname = "btn btn-primary";
+ private string _style = string.Empty;
+ private string _iconSpan = string.Empty;
- [Parameter]
- public string Action { get; set; } // required
+ [Parameter]
+ public string Action { get; set; } // required
- [Parameter]
- public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
+ [Parameter]
+ public string Text { get; set; } // optional - defaults to Action if not specified
- [Parameter]
- public string Text { get; set; } // optional - defaults to Action if not specified
+ [Parameter]
+ public string Parameters { get; set; } // optional - querystring parameters should be in the form of "id=x&name=y"
- [Parameter]
- public string Parameters { get; set; } // optional - querystring parameter should be in the form of "id=x&name=y"
+ [Parameter]
+ public int ModuleId { get; set; } = -1; // optional - allows the link to target a specific moduleid
- [Parameter]
- public string Class { get; set; } // optional - defaults to primary if not specified
+ [Parameter]
+ public Action OnClick { get; set; } = null; // optional - executes a method in the calling component
- [Parameter]
- public string Style { get; set; } // optional
+ [Parameter]
+ public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
- [Parameter]
- public bool Disabled { get; set; } // optional
+ [Parameter]
+ public string Permissions { get; set; } // optional - can be used to specify a permission string
- [Parameter]
- public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false.
+ [Parameter]
+ public bool Disabled { get; set; } // optional
- [Parameter]
- public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
+ [Parameter]
+ public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false.
- [Parameter]
- public bool IconOnly { get; set; } // optional - specifies only icon in link
+ [Parameter]
+ public string Class { get; set; } // optional - defaults to primary if not specified
- protected override void OnParametersSet()
- {
- base.OnParametersSet();
+ [Parameter]
+ public string Style { get; set; } // optional
- _text = Action;
- if (!string.IsNullOrEmpty(Text))
- {
- _text = Text;
- }
+ [Parameter]
+ public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
- if (IconOnly && !string.IsNullOrEmpty(IconName))
- {
- _text = string.Empty;
- }
+ [Parameter]
+ public bool IconOnly { get; set; } // optional - specifies only icon in link
- if (!string.IsNullOrEmpty(Parameters))
- {
- _parameters = Parameters;
- }
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
- if (!string.IsNullOrEmpty(Class))
- {
- _classname = Class;
- }
+ _text = Action;
+ if (!string.IsNullOrEmpty(Text))
+ {
+ _text = Text;
+ }
- if (!string.IsNullOrEmpty(Style))
- {
- _style = Style;
- }
+ if (IconOnly && !string.IsNullOrEmpty(IconName))
+ {
+ _text = string.Empty;
+ }
- if (!string.IsNullOrEmpty(EditMode))
- {
- _editmode = bool.Parse(EditMode);
- }
+ if (!string.IsNullOrEmpty(Parameters))
+ {
+ _parameters = Parameters;
+ }
- if (!string.IsNullOrEmpty(IconName))
- {
- if (!IconName.Contains(" "))
- {
- IconName = "oi oi-" + IconName;
- }
- _iconSpan = $" {(IconOnly ? "" : " ")}";
+ if (!string.IsNullOrEmpty(Class))
+ {
+ _classname = Class;
+ }
- }
+ if (!string.IsNullOrEmpty(Style))
+ {
+ _style = Style;
+ }
- _text = Localize(nameof(Text), _text);
- _url = EditUrl(Action, _parameters);
+ if (!string.IsNullOrEmpty(EditMode))
+ {
+ _editmode = bool.Parse(EditMode);
+ }
+
+ if (!string.IsNullOrEmpty(IconName))
+ {
+ if (!IconName.Contains(" "))
+ {
+ IconName = "oi oi-" + IconName;
+ }
+ _iconSpan = $" {(IconOnly ? "" : " ")}";
+ }
+
+ _permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
+ _text = Localize(nameof(Text), _text);
+ _url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
_authorized = IsAuthorized();
}
@@ -136,10 +153,10 @@
authorized = true;
break;
case SecurityAccessLevel.View:
- authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions);
+ authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, _permissions);
break;
case SecurityAccessLevel.Edit:
- authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions);
+ authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor
index fec95246..40dc54ca 100644
--- a/Oqtane.Client/Modules/Controls/Pager.razor
+++ b/Oqtane.Client/Modules/Controls/Pager.razor
@@ -4,7 +4,7 @@
@if (ItemList != null)
{
- @if (Toolbar == "Top" && _pages > 0 && Items.Count() > _maxItems)
+ @if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{
}
- @if (Toolbar == "Bottom" && _pages > 0 && Items.Count() > _maxItems)
+ @if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{