9/2/2020 Admin

Creating a Custom Distribution of Blazor Oqtane Using Site Templates


Blazor Oqtane allows you to create a Custom Distribution that installs your custom modules and custom content. This allows you to distribute Oqtane in a way, that when an end-user installs it, the site has your custom module configured and ready to work.

Oqtane

Oqtane is an application that is built using Microsoft’s Blazor technology. It allows you to deploy and run modules written in Blazor. When Oqtane is deployed and running, it provides a dynamic web experience that can be run as client side Blazor or as server side Blazor.

You can find out more about Oqtane at this link: www.oqtane.org

Custom Site Templates

The functionality is implemented using custom Site Templates.

Shaun Walker, the creator of Oqtane, and the author of the feature, describes it this way:

“The focus of Oqtane is to allow developers to build web applications more productively. The requirement is that a developer may want to create a custom theme, some custom modules, and a custom site template. They would package these with Oqtane as a "distribution". When a user installs the system it would automatically install all of the modules, theme, and site template to create a custom application for a specific business purpose.”

“What I am envisioning here is a simple interface ( ie. ISiteTemplate ) which has a Name property and a generic method for CreateSite(Site site). Within this method a developer would have full control over the site creation process. They could create any entities they desire ( ie. users, roles, pages, modules, folders, files, jobs, etc.... ).”

“…Inside the CreateSite() method, the developer would have the ability to call any Repository method it wants - ie, AddPage(), AddRole(), etc...”

Examine the Default Template

image

The Site Template, used when installing Oqtane, is defined in the SiteTemplate property in the appsettings.json file.

If you don't define a Site Template the Default Template is used.

image

A Site Template inherits from ISiteTemplate and implements a CreateSite method, that takes a Site object as a parameter, and returns a collection of PageTemplate.

A PageTemplate can contain a collection of PageTemplateModule that is used to define any modules that are to be loaded on the page.

The full definition is below:

								
									public
									class
									SiteTemplate
								
									{
								
									public
									string
									Name {
									get;
									set; }
								
									public
									string
									TypeName {
									get;
									set; }
								
									}
								

								
									public
									class
									PageTemplate
								
									{
								
									public
									string
									Name {
									get;
									set; }
								
									public
									string
									Parent {
									get;
									set; }
								
									public
									string
									Path {
									get;
									set; }
								
									public
									string
									Icon {
									get;
									set; }
								
									public
									bool
									IsNavigation {
									get;
									set; }
								
									public
									bool
									IsPersonalizable {
									get;
									set; }
								
									public
									string
									PagePermissions {
									get;
									set; }
								
									public
									List<PageTemplateModule> PageTemplateModules {
									get;
									set; }
								

								
									[Obsolete("This property is obsolete",
									false)]
								
									public
									bool
									EditMode {
									get;
									set; }
								
									}
								

								
									public
									class
									PageTemplateModule
								
									{
								
									public
									string
									ModuleDefinitionName {
									get;
									set; }
								
									public
									string
									Title {
									get;
									set; }
								
									public
									string
									Pane {
									get;
									set; }
								
									public
									string
									ModulePermissions {
									get;
									set; }
								
									public
									string
									Content {
									get;
									set; }
								
									}

image

Oqtane includes two Site Templates, DefaultSiteTemplate and EmptySiteTemplate.

Below is an example of code from the DefaultSiteTemplate (with the content edited for brevity).

Notice that it defines a PageTemplate, ads a HtmlText module, and sets the content of the HtmlText module:

								
									public
									class
									DefaultSiteTemplate : ISiteTemplate
								
									{
								
									private
									readonly
									IWebHostEnvironment _environment;
								
									private
									readonly
									ISiteRepository _siteRepository;
								
									private
									readonly
									IFolderRepository _folderRepository;
								
									private
									readonly
									IFileRepository _fileRepository;
								

								
									public
									DefaultSiteTemplate(IWebHostEnvironment environment,
								
									ISiteRepository siteRepository,
								
									IFolderRepository folderRepository,
								
									IFileRepository fileRepository)
								
									{
								
									_environment = environment;
								
									_siteRepository = siteRepository;
								
									_folderRepository = folderRepository;
								
									_fileRepository = fileRepository;
								
									}
								

								
									public
									string
									Name
								
									{
								
									get
									{
									return
									"Default Site Template"; }
								
									}
								

								
									public
									List<PageTemplate> CreateSite(Site site)
								
									{
								
									List<PageTemplate> _pageTemplates =
									new
									List<PageTemplate>();
								

								
									_pageTemplates.Add(new
									PageTemplate
								
									{
								
									Name = "Home",
								
									Parent = "",
								
									Path = "",
								
									Icon = "home",
								
									IsNavigation =
									true,
								
									IsPersonalizable =
									false,
								
									PagePermissions =
									new
									List<Permission> {
								
									new
									Permission(PermissionNames.View, Constants.AllUsersRole,
									true),
								
									new
									Permission(PermissionNames.View, Constants.AdminRole,
									true),
								
									new
									Permission(PermissionNames.Edit, Constants.AdminRole,
									true)
								
									}.EncodePermissions(),
								
									PageTemplateModules =
									new
									List<PageTemplateModule> {
								
									new
									PageTemplateModule {
								
									ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client",
								
									Title = "Welcome To Oqtane...", Pane = "Content",
								
									ModulePermissions =
									new
									List<Permission> {
								
									new
									Permission(PermissionNames.View, Constants.AllUsersRole,
									true),
								
									new
									Permission(PermissionNames.View, Constants.AdminRole,
									true),
								
									new
									Permission(PermissionNames.Edit, Constants.AdminRole,
									true)
								
									}.EncodePermissions(),
								
									Content = "<p>Hello World!</p>"
								
									}
								
									}
								
									});
								

								
									return
									_pageTemplates;
								
									}
								
									}

