diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor
index 3a8a94ce..aaffa1f7 100644
--- a/Oqtane.Client/Modules/Controls/ActionDialog.razor
+++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor
@@ -28,7 +28,14 @@
}
@if (authorized)
{
-
+ if (Disabled)
+ {
+
+ }
+ else
+ {
+
+ }
}
@code {
@@ -36,13 +43,13 @@
public string Header { get; set; } // required
[Parameter]
- public string Message { get; set; } // required
+ public string Message { get; set; } // required
[Parameter]
public string Text { get; set; } // optional - defaults to Action if not specified
[Parameter]
- public string Action { get; set; } // optional
+ public string Action { get; set; } // optional
[Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
@@ -50,6 +57,9 @@
[Parameter]
public string Class { get; set; } // optional
+ [Parameter]
+ public bool Disabled { get; set; } // optional
+
[Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor
index d3c3a08b..2ed9c3e7 100644
--- a/Oqtane.Client/Modules/Controls/ActionLink.razor
+++ b/Oqtane.Client/Modules/Controls/ActionLink.razor
@@ -4,7 +4,14 @@
@if (authorized)
{
- @text
+ if (Disabled)
+ {
+ @text
+ }
+ else
+ {
+ @text
+ }
}
@code {
@@ -26,6 +33,9 @@
[Parameter]
public string Style { get; set; } // optional
+ [Parameter]
+ public bool Disabled { get; set; } // optional
+
[Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
diff --git a/Oqtane.Client/Shared/SiteRouter.razor b/Oqtane.Client/Shared/SiteRouter.razor
index 2438ed57..9cbed5f8 100644
--- a/Oqtane.Client/Shared/SiteRouter.razor
+++ b/Oqtane.Client/Shared/SiteRouter.razor
@@ -190,7 +190,10 @@
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = Reload.Page;
- editmode = page.EditMode;
+ if (page!=null)
+ {
+ editmode = page.EditMode;
+ }
}
user = null;
diff --git a/Oqtane.Client/Themes/Controls/ControlPanel.razor b/Oqtane.Client/Themes/Controls/ControlPanel.razor
index 68a80cfe..e433f30e 100644
--- a/Oqtane.Client/Themes/Controls/ControlPanel.razor
+++ b/Oqtane.Client/Themes/Controls/ControlPanel.razor
@@ -301,7 +301,7 @@
private async Task AddModule()
{
- if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions))
+ if (UserSecurity.IsAuthorized(PageState.User, "Edit", PageState.Page.Permissions) && !string.IsNullOrWhiteSpace(moduledefinitionname) && moduledefinitionname != "-")
{
if (moduletype == "new")
{
diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs
index 23b5c4d7..dc8361ec 100644
--- a/Oqtane.Server/Controllers/SiteController.cs
+++ b/Oqtane.Server/Controllers/SiteController.cs
@@ -87,8 +87,15 @@ namespace Oqtane.Controllers
[Authorize(Roles = Constants.HostRole)]
public void Delete(int id)
{
- Sites.DeleteSite(id);
- logger.Log(LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id);
+ if (Sites.GetSites().Count() > 1)
+ {
+ Sites.DeleteSite(id);
+ logger.Log(LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id);
+ }
+ else
+ {
+ logger.Log(LogLevel.Warning, this, LogFunction.Delete, "Unable to delete the root site.");
+ }
}
}
}
diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs
index 495930ba..b51aace3 100644
--- a/Oqtane.Server/Repository/SiteRepository.cs
+++ b/Oqtane.Server/Repository/SiteRepository.cs
@@ -142,10 +142,13 @@ namespace Oqtane.Repository
}
public void DeleteSite(int siteId)
- {
- Site site = db.Site.Find(siteId);
- db.Site.Remove(site);
- db.SaveChanges();
+ {
+ if (db.Site.Count() > 1)
+ {
+ var site = db.Site.Find(siteId);
+ db.Site.Remove(site);
+ db.SaveChanges();
+ }
}
private void CreateSite(Site site)
diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json
index dbfefb7f..7f07c90a 100644
--- a/Oqtane.Server/appsettings.json
+++ b/Oqtane.Server/appsettings.json
@@ -2,4 +2,4 @@
"ConnectionStrings": {
"DefaultConnection": ""
}
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index e719bd89..f3fa1aa3 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,9 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera
**To get started with Oqtane:**
- 1. Oqtane is currently compatible with **[.NET Core 3.1](https://dotnet.microsoft.com/download/dotnet-core/3.1)**.
+ 1. Install **[.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)**.
- 2. Install the latest edition of [Visual Studio 2019](https://visualstudio.com/vs/) (version 16.4 or higher) with the **ASP.NET and web development** workload. Installing the latest edition will also install the latest version of .NET Core 3.1.
+ 2. Install the latest edition of [Visual Studio 2019](https://visualstudio.com/vs/) (version 16.4 or higher) with the **ASP.NET and web development** workload. If you do not have a SQL Server installation available already and you wish to use LocalDB for development, you must also install the **.NET desktop development workload**.
3. Download or Clone the Oqtane source code to your local system. Open the **Oqtane.sln** solution file. If you want to develop using **server-side** Blazor (which includes a full debugging experience in Visual Studio) you should choose to Build the solution using the default Debug configuration. If you want to develop using **client-side** Blazor (WebAssembly) you should first choose the "Wasm" configuration option in the Visual Studio toolbar and then Build.
@@ -18,6 +18,29 @@ Oqtane uses Blazor, a new web framework for .NET Core that lets you build intera
# Roadmap
This project is a work in progress and the schedule for implementing enhancements is dependent upon the availability of community members who are willing/able to assist.
+V1 (MVP)
+- Multi-Tenant ( Shared Database & Isolated Database ) **done**
+- Modular Architecture / Headless API **done**
+- Dynamic Page Compositing Model / Site & Page Management **done**
+- Authentication / User Management / Profile Management **done**
+- Authorization / Roles Management / Granular Permissions **done**
+- Dynamic Routing **done**
+- Extensibility via Custom Modules **done**
+- Extensibility via Custom Themes **done**
+- Event Logging **done**
+- Folder / File Management **done**
+- Recycle Bin **done**
+- Scheduled Jobs ( Background Processing ) **done**
+- Notifications / Email Delivery **done**
+- Auto-Upgrade Framework **done**
+
+V.Next
+- Optional Encryption of Settings Values ( ie. via an IsSecure flag )
+- Localization
+- Migrate to Code-Behind Pattern ( *.razor.cs )
+- Generic Repository Pattern
+- JwT token authentication
+
# Background
Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly templates (skins), and extensibility via third party modules.
@@ -55,3 +78,6 @@ Control panel for adding, editing, and deleting pages as well as adding new modu

+Admin dashboard for accessing the variuous administrative features of the framework:
+
+