-
-
+
+
-
+
+
+
+
+
@@ -24,24 +23,49 @@
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } }
- public string Email { get; set; } = "";
- public string Password { get; set; } = "";
+ string Username = "";
+ string Password = "";
+ string Email = "";
private async Task RegisterUser()
{
- User user = new User();
- user.SiteId = PageState.Site.SiteId;
- user.Username = Email;
- user.DisplayName = Email;
- user.Email = Email;
- user.IsHost = false;
- user.Password = Password;
- await UserService.AddUserAsync(user);
- NavigationManager.NavigateTo("");
+ try
+ {
+ if (Username != "" && Password != "" && Email != "")
+ {
+ User user = new User();
+ user.SiteId = PageState.Site.SiteId;
+ user.Username = Username;
+ user.DisplayName = Username;
+ user.Email = Email;
+ user.Password = Password;
+ user = await UserService.AddUserAsync(user);
+
+ if (user != null)
+ {
+ await logger.LogInformation("User Created {Username} {Email}", Username, Email);
+ NavigationManager.NavigateTo(NavigateUrl(""));
+ }
+ else
+ {
+ await logger.LogError("Error Adding User {Username} {Email}", Username, Email);
+ AddModuleMessage("Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.", MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", Username, Email, ex.Message);
+ AddModuleMessage("Error Adding User", MessageType.Error);
+ }
}
private void Cancel()
{
- NavigationManager.NavigateTo(NavigateUrl("")); // navigate to home
+ NavigationManager.NavigateTo(NavigateUrl(""));
}
}
diff --git a/Oqtane.Client/Modules/Admin/Roles/Add.razor b/Oqtane.Client/Modules/Admin/Roles/Add.razor
new file mode 100644
index 00000000..f4e26aa8
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Roles/Add.razor
@@ -0,0 +1,79 @@
+@namespace Oqtane.Modules.Admin.Roles
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IRoleService RoleService
+
+
+
+
Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
+
+ string name = "";
+ string description = "";
+ string isautoassigned = "False";
+ string issystem = "False";
+
+ private async Task SaveRole()
+ {
+ try
+ {
+ Role role = new Role();
+ role.SiteId = PageState.Page.SiteId;
+ role.Name = name;
+ role.Description = description;
+ role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned));
+ role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem));
+ role = await RoleService.AddRoleAsync(role);
+ await logger.LogInformation("Role Added {Role}", role);
+
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Adding Role", ex.Message);
+ AddModuleMessage(ex.Message, MessageType.Error);
+ }
+ }
+
+}
diff --git a/Oqtane.Client/Modules/Admin/Roles/Edit.razor b/Oqtane.Client/Modules/Admin/Roles/Edit.razor
new file mode 100644
index 00000000..3ab20d91
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Roles/Edit.razor
@@ -0,0 +1,98 @@
+@namespace Oqtane.Modules.Admin.Roles
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IRoleService RoleService
+
+
+
+
Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
+
+ int roleid;
+ string name = "";
+ string description = "";
+ string isautoassigned = "False";
+ string issystem = "False";
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ roleid = Int32.Parse(PageState.QueryString["id"]);
+ Role role = await RoleService.GetRoleAsync(roleid);
+ if (role != null)
+ {
+ name = role.Name;
+ description = role.Description;
+ isautoassigned = role.IsAutoAssigned.ToString();
+ issystem = role.IsSystem.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Role {RoleId} {Error}", roleid, ex.Message);
+ AddModuleMessage("Error Loading Role", MessageType.Error);
+ }
+ }
+
+ private async Task SaveRole()
+ {
+ try
+ {
+ Role role = await RoleService.GetRoleAsync(roleid);
+ role.Name = name;
+ role.Description = description;
+ role.IsAutoAssigned = (isautoassigned == null ? false : Boolean.Parse(isautoassigned));
+ role.IsSystem = (issystem == null ? false : Boolean.Parse(issystem));
+ role = await RoleService.UpdateRoleAsync(role);
+ await logger.LogInformation("Role Saved {Role}", role);
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving Role {RoleId} {Error}", roleid, ex.Message);
+ AddModuleMessage("Error Saving Role", MessageType.Error);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/Roles/Index.razor b/Oqtane.Client/Modules/Admin/Roles/Index.razor
index ac5deb59..79124760 100644
--- a/Oqtane.Client/Modules/Admin/Roles/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Roles/Index.razor
@@ -1,9 +1,4 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Services
-@using Oqtane.Models
-@using Oqtane.Modules
-@using Oqtane.Modules.Controls
-@namespace Oqtane.Modules.Admin.Roles
+@namespace Oqtane.Modules.Admin.Roles
@inherits ModuleBase
@inject IRoleService RoleService
@@ -13,21 +8,20 @@
}
else
{
-
-
-
- Name |
-
-
-
- @foreach (var Role in Roles)
- {
-
- @Role.Name |
-
- }
-
-
+
+
+
+
+
+ |
+ |
+ @context.Name |
+
+
}
@code {
@@ -35,8 +29,23 @@ else
List
Roles;
- protected override async Task OnInitializedAsync()
+ protected override async Task OnParametersSetAsync()
{
Roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
}
+
+ private async Task DeleteRole(Role Role)
+ {
+ try
+ {
+ await RoleService.DeleteRoleAsync(Role.RoleId);
+ await logger.LogInformation("Role Deleted {Role}", Role);
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Deleting Role {Role} {Error}", Role, ex.Message);
+ AddModuleMessage("Error Deleting Role", MessageType.Error);
+ }
+ }
}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor
index 83315f12..66a7d303 100644
--- a/Oqtane.Client/Modules/Admin/Sites/Add.razor
+++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor
@@ -1,17 +1,11 @@
-@using Microsoft.AspNetCore.Components.Routing
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Models
-@using Oqtane.Services
-@using Oqtane.Modules
-@using Oqtane.Shared
-@using Oqtane.Security
-@namespace Oqtane.Modules.Admin.Sites
+@namespace Oqtane.Modules.Admin.Sites
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ITenantService TenantService
@inject IAliasService AliasService
@inject ISiteService SiteService
-@inject IPageService PageService
+@inject IThemeService ThemeService
+@inject IUserService UserService
@if (tenants == null)
{
@@ -19,46 +13,107 @@
}
else
{
-
Cancel
}
@@ -66,54 +121,152 @@ else
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+ Dictionary themes = new Dictionary();
+ Dictionary panelayouts = new Dictionary();
+ Dictionary containers = new Dictionary();
+
List tenants;
- string tenantid = "";
+ string tenantid = "-1";
string name = "";
- string url = "";
+ string urls = "";
string logo = "";
+ string themetype = "";
+ string layouttype = "";
+ string containertype = "";
+ bool isinitialized = true;
+ string username = "";
+ string password = "";
protected override async Task OnInitializedAsync()
{
tenants = await TenantService.GetTenantsAsync();
+ urls = PageState.Alias.Name;
+ themes = ThemeService.GetThemeTypes(PageState.Themes);
+ containers = ThemeService.GetContainerTypes(PageState.Themes);
+ username = PageState.User.Username;
+ }
+
+ private async void TenantChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ tenantid = (string)e.Value;
+ if (tenantid != "-1")
+ {
+ Tenant tenant = tenants.Where(item => item.TenantId == int.Parse(tenantid)).FirstOrDefault();
+ if (tenant != null)
+ {
+ isinitialized = tenant.IsInitialized;
+ StateHasChanged();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", tenantid, ex.Message);
+ AddModuleMessage("Error Loading Tenant", MessageType.Error);
+ }
+ }
+
+ private async void ThemeChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ themetype = (string)e.Value;
+ if (themetype != "")
+ {
+ panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype);
+ }
+ else
+ {
+ panelayouts = new Dictionary();
+ }
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", themetype, ex.Message);
+ AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
+ }
}
private async Task SaveSite()
{
- Site site = new Site();
- site.Name = name;
- site.Logo = (logo == null ? "" : logo);
- await SiteService.AddSiteAsync(site);
- List sites = await SiteService.GetSitesAsync();
- site = sites.Where(item => item.Name == name).FirstOrDefault();
+ if (tenantid != "-1" && name != "" && urls != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype)) && !string.IsNullOrEmpty(containertype))
+ {
+ bool isvalid = true;
- Alias alias = new Alias();
- alias.Name = url;
- alias.TenantId = int.Parse(tenantid);
- alias.SiteId = site.SiteId;
- await AliasService.AddAliasAsync(alias);
+ if (!isinitialized)
+ {
+ User user = new User();
+ user.SiteId = PageState.Site.SiteId;
+ user.Username = username;
+ user.Password = password;
+ user = await UserService.LoginUserAsync(user, false, false);
+ isvalid = user.IsAuthenticated;
+ }
- // need to add a home page and admin pages
- Page p = new Page();
- p.SiteId = site.SiteId;
- p.ParentId = null;
- p.Name = "Home";
- p.Path = "";
- p.Order = 1;
- p.IsNavigation = true;
- p.ThemeType = PageState.Site.DefaultThemeType;
- p.LayoutType = "";
- p.Icon = "";
- Type type = Type.GetType(p.ThemeType);
- System.Reflection.PropertyInfo property = type.GetProperty("Panes");
- p.Panes = (string)property.GetValue(Activator.CreateInstance(type), null);
+ if (isvalid)
+ {
+ List aliases = new List();
+ urls = urls.Replace("\n", ",");
+ foreach (string name in urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ Alias alias = new Alias();
+ alias.Name = name;
+ alias.TenantId = int.Parse(tenantid);
+ alias.SiteId = -1;
+ alias = await AliasService.AddAliasAsync(alias);
+ aliases.Add(alias);
+ }
- List permissionstrings = new List();
- permissionstrings.Add(new PermissionString { PermissionName = "View", Permissions = Constants.AllUsersRole });
- permissionstrings.Add(new PermissionString { PermissionName = "Edit", Permissions = Constants.AdminRole });
- p.Permissions = UserSecurity.SetPermissionStrings(permissionstrings);
+ Site site = new Site();
+ site.TenantId = int.Parse(tenantid);
+ site.Name = name;
+ site.Logo = (logo == null ? "" : logo);
+ site.DefaultThemeType = themetype;
+ site.DefaultLayoutType = (layouttype == null ? "" : layouttype);
+ site.DefaultContainerType = containertype;
+ site = await SiteService.AddSiteAsync(site, aliases[0]);
- await PageService.AddPageAsync(p);
+ foreach(Alias alias in aliases)
+ {
+ alias.SiteId = site.SiteId;
+ await AliasService.UpdateAliasAsync(alias);
+ }
+
+ if (!isinitialized)
+ {
+ User user = new User();
+ user.SiteId = site.SiteId;
+ user.Username = username;
+ user.Password = password;
+ user.Email = PageState.User.Email;
+ user.DisplayName = PageState.User.DisplayName;
+ user = await UserService.AddUserAsync(user, aliases[0]);
+
+ if (user != null)
+ {
+ Tenant tenant = tenants.Where(item => item.TenantId == int.Parse(tenantid)).FirstOrDefault();
+ tenant.IsInitialized = true;
+ await TenantService.UpdateTenantAsync(tenant);
+ }
+ }
+ await logger.LogInformation("Site Created {Site}", site);
+
+ Uri uri = new Uri(NavigationManager.Uri);
+ NavigationManager.NavigateTo(uri.Scheme + "://" + aliases[0].Name, true);
+ }
+ else
+ {
+ await logger.LogError("Invalid Password Entered For Host {Username}", username);
+ AddModuleMessage("Invalid Host Password", MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Provide A Tenant, Site Name, Alias, And Default Theme/Container", MessageType.Warning);
+ }
- NavigationManager.NavigateTo(url, true);
}
}
diff --git a/Oqtane.Client/Modules/Admin/Sites/Edit.razor b/Oqtane.Client/Modules/Admin/Sites/Edit.razor
new file mode 100644
index 00000000..b870e57d
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Sites/Edit.razor
@@ -0,0 +1,248 @@
+@namespace Oqtane.Modules.Admin.Sites
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject ISiteService SiteService
+@inject IAliasService AliasService
+@inject IThemeService ThemeService
+
+@if (themes == null)
+{
+ Loading...
+}
+else
+{
+
+
+ Cancel
+
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ Dictionary themes = new Dictionary();
+ Dictionary panelayouts = new Dictionary();
+ Dictionary containers = new Dictionary();
+
+ Alias Alias;
+ int siteid;
+ string name = "";
+ List aliases;
+ string urls = "";
+ string logo = "";
+ string themetype;
+ string layouttype;
+ string containertype;
+
+ string createdby;
+ DateTime createdon;
+ string modifiedby;
+ DateTime modifiedon;
+ string deletedby;
+ DateTime? deletedon;
+ string isdeleted;
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ themes = ThemeService.GetThemeTypes(PageState.Themes);
+ containers = ThemeService.GetContainerTypes(PageState.Themes);
+ Alias = PageState.Aliases.Where(item => item.AliasId == Int32.Parse(PageState.QueryString["id"])).FirstOrDefault();
+
+ siteid = Alias.SiteId;
+ Site site = await SiteService.GetSiteAsync(siteid, Alias);
+ if (site != null)
+ {
+ name = site.Name;
+ aliases = PageState.Aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList();
+ foreach (Alias alias in aliases)
+ {
+ urls += alias.Name + "\n";
+ }
+ logo = site.Logo;
+ themetype = site.DefaultThemeType;
+ panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype);
+ layouttype = site.DefaultLayoutType;
+ containertype = site.DefaultContainerType;
+
+ createdby = site.CreatedBy;
+ createdon = site.CreatedOn;
+ modifiedby = site.ModifiedBy;
+ modifiedon = site.ModifiedOn;
+ deletedby = site.DeletedBy;
+ deletedon = site.DeletedOn;
+ isdeleted = site.IsDeleted.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", siteid, ex.Message);
+ AddModuleMessage(ex.Message, MessageType.Error);
+ }
+ }
+
+ private async void ThemeChanged(ChangeEventArgs e)
+ {
+ try
+ {
+ themetype = (string)e.Value;
+ if (themetype != "")
+ {
+ panelayouts = ThemeService.GetPaneLayoutTypes(PageState.Themes, themetype);
+ }
+ else
+ {
+ panelayouts = new Dictionary();
+ }
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", themetype, ex.Message);
+ AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
+ }
+ }
+
+ private async Task SaveSite()
+ {
+ try
+ {
+ if (name != "" && urls != "" && !string.IsNullOrEmpty(themetype) && (panelayouts.Count == 0 || !string.IsNullOrEmpty(layouttype)) && !string.IsNullOrEmpty(containertype))
+ {
+ Site site = await SiteService.GetSiteAsync(siteid, Alias);
+ if (site != null)
+ {
+ site.Name = name;
+ site.Logo = (logo == null ? "" : logo);
+ site.DefaultThemeType = themetype;
+ site.DefaultLayoutType = (layouttype == null ? "" : layouttype);
+ site.DefaultContainerType = containertype;
+ site.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
+
+ site = await SiteService.UpdateSiteAsync(site, Alias);
+
+ urls = urls.Replace("\n", ",");
+ string[] names = urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (Alias alias in aliases)
+ {
+ if (!names.Contains(alias.Name))
+ {
+ await AliasService.DeleteAliasAsync(alias.AliasId);
+ }
+ }
+ foreach (string name in names)
+ {
+ if (!aliases.Exists(item => item.Name == name))
+ {
+ Alias alias = new Alias();
+ alias.Name = name;
+ alias.TenantId = site.TenantId;
+ alias.SiteId = site.SiteId;
+ await AliasService.AddAliasAsync(alias);
+ }
+ }
+ await logger.LogInformation("Site Saved {Site}", site);
+
+ NavigationManager.NavigateTo(NavigateUrl(Reload.Site));
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Provide A Site Name, Alias, And Default Theme/Container", MessageType.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", siteid, ex.Message);
+ AddModuleMessage("Error Saving Site", MessageType.Error);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/Sites/Index.razor b/Oqtane.Client/Modules/Admin/Sites/Index.razor
index aa75fb4c..46152dee 100644
--- a/Oqtane.Client/Modules/Admin/Sites/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Sites/Index.razor
@@ -1,11 +1,7 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Services
-@using Oqtane.Models
-@using Oqtane.Modules
-@using Oqtane.Modules.Controls
-@namespace Oqtane.Modules.Admin.Sites
+@namespace Oqtane.Modules.Admin.Sites
@inherits ModuleBase
-
+@inject NavigationManager NavigationManager
+@inject IAliasService AliasService
@inject ISiteService SiteService
@if (sites == null)
@@ -14,31 +10,55 @@
}
else
{
-
-
-
- Name |
-
-
-
- @foreach (var site in sites)
- {
-
- @site.Name |
-
- }
-
-
+
+
+
+
+ |
+ |
+ @context.Name |
+
+
}
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
- List sites;
+ List sites;
+ string scheme;
- protected override async Task OnInitializedAsync()
+ protected override void OnParametersSet()
{
- sites = await SiteService.GetSitesAsync();
+ Uri uri = new Uri(NavigationManager.Uri);
+ scheme = uri.Scheme + "://";
+
+ sites = new List();
+ foreach (Alias alias in PageState.Aliases.OrderBy(item => item.Name))
+ {
+ if (!sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId))
+ {
+ sites.Add(alias);
+ }
+ }
+ }
+
+ private async Task DeleteSite(Alias Alias)
+ {
+ try
+ {
+ await SiteService.DeleteSiteAsync(Alias.SiteId, Alias);
+ await logger.LogInformation("Sited Deleted {Alias}", Alias);
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Deleting Site {Error}", ex.Message);
+ AddModuleMessage("Error Deleting Site", MessageType.Error);
+ }
}
}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor
new file mode 100644
index 00000000..e12a27a3
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor
@@ -0,0 +1,82 @@
+@namespace Oqtane.Modules.Admin.Tenants
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject ITenantService TenantService
+@inject IInstallationService InstallationService
+
+
+
+Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ string name = "";
+ string connectionstring = "";
+ string schema = "";
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ List tenants = await TenantService.GetTenantsAsync();
+ connectionstring = tenants.FirstOrDefault().DBConnectionString;
+ schema = tenants.FirstOrDefault().DBSchema;
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message);
+ AddModuleMessage("Error Loading Tenants", MessageType.Error);
+ }
+ }
+
+ private async Task SaveTenant()
+ {
+ ShowProgressIndicator();
+
+ connectionstring = connectionstring.Replace("\\\\", "\\");
+ GenericResponse response = await InstallationService.Install(connectionstring);
+ if (response.Success)
+ {
+ Tenant tenant = new Tenant();
+ tenant.Name = name;
+ tenant.DBConnectionString = connectionstring;
+ tenant.DBSchema = schema;
+ tenant.IsInitialized = false;
+ await TenantService.AddTenantAsync(tenant);
+ await logger.LogInformation("Tenant Created {Tenant}", tenant);
+
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ else
+ {
+ await logger.LogError("Error Creating Tenant {Error}", response.Message);
+ AddModuleMessage(response.Message, MessageType.Error);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor
new file mode 100644
index 00000000..ca0b8c9c
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor
@@ -0,0 +1,86 @@
+@namespace Oqtane.Modules.Admin.Tenants
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject ITenantService TenantService
+
+
+
+Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ int tenantid;
+ string name = "";
+ string connectionstring = "";
+ string schema = "";
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ tenantid = Int32.Parse(PageState.QueryString["id"]);
+ Tenant tenant = await TenantService.GetTenantAsync(tenantid);
+ if (tenant != null)
+ {
+ name = tenant.Name;
+ connectionstring = tenant.DBConnectionString;
+ schema = tenant.DBSchema;
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", tenantid, ex.Message);
+ AddModuleMessage("Error Loading Tenant", MessageType.Error);
+ }
+ }
+
+ private async Task SaveTenant()
+ {
+ try
+ {
+ connectionstring = connectionstring.Replace("\\\\", "\\");
+ Tenant tenant = await TenantService.GetTenantAsync(tenantid);
+ if (tenant != null)
+ {
+ tenant.Name = name;
+ tenant.DBConnectionString = connectionstring;
+ tenant.DBSchema = schema;
+ await TenantService.UpdateTenantAsync(tenant);
+ await logger.LogInformation("Tenant Saved {TenantId}", tenantid);
+
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving Tenant {TenantId} {Error}", tenantid, ex.Message);
+ AddModuleMessage("Error Saving Tenant", MessageType.Error);
+ }
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/Tenants/Index.razor b/Oqtane.Client/Modules/Admin/Tenants/Index.razor
new file mode 100644
index 00000000..5c22fcbf
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Tenants/Index.razor
@@ -0,0 +1,52 @@
+@namespace Oqtane.Modules.Admin.Tenants
+@inherits ModuleBase
+@inject ITenantService TenantService
+
+@if (tenants == null)
+{
+ Loading...
+}
+else
+{
+
+
+
+
+
+ |
+ |
+ @context.Name |
+
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ List tenants;
+
+ protected override async Task OnParametersSetAsync()
+ {
+ tenants = await TenantService.GetTenantsAsync();
+ }
+
+ private async Task DeleteTenant(Tenant Tenant)
+ {
+ try
+ {
+ await TenantService.DeleteTenantAsync(Tenant.TenantId);
+ await logger.LogInformation("Tenant Deleted {Tenant}", Tenant);
+ StateHasChanged();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Deleting Tenant {Tenant} {Error}", Tenant, ex.Message);
+ AddModuleMessage("Error Deleting Tenant", MessageType.Error);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Themes/Add.razor b/Oqtane.Client/Modules/Admin/Themes/Add.razor
new file mode 100644
index 00000000..bd243c9e
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Themes/Add.razor
@@ -0,0 +1,121 @@
+@namespace Oqtane.Modules.Admin.Themes
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IFileService FileService
+@inject IThemeService ThemeService
+@inject IPackageService PackageService
+
+
+
+
+@if (packages != null)
+{
+
+ Available Themes
+
+
+
+
+ @context.Name |
+ @context.Version |
+
+
+ |
+
+
+}
+
+@if (uploaded)
+{
+
+}
+Cancel
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ bool uploaded = false;
+ List packages;
+ FileUpload fileupload;
+
+ protected override async Task OnInitializedAsync()
+ {
+ List themes = await ThemeService.GetThemesAsync();
+ packages = await PackageService.GetPackagesAsync("theme");
+ foreach(Package package in packages.ToArray())
+ {
+ if (themes.Exists(item => Utilities.GetTypeName(item.ThemeName) == package.PackageId))
+ {
+ packages.Remove(package);
+ }
+ }
+ }
+
+ private async Task UploadTheme()
+ {
+ string[] files = await fileupload.GetFiles();
+ if (files.Length > 0)
+ {
+ if (files[0].Contains(".Theme."))
+ {
+ try
+ {
+ string result = await FileService.UploadFilesAsync("Themes", files, "");
+ if (result == "")
+ {
+ await logger.LogInformation("Theme Uploaded {Package}", files[0]);
+ AddModuleMessage("Theme Uploaded Successfully. Click Install To Complete Installation.", MessageType.Success);
+ uploaded = true;
+ StateHasChanged();
+ }
+ else
+ {
+ await logger.LogInformation("Theme Upload Failed For {Package}", result.Replace(",",", "));
+ AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Theme Upload Failed {Package} {Error}", files[0], ex.Message);
+ AddModuleMessage("Theme Upload Failed", MessageType.Error);
+ }
+ }
+ else
+ {
+ await logger.LogError("Invalid Theme Package {Package}", files[0]);
+ AddModuleMessage("Invalid Theme Package", MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Select A Theme To Upload", MessageType.Warning);
+ }
+ }
+
+ private async Task InstallThemes()
+ {
+ await ThemeService.InstallThemesAsync();
+ NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
+ }
+
+ private async Task DownloadTheme(string packageid, string version)
+ {
+ await PackageService.DownloadPackageAsync(packageid, version, "Themes");
+ AddModuleMessage("Theme Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success);
+ uploaded = true;
+ StateHasChanged();
+ }
+}
diff --git a/Oqtane.Client/Modules/Admin/Themes/Index.razor b/Oqtane.Client/Modules/Admin/Themes/Index.razor
index f272dba6..501dec1b 100644
--- a/Oqtane.Client/Modules/Admin/Themes/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Themes/Index.razor
@@ -1,42 +1,78 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Services
-@using Oqtane.Models
-@using Oqtane.Modules
-@namespace Oqtane.Modules.Admin.Themes
+@namespace Oqtane.Modules.Admin.Themes
@inherits ModuleBase
-
+@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
+@inject IPackageService PackageService
-@if (Themes == null)
+@if (themes == null)
{
Loading...
}
else
{
-
-
-
- Name |
-
-
-
- @foreach (var theme in Themes)
- {
-
- @theme.Name |
-
- }
-
-
+
+
+
+
+
+
+ @if (context.AssemblyName != "Oqtane.Client")
+ {
+
+ }
+ |
+ @context.Name |
+ @context.Version |
+
+ @if (UpgradeAvailable(context.ThemeName, context.Version))
+ {
+
+ }
+ |
+
+
}
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
- List Themes;
+ List themes;
+ List packages;
protected override async Task OnInitializedAsync()
{
- Themes = await ThemeService.GetThemesAsync();
+ themes = await ThemeService.GetThemesAsync();
+ packages = await PackageService.GetPackagesAsync("module");
+ }
+
+ private bool UpgradeAvailable(string themename, string version)
+ {
+ bool upgradeavailable = false;
+ Package package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
+ if (package != null)
+ {
+ upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
+ }
+ return upgradeavailable;
+ }
+
+ private async Task DownloadTheme(string themename, string version)
+ {
+ await PackageService.DownloadPackageAsync(themename, version, "Themes");
+ await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
+ await ThemeService.InstallThemesAsync();
+ NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
+ }
+
+ private async Task DeleteTheme(Theme Theme)
+ {
+ await ThemeService.DeleteThemeAsync(Theme.ThemeName);
+ await logger.LogInformation("Theme Deleted {Theme}", Theme);
+ NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
}
}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor
new file mode 100644
index 00000000..2ae44820
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor
@@ -0,0 +1,109 @@
+@namespace Oqtane.Modules.Admin.Upgrade
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IFileService FileService
+@inject IPackageService PackageService
+@inject IInstallationService InstallationService
+
+
+
+
+
+ |
+
+
+ |
+
+
+@if (uploaded)
+{
+
+}
+else
+{
+
+}
+
+@if (upgradeavailable)
+{
+
+ Upgrade Available
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
+
+ bool uploaded = false;
+ bool upgradeavailable = false;
+ FileUpload fileupload;
+
+ protected override async Task OnInitializedAsync()
+ {
+ List packages = await PackageService.GetPackagesAsync("framework");
+ Package package = packages.FirstOrDefault();
+ if (package != null)
+ {
+ upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
+ }
+ if (!upgradeavailable)
+ {
+ AddModuleMessage("Framework Is Up To Date", MessageType.Info);
+ }
+ }
+
+ private async Task UploadFile()
+ {
+ string[] files = await fileupload.GetFiles();
+ if (files.Length > 0)
+ {
+ if (files[0].Contains(".Framework."))
+ {
+ try
+ {
+ string result = await FileService.UploadFilesAsync("Framework", files, "");
+ if (result == "")
+ {
+ await logger.LogInformation("Framework Uploaded {Package}", files[0]);
+ AddModuleMessage("Framework Uploaded Successfully. Click Upgrade To Complete Installation.", MessageType.Success);
+ uploaded = true;
+ StateHasChanged();
+ }
+ else
+ {
+ await logger.LogInformation("Framework Upload Failed For {Package}", result.Replace(",",", "));
+ AddModuleMessage("Upload Failed For " + result.Replace(",",", ") + ". This Could Be Due To A Network Error Or Because A File Type Is Restricted.", MessageType.Error);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Framework Upload Failed {Package} {Error}", files[0], ex.Message);
+ AddModuleMessage("Framework Upload Failed. " + ex.Message, MessageType.Error);
+ }
+ }
+ else
+ {
+ await logger.LogError("Invalid Framework Package {Package}", files[0]);
+ AddModuleMessage("Invalid Framework Package", MessageType.Error);
+ }
+ }
+ else
+ {
+ AddModuleMessage("You Must Select A Framework Package To Upload", MessageType.Warning);
+ }
+ }
+
+ private async Task Upgrade()
+ {
+ await InstallationService.Upgrade();
+ NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
+ }
+
+ private async Task Download(string packageid, string version)
+ {
+ await PackageService.DownloadPackageAsync(packageid, version, "Framework");
+ await InstallationService.Upgrade();
+ NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
+ }
+}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Profile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor
similarity index 71%
rename from Oqtane.Client/Modules/Admin/Profile/Index.razor
rename to Oqtane.Client/Modules/Admin/UserProfile/Index.razor
index dc89e517..a68b1fd5 100644
--- a/Oqtane.Client/Modules/Admin/Profile/Index.razor
+++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor
@@ -1,27 +1,27 @@
-@using Microsoft.AspNetCore.Components.Routing
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Modules.Controls
-@using Oqtane.Modules
-@using Oqtane.Models
-@using Oqtane.Services
-@namespace Oqtane.Modules.Admin.Profile
+@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IProfileService ProfileService
@inject ISettingService SettingService
-
-
@if (PageState.User != null && profiles != null)
{
-
+
|
-
+
+ |
+
+
+
+
+ |
+
+
|
@@ -32,6 +32,14 @@
+
+
+
+ |
+
+
+ |
+
@foreach (Profile profile in profiles)
{
@@ -64,9 +72,10 @@
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } }
- string message = "";
- string displayname = "";
+ string username = "";
+ string password = "";
string email = "";
+ string displayname = "";
List profiles;
Dictionary settings;
string category = "";
@@ -77,19 +86,21 @@
{
if (PageState.User != null)
{
- displayname = PageState.User.DisplayName;
+ username = PageState.User.Username;
email = PageState.User.Email;
+ displayname = PageState.User.DisplayName;
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
}
else
{
- message = "Current User Is Not Logged In";
+ AddModuleMessage("Current User Is Not Logged In", MessageType.Warning);
}
}
catch (Exception ex)
{
- message = ex.Message;
+ await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
+ AddModuleMessage("Error Loading User Profile", MessageType.Error);
}
}
@@ -103,16 +114,20 @@
try
{
User user = PageState.User;
- user.DisplayName = displayname;
+ user.Username = username;
+ user.Password = password;
user.Email = email;
+ user.DisplayName = displayname;
await UserService.UpdateUserAsync(user);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
+ await logger.LogInformation("User Profile Saved");
- NavigationManager.NavigateTo("");
+ NavigationManager.NavigateTo(NavigateUrl(""));
}
catch (Exception ex)
{
- message = ex.Message;
+ await logger.LogError(ex, "Error Saving User Profile {Error}", ex.Message);
+ AddModuleMessage("Error Saving User Profile", MessageType.Error);
}
}
diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor
new file mode 100644
index 00000000..7d9e6647
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Users/Add.razor
@@ -0,0 +1,133 @@
+@namespace Oqtane.Modules.Admin.Users
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IUserService UserService
+@inject IProfileService ProfileService
+@inject ISettingService SettingService
+
+@if (profiles != null)
+{
+
+
+ Cancel
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
+
+ string username = "";
+ string password = "";
+ string email = "";
+ string displayname = "";
+ List profiles;
+ Dictionary settings;
+ string category = "";
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
+ settings = new Dictionary();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
+ AddModuleMessage("Error Loading User Profile", MessageType.Error);
+ }
+ }
+
+ private async Task SaveUser()
+ {
+ try
+ {
+ User user = new User();
+ user.SiteId = PageState.Site.SiteId;
+ user.Username = username;
+ user.Password = password;
+ user.Email = email;
+ user.DisplayName = string.IsNullOrWhiteSpace(user.DisplayName) ? user.Username : user.DisplayName;
+
+ user = await UserService.AddUserAsync(user);
+
+ if (user != null)
+ {
+ await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
+ await logger.LogInformation("User Created {User}", user);
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ else
+ {
+ await logger.LogError("Error Adding User {Username} {Email}", username, email);
+ AddModuleMessage("Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.", MessageType.Error);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", username, email, ex.Message);
+ AddModuleMessage("Error Adding User", MessageType.Error);
+ }
+ }
+
+ private void ProfileChanged(ChangeEventArgs e, string SettingName)
+ {
+ string value = (string)e.Value;
+ settings = SettingService.SetSetting(settings, SettingName, value);
+ }
+
+}
diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor
new file mode 100644
index 00000000..40a5605d
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor
@@ -0,0 +1,169 @@
+@namespace Oqtane.Modules.Admin.Users
+@inherits ModuleBase
+@inject NavigationManager NavigationManager
+@inject IUserService UserService
+@inject IProfileService ProfileService
+@inject ISettingService SettingService
+
+@if (profiles != null)
+{
+
+
+ Cancel
+
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
+
+ int userid;
+ string username = "";
+ string password = "";
+ string email = "";
+ string displayname = "";
+ List profiles;
+ Dictionary settings;
+ string category = "";
+ string createdby;
+ DateTime createdon;
+ string modifiedby;
+ DateTime modifiedon;
+ string deletedby;
+ DateTime? deletedon;
+ string isdeleted;
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
+
+ userid = Int32.Parse(PageState.QueryString["id"]);
+ User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
+ if (user != null)
+ {
+ username = user.Username;
+ email = user.Email;
+ displayname = user.DisplayName;
+ settings = await SettingService.GetUserSettingsAsync(user.UserId);
+ createdby = user.CreatedBy;
+ createdon = user.CreatedOn;
+ modifiedby = user.ModifiedBy;
+ modifiedon = user.ModifiedOn;
+ deletedby = user.DeletedBy;
+ deletedon = user.DeletedOn;
+ isdeleted = user.IsDeleted.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
+ AddModuleMessage("Error Loading User", MessageType.Error);
+ }
+ }
+
+ private string GetProfileValue(string SettingName, string DefaultValue)
+ {
+ return SettingService.GetSetting(settings, SettingName, DefaultValue);
+ }
+
+ private async Task SaveUser()
+ {
+ try
+ {
+ User user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
+ user.SiteId = PageState.Site.SiteId;
+ user.Username = username;
+ user.Password = password;
+ user.Email = email;
+ user.DisplayName = string.IsNullOrWhiteSpace(user.DisplayName) ? user.Username : user.DisplayName;
+ user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
+
+ user = await UserService.UpdateUserAsync(user);
+ await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
+ await logger.LogInformation("User Saved {User}", user);
+
+ NavigationManager.NavigateTo(NavigateUrl());
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving User {Username} {Email} {Error}", username, email, ex.Message);
+ AddModuleMessage("Error Saving User", MessageType.Error);
+ }
+ }
+
+ private void ProfileChanged(ChangeEventArgs e, string SettingName)
+ {
+ string value = (string)e.Value;
+ settings = SettingService.SetSetting(settings, SettingName, value);
+ }
+
+}
diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor
index d7c6e048..c6e52c6e 100644
--- a/Oqtane.Client/Modules/Admin/Users/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Users/Index.razor
@@ -1,43 +1,59 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Services
-@using Oqtane.Models
-@using Oqtane.Modules
-@using Oqtane.Modules.Controls
-@namespace Oqtane.Modules.Admin.Users
+@namespace Oqtane.Modules.Admin.Users
@inherits ModuleBase
-
+@inject IUserRoleService UserRoleService
@inject IUserService UserService
-@if (Users == null)
+@if (userroles == null)
{
Loading...
}
else
{
-
-
-
- Name |
-
-
-
- @foreach (var User in Users)
- {
-
- @User.Username |
-
- }
-
-
+
+
+
+
+
+ |
+ |
+ |
+ @context.User.DisplayName |
+
+
}
@code {
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
- List Users;
+ List userroles;
protected override async Task OnInitializedAsync()
{
- Users = await UserService.GetUsersAsync(PageState.Site.SiteId);
+ userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
+ userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
+ }
+
+ private async Task DeleteUser(UserRole UserRole)
+ {
+ try
+ {
+ User user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId);
+ if (user != null)
+ {
+ await UserService.DeleteUserAsync(user.UserId);
+ await logger.LogInformation("User Deleted {User}", UserRole.User);
+ StateHasChanged();
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Deleting User {User} {Error}", UserRole.User, ex.Message);
+ AddModuleMessage(ex.Message, MessageType.Error);
+ }
}
}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor
new file mode 100644
index 00000000..94179f86
--- /dev/null
+++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor
@@ -0,0 +1,187 @@
+@namespace Oqtane.Modules.Admin.Users
+@inherits ModuleBase
+@inject IRoleService RoleService
+@inject IUserRoleService UserRoleService
+
+@if (userroles == null)
+{
+ Loading...
+}
+else
+{
+
+
+ Cancel
+
+
+
+
+
+
+ @context.Role.Name |
+
+ @if (!context.Role.IsSystem)
+ {
+
+ }
+ |
+
+
+
+}
+
+@code {
+ public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
+
+ int userid;
+ List roles;
+ int roleid = -1;
+ string effectivedate = "";
+ string expirydate = "";
+ List userroles;
+
+ protected override async Task OnInitializedAsync()
+ {
+ try
+ {
+ userid = Int32.Parse(PageState.QueryString["id"]);
+ roles = await RoleService.GetRolesAsync(PageState.Site.SiteId);
+ await GetUserRoles();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading Roles {Error}", ex.Message);
+ AddModuleMessage("Error Loading Roles", MessageType.Error);
+ }
+ }
+
+ private async Task GetUserRoles()
+ {
+ try
+ {
+ userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
+ userroles = userroles.Where(item => item.UserId == userid).ToList();
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Loading User Roles {UserId} {Error}", userid, ex.Message);
+ AddModuleMessage("Error Loading User Roles", MessageType.Error);
+ }
+ }
+
+ private async Task SaveUserRole()
+ {
+ try
+ {
+ if (roleid != -1)
+ {
+ UserRole userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
+ if (userrole != null)
+ {
+ if (string.IsNullOrEmpty(effectivedate))
+ {
+ userrole.EffectiveDate = null;
+ }
+ else
+ {
+ userrole.EffectiveDate = DateTime.Parse(effectivedate);
+ }
+ if (string.IsNullOrEmpty(expirydate))
+ {
+ userrole.ExpiryDate = null;
+ }
+ else
+ {
+ userrole.ExpiryDate = DateTime.Parse(expirydate);
+ }
+ await UserRoleService.UpdateUserRoleAsync(userrole);
+ }
+ else
+ {
+ userrole = new UserRole();
+ userrole.UserId = userid;
+ userrole.RoleId = roleid;
+ if (string.IsNullOrEmpty(effectivedate))
+ {
+ userrole.EffectiveDate = null;
+ }
+ else
+ {
+ userrole.EffectiveDate = DateTime.Parse(effectivedate);
+ }
+ if (string.IsNullOrEmpty(expirydate))
+ {
+ userrole.ExpiryDate = null;
+ }
+ else
+ {
+ userrole.ExpiryDate = DateTime.Parse(expirydate);
+ }
+ await UserRoleService.AddUserRoleAsync(userrole);
+ }
+ await GetUserRoles();
+ await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
+ AddModuleMessage("User Assigned To Role", MessageType.Success);
+ }
+ else
+ {
+ AddModuleMessage("You Must Select A Role", MessageType.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Saving User Roles {UserId} {Error}", userid, ex.Message);
+ AddModuleMessage("Error Saving User Roles", MessageType.Error);
+ }
+ }
+
+ private async Task DeleteUserRole(int UserRoleId)
+ {
+ try
+ {
+ await UserRoleService.DeleteUserRoleAsync(UserRoleId);
+ await GetUserRoles();
+ await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
+ AddModuleMessage("User Removed From Role", MessageType.Success);
+ }
+ catch (Exception ex)
+ {
+ await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
+ AddModuleMessage("Error Removing User From Role", MessageType.Error);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor
new file mode 100644
index 00000000..41dd4b21
--- /dev/null
+++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor
@@ -0,0 +1,130 @@
+@namespace Oqtane.Modules.Controls
+@inherits ModuleBase
+
+@if (visible)
+{
+
+}
+@if (authorized)
+{
+
+}
+
+@code {
+ [Parameter]
+ public string Header { get; set; } // required
+
+ [Parameter]
+ public string Message { get; set; } // required
+
+ [Parameter]
+ public string Action { get; set; } // defaults to Ok if not specified
+
+ [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 Class { get; set; } // optional
+
+ [Parameter]
+ public Action OnClick { get; set; } // required - executes a method in the calling component
+
+ bool visible = false;
+ bool authorized = false;
+
+ protected override void OnParametersSet()
+ {
+ if (string.IsNullOrEmpty(Action))
+ {
+ Action = "Ok";
+ }
+ if (string.IsNullOrEmpty(Text))
+ {
+ Text = Action;
+ }
+ if (string.IsNullOrEmpty(Class))
+ {
+ Class = "btn btn-success";
+ }
+ authorized = IsAuthorized();
+ }
+
+ private bool IsAuthorized()
+ {
+ bool authorized = false;
+ if (PageState.EditMode)
+ {
+ SecurityAccessLevel security = SecurityAccessLevel.Host;
+ if (Security == null)
+ {
+ string typename = ModuleState.ModuleType.Replace(Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0) + ",", Action + ",");
+ Type moduleType = Type.GetType(typename);
+ if (moduleType != null)
+ {
+ var moduleobject = Activator.CreateInstance(moduleType);
+ security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
+ }
+ else
+ {
+ security = SecurityAccessLevel.Anonymous; // occurs when an action does not have a corresponding module control
+ }
+ }
+ else
+ {
+ security = Security.Value;
+ }
+ switch (security)
+ {
+ case SecurityAccessLevel.Anonymous:
+ authorized = true;
+ break;
+ case SecurityAccessLevel.View:
+ authorized = UserSecurity.IsAuthorized(PageState.User, "View", ModuleState.Permissions);
+ break;
+ case SecurityAccessLevel.Edit:
+ authorized = UserSecurity.IsAuthorized(PageState.User, "Edit", ModuleState.Permissions);
+ break;
+ case SecurityAccessLevel.Admin:
+ authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole);
+ break;
+ case SecurityAccessLevel.Host:
+ authorized = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
+ break;
+ }
+ }
+ return authorized;
+ }
+
+ private void DisplayModal()
+ {
+ visible = !visible;
+ StateHasChanged();
+ }
+
+ private void Confirm()
+ {
+ DisplayModal();
+ OnClick();
+ }
+}
diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor
index 498ea8e7..afb0a8bb 100644
--- a/Oqtane.Client/Modules/Controls/ActionLink.razor
+++ b/Oqtane.Client/Modules/Controls/ActionLink.razor
@@ -1,10 +1,4 @@
-@using Microsoft.AspNetCore.Components.Routing
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Modules
-@using Oqtane.Services
-@using Oqtane.Shared
-@using Oqtane.Security
-@namespace Oqtane.Modules.Controls
+@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@inject IUserService UserService
@@ -15,16 +9,19 @@
@code {
[Parameter]
- public string Action { get; set; }
+ public string Action { get; set; } // required
[Parameter]
- public string Text { get; set; } // optional
+ public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter]
- public string Parameters { get; set; } // optional
+ public string Text { get; set; } // optional - defaults to Action if not specified
[Parameter]
- public string Class { get; set; } // optional
+ public string Parameters { get; set; } // optional - querystring parameter should be in the form of "id=x&name=y"
+
+ [Parameter]
+ public string Class { get; set; } // optional - defaults to primary if not specified
[Parameter]
public string Style { get; set; } // optional
@@ -39,12 +36,12 @@
protected override void OnParametersSet()
{
text = Action;
- if (!String.IsNullOrEmpty(Text))
+ if (!string.IsNullOrEmpty(Text))
{
text = Text;
}
- if (!String.IsNullOrEmpty(Parameters))
+ if (!string.IsNullOrEmpty(Parameters))
{
parameters = Parameters;
}
@@ -60,34 +57,53 @@
}
url = EditUrl(Action, parameters);
+ authorized = IsAuthorized();
+ }
+ private bool IsAuthorized()
+ {
+ bool authorized = false;
if (PageState.EditMode)
{
- string typename = ModuleState.ModuleType.Replace(Utilities.GetTypeNameClass(ModuleState.ModuleType) + ",", Action + ",");
- Type moduleType = Type.GetType(typename);
- if (moduleType != null)
+ SecurityAccessLevel security = SecurityAccessLevel.Host;
+ if (Security == null)
{
- var moduleobject = Activator.CreateInstance(moduleType);
- SecurityAccessLevel SecurityAccessLevel = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
- switch (SecurityAccessLevel)
+ string typename = ModuleState.ModuleType.Replace(Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0) + ",", Action + ",");
+ Type moduleType = Type.GetType(typename);
+ if (moduleType != null)
{
- case SecurityAccessLevel.Anonymous:
- authorized = true;
- break;
- case SecurityAccessLevel.View:
- authorized = UserSecurity.IsAuthorized(PageState.User, "View", ModuleState.Permissions);
- break;
- case SecurityAccessLevel.Edit:
- authorized = UserSecurity.IsAuthorized(PageState.User, "Edit", ModuleState.Permissions);
- break;
- case SecurityAccessLevel.Admin:
- authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole);
- break;
- case SecurityAccessLevel.Host:
- authorized = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
- break;
+ var moduleobject = Activator.CreateInstance(moduleType);
+ security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
+ }
+ else
+ {
+ security = SecurityAccessLevel.Anonymous; // occurs when an action does not have a corresponding module control
+ Class = "btn btn-warning"; // alert developer of missing module comtrol
}
}
+ else
+ {
+ security = Security.Value;
+ }
+ switch (security)
+ {
+ case SecurityAccessLevel.Anonymous:
+ authorized = true;
+ break;
+ case SecurityAccessLevel.View:
+ authorized = UserSecurity.IsAuthorized(PageState.User, "View", ModuleState.Permissions);
+ break;
+ case SecurityAccessLevel.Edit:
+ authorized = UserSecurity.IsAuthorized(PageState.User, "Edit", ModuleState.Permissions);
+ break;
+ case SecurityAccessLevel.Admin:
+ authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole);
+ break;
+ case SecurityAccessLevel.Host:
+ authorized = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
+ break;
+ }
}
+ return authorized;
}
}
diff --git a/Oqtane.Client/Modules/Controls/AuditInfo.razor b/Oqtane.Client/Modules/Controls/AuditInfo.razor
index f83705a7..40938da3 100644
--- a/Oqtane.Client/Modules/Controls/AuditInfo.razor
+++ b/Oqtane.Client/Modules/Controls/AuditInfo.razor
@@ -1,6 +1,4 @@
-@using Oqtane.Modules
-@using Microsoft.AspNetCore.Components.Web
-@namespace Oqtane.Modules.Controls
+@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@if (text != "")
@@ -20,6 +18,15 @@
[Parameter]
public DateTime ModifiedOn { get; set; }
+
+ [Parameter]
+ public string DeletedBy { get; set; }
+
+ [Parameter]
+ public DateTime? DeletedOn { get; set; }
+
+ [Parameter]
+ public bool IsDeleted { get; set; }
[Parameter]
public string Style { get; set; }
@@ -56,5 +63,19 @@
}
text += "";
}
+
+ if (!String.IsNullOrEmpty(DeletedBy) || DeletedOn.HasValue)
+ {
+ text += "Deleted ";
+ if (!String.IsNullOrEmpty(DeletedBy))
+ {
+ text += " by " + DeletedBy + "";
+ }
+ if (DeletedOn != null)
+ {
+ text += " on " + DeletedOn.Value.ToString("MMM dd yyyy HH:mm:ss") + "";
+ }
+ text += "
";
+ }
}
}
diff --git a/Oqtane.Client/Modules/Controls/FileUpload.razor b/Oqtane.Client/Modules/Controls/FileUpload.razor
index 626673a2..43f0228a 100644
--- a/Oqtane.Client/Modules/Controls/FileUpload.razor
+++ b/Oqtane.Client/Modules/Controls/FileUpload.razor
@@ -1,13 +1,13 @@
-@using Microsoft.AspNetCore.Components.Web
-@namespace Oqtane.Modules.Controls
+@namespace Oqtane.Modules.Controls
+@inject IJSRuntime jsRuntime
@if (multiple)
{
-
+
}
else
{
-
+
}
@@ -25,6 +25,7 @@ else
string progressinfoid = "";
string progressbarid = "";
string filter = "*";
+ string files = "";
bool multiple = false;
protected override void OnInitialized()
@@ -43,4 +44,11 @@ else
multiple = bool.Parse(Multiple);
}
}
+
+ public async Task GetFiles()
+ {
+ var interop = new Interop(jsRuntime);
+ string[] files = await interop.GetFiles(fileid);
+ return files;
+ }
}
diff --git a/Oqtane.Client/Modules/Controls/ModuleMessage.razor b/Oqtane.Client/Modules/Controls/ModuleMessage.razor
index 5b088b43..a756e9dd 100644
--- a/Oqtane.Client/Modules/Controls/ModuleMessage.razor
+++ b/Oqtane.Client/Modules/Controls/ModuleMessage.razor
@@ -1,12 +1,9 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Modules
-@namespace Oqtane.Modules.Controls
+@namespace Oqtane.Modules.Controls
@inherits ModuleBase
-@if (Message != "")
+@if (!string.IsNullOrEmpty(message))
{
- @Message
-
+ @message
}
@@ -17,24 +14,40 @@
[Parameter]
public MessageType Type { get; set; }
- string type = "alert alert-danger";
+ string message = "";
+ string classname = "alert alert-danger";
- protected override void OnInitialized()
+ protected override void OnParametersSet()
{
- switch (Type)
+ message = Message;
+ classname = GetMessageType(Type);
+ }
+
+ public void SetModuleMessage(string message, MessageType type)
+ {
+ this.message = message;
+ classname = GetMessageType(type);
+ StateHasChanged();
+ }
+
+ private string GetMessageType(MessageType type)
+ {
+ string classname = "";
+ switch (type)
{
case MessageType.Success:
- type = "alert alert-success";
+ classname = "alert alert-success";
break;
case MessageType.Info:
- type = "alert alert-info";
+ classname = "alert alert-info";
break;
case MessageType.Warning:
- type = "alert alert-warning";
+ classname = "alert alert-warning";
break;
case MessageType.Error:
- type = "alert alert-danger";
+ classname = "alert alert-danger";
break;
}
+ return classname;
}
}
diff --git a/Oqtane.Client/Modules/Controls/Pager.razor b/Oqtane.Client/Modules/Controls/Pager.razor
new file mode 100644
index 00000000..b9bded82
--- /dev/null
+++ b/Oqtane.Client/Modules/Controls/Pager.razor
@@ -0,0 +1,195 @@
+@namespace Oqtane.Modules.Controls
+@inherits ModuleBase
+@typeparam TableItem
+
+
+ @if(Format == "Table")
+ {
+
+
+ @Header
+
+
+ @foreach (var item in ItemList)
+ {
+ @Row(item)
+ }
+
+
+ }
+ @if(Format == "Grid")
+ {
+
+
@Header
+ @foreach (var item in ItemList)
+ {
+
@Row(item)
+ }
+
+ }
+
+ @if (Page > MaxPages)
+ {
+
+ }
+ @if (EndPage > 1)
+ {
+
+ @for (int i = StartPage; i <= EndPage; i++)
+ {
+ var pager = i;
+
+ }
+
+ }
+ @if (EndPage < Pages)
+ {
+
+ }
+ @if (EndPage > 1)
+ {
+ Page @Page of @Pages
+ }
+
+
+
+@code {
+ int Pages = 0;
+ int Page = 1;
+ int MaxItems;
+ int MaxPages;
+ int StartPage;
+ int EndPage;
+
+ [Parameter]
+ public string Format { get; set; }
+
+ [Parameter]
+ public RenderFragment Header { get; set; }
+
+ [Parameter]
+ public RenderFragment Row { get; set; }
+
+ [Parameter]
+ public IEnumerable Items { get; set; }
+
+ [Parameter]
+ public string PageSize { get; set; }
+
+ [Parameter]
+ public string DisplayPages { get; set; }
+
+ [Parameter]
+ public string Class { get; set; }
+
+ IEnumerable ItemList { get; set; }
+
+ protected override void OnParametersSet()
+ {
+ if (string.IsNullOrEmpty(Format))
+ {
+ Format = "Table";
+ }
+ if (string.IsNullOrEmpty(Class))
+ {
+ if (Format == "Table")
+ {
+ Class = "table table-borderless";
+ }
+ else
+ {
+ Class = "container";
+ }
+ }
+ if (string.IsNullOrEmpty(PageSize))
+ {
+ MaxItems = 10;
+ }
+ else
+ {
+ MaxItems = int.Parse(PageSize);
+ }
+ if (string.IsNullOrEmpty(DisplayPages))
+ {
+ MaxPages = 5;
+ }
+ else
+ {
+ MaxPages = int.Parse(DisplayPages);
+ }
+
+ if (Items != null)
+ {
+ ItemList = Items.Skip((Page - 1) * MaxItems).Take(MaxItems);
+ Pages = (int)Math.Ceiling(Items.Count() / (decimal)MaxItems);
+ }
+
+ SetPagerSize("forward");
+ }
+
+ public void UpdateList(int CurrentPage)
+ {
+ ItemList = Items.Skip((CurrentPage - 1) * MaxItems).Take(MaxItems);
+ Page = CurrentPage;
+ StateHasChanged();
+ }
+
+ public void SetPagerSize(string direction)
+ {
+ if (direction == "forward")
+ {
+ if (EndPage + 1 < Pages)
+ {
+ StartPage = EndPage + 1;
+ }
+ else
+ {
+ StartPage = 1;
+ }
+
+ if (EndPage + MaxPages < Pages)
+ {
+ EndPage = StartPage + MaxPages - 1;
+ }
+ else
+ {
+ EndPage = Pages;
+ }
+ StateHasChanged();
+ }
+ else if (direction == "back")
+ {
+ EndPage = StartPage - 1;
+ StartPage = StartPage - MaxPages;
+ }
+ }
+
+ public void NavigateToPage(string direction)
+ {
+ if (direction == "next")
+ {
+ if (Page < Pages)
+ {
+ if (Page == EndPage)
+ {
+ SetPagerSize("forward");
+ }
+ Page += 1;
+ }
+ }
+ else if (direction == "previous")
+ {
+ if (Page > 1)
+ {
+ if (Page == StartPage)
+ {
+ SetPagerSize("back");
+ }
+ Page -= 1;
+ }
+ }
+ UpdateList(Page);
+ }
+}
\ No newline at end of file
diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor
index 130b0dde..2345eb37 100644
--- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor
+++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor
@@ -1,10 +1,4 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Services
-@using Oqtane.Modules
-@using Oqtane.Models
-@using Oqtane.Security
-@using Oqtane.Shared
-@namespace Oqtane.Modules.Controls
+@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@inject IRoleService RoleService
@inject IUserService UserService
@@ -83,6 +77,9 @@
[Parameter]
public string EntityName { get; set; }
+ [Parameter]
+ public string PermissionNames { get; set; }
+
[Parameter]
public string Permissions { get; set; }
@@ -95,11 +92,14 @@
protected override async Task OnInitializedAsync()
{
- permissionnames = PageState.ModuleDefinitions.Find(item => item.ModuleDefinitionName == ModuleState.ModuleDefinitionName).Permissions;
- if (string.IsNullOrEmpty(permissionnames))
+ if (string.IsNullOrEmpty(PermissionNames))
{
permissionnames = "View,Edit";
}
+ else
+ {
+ permissionnames = PermissionNames;
+ }
roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
roles.Insert(0, new Role { Name = Constants.AllUsersRole });
diff --git a/Oqtane.Client/Modules/Controls/TabControl.razor b/Oqtane.Client/Modules/Controls/TabControl.razor
new file mode 100644
index 00000000..e6cda943
--- /dev/null
+++ b/Oqtane.Client/Modules/Controls/TabControl.razor
@@ -0,0 +1,43 @@
+@namespace Oqtane.Modules.Controls
+@inherits ModuleBase
+
+
+
+ @foreach (TabPanel tabPanel in TabPanels)
+ {
+
+ }
+
+ @ChildContent
+
+
+@code {
+ // Next line is needed so we are able to add components inside
+ [Parameter]
+ public RenderFragment ChildContent { get; set; }
+
+ public TabPanel ActiveTabPanel { get; set; }
+ List TabPanels = new List();
+
+ internal void AddTabPanel(TabPanel tabPanel)
+ {
+ TabPanels.Add(tabPanel);
+ if (TabPanels.Count == 1)
+ ActiveTabPanel = tabPanel;
+ StateHasChanged();
+ }
+
+ string GetButtonClass(TabPanel tabPanel)
+ {
+ return tabPanel == ActiveTabPanel ? "btn-primary" : "btn-secondary";
+ }
+
+ void ActivateTabPanel(TabPanel tabPanel)
+ {
+ ActiveTabPanel = tabPanel;
+ }
+}
diff --git a/Oqtane.Client/Modules/Controls/TabPanel.razor b/Oqtane.Client/Modules/Controls/TabPanel.razor
new file mode 100644
index 00000000..475d6258
--- /dev/null
+++ b/Oqtane.Client/Modules/Controls/TabPanel.razor
@@ -0,0 +1,27 @@
+@namespace Oqtane.Modules.Controls
+@inherits ModuleBase
+
+@if (Parent.ActiveTabPanel == (TabPanel)(object)this)
+{
+ @ChildContent
+}
+
+@code {
+ [CascadingParameter]
+ private TabControl Parent { get; set; }
+
+ [Parameter]
+ public RenderFragment ChildContent { get; set; }
+
+ [Parameter]
+ public string Text { get; set; }
+
+ protected override void OnInitialized()
+ {
+ if (Parent == null)
+ throw new ArgumentNullException(nameof(Parent), "TabPanel must exist within a TabControl");
+
+ base.OnInitialized();
+ Parent.AddTabPanel((TabPanel)(object)this);
+ }
+}
diff --git a/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor b/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor
index b411d723..c8881f3f 100644
--- a/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor
+++ b/Oqtane.Client/Modules/Controls/TriStateCheckBox.razor
@@ -1,5 +1,4 @@
-@using Microsoft.AspNetCore.Components.Web
-@namespace Oqtane.Modules.Controls
+@namespace Oqtane.Modules.Controls
diff --git a/Oqtane.Client/Modules/Counter/Index.razor b/Oqtane.Client/Modules/Counter/Index.razor
index 9b8dd4b3..b511cfc0 100644
--- a/Oqtane.Client/Modules/Counter/Index.razor
+++ b/Oqtane.Client/Modules/Counter/Index.razor
@@ -1,5 +1,3 @@
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Modules
@namespace Oqtane.Modules.Counter
@inherits ModuleBase
Current count: @currentCount
diff --git a/Oqtane.Client/Modules/Counter/Module.cs b/Oqtane.Client/Modules/Counter/Module.cs
index 972e366f..5f509ae2 100644
--- a/Oqtane.Client/Modules/Counter/Module.cs
+++ b/Oqtane.Client/Modules/Counter/Module.cs
@@ -1,5 +1,4 @@
-using Oqtane.Modules;
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Oqtane.Modules.Counter
{
diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor
index 887bd056..f9d891d4 100644
--- a/Oqtane.Client/Modules/HtmlText/Edit.razor
+++ b/Oqtane.Client/Modules/HtmlText/Edit.razor
@@ -1,26 +1,18 @@
-@using Microsoft.AspNetCore.Components.Routing
-@using Microsoft.AspNetCore.Components.Web
-@using Oqtane.Modules
-@using Oqtane.Modules.Controls
-@using Oqtane.Modules.HtmlText.Services
+@using Oqtane.Modules.HtmlText.Services
@using Oqtane.Modules.HtmlText.Models
-@using System.Net.Http;
-@using Oqtane.Shared;
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject HttpClient http
@inject SiteState sitestate
-
-
-