Templates can also perform any other logic, that can be executed using C# code.


The following example shows how the Default Template copies the file for the Oqtane logo, to the proper location, and adds a reference to it in the database:

								
									if
									(System.IO.File.Exists(
								
									Path.Combine(_environment.WebRootPath, "images", "logo-white.png")))
								
									{
								
									string
									folderpath = Utilities.PathCombine(
								
									_environment.ContentRootPath, "Content", "Tenants", site.TenantId.ToString(),
								
									"Sites", site.SiteId.ToString(), Path.DirectorySeparatorChar.ToString());
								

								
									System.IO.Directory.CreateDirectory(folderpath);
								
									if
									(!System.IO.File.Exists(Path.Combine(folderpath, "logo-white.png")))
								
									{
								
									System.IO.File.Copy(Path.Combine(_environment.WebRootPath,
								
									"images", "logo-white.png"), Path.Combine(folderpath, "logo-white.png"));
								
									}
								

								
									Folder folder = _folderRepository.GetFolder(site.SiteId, "");
								

								
									Oqtane.Models.File file = _fileRepository.AddFile(
								
									new
									Oqtane.Models.File {
								
									FolderId = folder.FolderId,
								
									Name = "logo-white.png",
								
									Extension = "png", Size = 8192,
								
									ImageHeight = 80, ImageWidth = 250
								
									});
								

								
									site.LogoFileId = file.FileId;
								
									_siteRepository.UpdateSite(site);
								
									}

Creating A Custom Template

image

To create our own custom Site Template, we can add a class to:

oqtane.framework\Oqtane.Server\Infrastructure\SiteTemplates\CustomSiteTemplate.cs

Note: You do not have to alter the Oqtane source code to create a new template, you can simply create a separate project (that inherits from ISiteTemplate) and include your assembly (.dll) in the distribution. You would need to also include the code/assemblys for any custom modules you want to load in the template.

We use the following code:

								
									using
									Oqtane.Models;
								
									using
									Oqtane.Infrastructure;
								
									using
									System.Collections.Generic;
								
									using
									Oqtane.Repository;
								
									using
									Microsoft.AspNetCore.Hosting;
								
									using
									Oqtane.Extensions;
								
									using
									Oqtane.Shared;
								
									using
									System.IO;
								

								
									namespace
									Oqtane.SiteTemplates
								
{
								
									public
									class
									CustomSiteTemplate : ISiteTemplate
								
									{
								

								
									private
									readonly
									IWebHostEnvironment _environment;
								
									private
									readonly
									ISiteRepository _siteRepository;
								
									private
									readonly
									IFolderRepository _folderRepository;
								
									private
									readonly
									IFileRepository _fileRepository;
								

								
									public
									CustomSiteTemplate(
								
									IWebHostEnvironment environment,
								
									ISiteRepository siteRepository,
								
									IFolderRepository folderRepository,
								
									IFileRepository fileRepository)
								
									{
								
									_environment = environment;
								
									_siteRepository = siteRepository;
								
									_folderRepository = folderRepository;
								
									_fileRepository = fileRepository;
								
									}
								

								
									public
									string
									Name
								
									{
								
									get
									{
									return
									"Custom Site Template"; }
								
									}
								

								
									public
									List<PageTemplate> CreateSite(Site site)
								
									{
								
									List<PageTemplate> _pageTemplates =
								
									new
									List<PageTemplate>();
								

								
									_pageTemplates.Add(new
									PageTemplate
								
									{
								
									Name = "Home",
								
									Parent = "",
								
									Path = "",
								
									Icon = "home",
								
									IsNavigation =
									true,
								
									IsPersonalizable =
									false,
								
									PagePermissions =
									new
									List<Permission> {
								
									new
									Permission(PermissionNames.View,
								
									Constants.AllUsersRole,
									true),
								
									new
									Permission(PermissionNames.View,
								
									Constants.AdminRole,
									true),
								
									new
									Permission(PermissionNames.Edit,
								
									Constants.AdminRole,
									true)
								
									}.EncodePermissions(),
								
									PageTemplateModules =
									new
									List<PageTemplateModule> {
								
									new
									PageTemplateModule {
								
									ModuleDefinitionName =
								
									"Oqtane.Modules.HtmlText, Oqtane.Client",
								
									Title = "Welcome To Oqtane...",
								
									Pane = "Content",
								
									ModulePermissions =
									new
									List<Permission> {
								
									new
									Permission(PermissionNames.View,
								
									Constants.AllUsersRole,
									true),
								
									new
									Permission(PermissionNames.View,
								
									Constants.AdminRole,
									true),
								
									new
									Permission(PermissionNames.Edit,
								
									Constants.AdminRole,
									true)
								
									}.EncodePermissions(),
								
									Content = "<h4>This is my Custom Site Template</h4>"
								
									}
								
									}
								
									});
								

								
									if
									(System.IO.File.Exists(Path.Combine(
								
									_environment.WebRootPath,
								
									"images", "logo-white.png")))
								
									{
								
									string
									folderpath = Utilities.PathCombine(
								
									_environment.ContentRootPath,
								
									"Content",
								
									"Tenants",
								
									site.TenantId.ToString(),
								
									"Sites", site.SiteId.ToString(),
								
									Path.DirectorySeparatorChar.ToString());
								

								
									System.IO.Directory.CreateDirectory(folderpath);
								

								
									if
									(!System.IO.File.Exists(Path.Combine(folderpath,
								
									"logo-white.png")))
								
									{
								
									System.IO.File.Copy(Path.Combine(_environment.WebRootPath,
								
									"images", "logo-white.png"), Path.Combine(
								
									folderpath,
								
									"logo-white.png"));
								
									}
								

								
									Folder folder = _folderRepository.GetFolder(site.SiteId, "");
								

								
									Oqtane.Models.File file = _fileRepository.AddFile(
								
									new
									Oqtane.Models.File {
								
									FolderId = folder.FolderId, Name = "logo-white.png",
								
									Extension = "png",
								
									Size = 8192,
								
									ImageHeight = 80,
								
									ImageWidth = 250 });
								

								
									site.LogoFileId = file.FileId;
								
									_siteRepository.UpdateSite(site);
								
									}
								

								
									return
									_pageTemplates;
								
									}
								
									}
								
}

We set the template to load, when Oqtane is installed, by setting the SiteTemplate property in the appsettings.json file:

								
{
								
									"ConnectionStrings": {
								
									"DefaultConnection": ""
								
									},
								
									"Runtime": "Server",
								
									"Installation": {
								
									"DefaultAlias": "",
								
									"HostPassword": "",
								
									"HostEmail": "",
								
									"SiteTemplate": "Oqtane.SiteTemplates.CustomSiteTemplate, Oqtane.Server",
								
									"DefaultTheme": "",
								
									"DefaultLayout": "",
								
									"DefaultContainer": ""
								
									}
								
}

image

When we install Oqtane, the custom Site Template is used.

Using Deploy To Azure

image

If we want to use the Deploy to Azure button (from our own GitHub repository), we can do that with the following additional steps.

image

In this example the code will reside in its own GitHub project called AdefWebserver/oqtane.framework, in its own GitHub branch,  called custom-template.

We update the azuredeploy.json file, in the root of the project, to point to the repository and the branch:

image

Next, we update the README.md file, in the root of the project…

image

We alter the url of the Deploy to Azure button to point to the azuredeploy.json file in the root of the project (note that it is URL encoded):

								
https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fadefwebserver%2Foqtane.framework%2Fcustom-template%2Fazuredeploy.json

When the Deploy to Azure button is clicked, a wizard will display and allow the user to install Oqtane using the custom Site Template.

For more information see: Oqtane Deploy to Azure.

Links

What is Blazor Oqtane?

ISiteTemplate ideas and extensions #296

Sites should be created based on ISiteTemplate #281

Oqtane Deploy to Azure

An error has occurred. This application may no longer respond until reloaded. Reload 🗙