1/22/2022 Admin

Oqtane Module Creator


image

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.

What makes Oqtane different from other Blazor applications, is that it produces the entire website, not just a single application. Oqtane was created by Shaun Walker and is inspired by the DotNetNuke web application framework. Oqtane is a native Blazor application written from the ground up using modern .NET Core technology. It is a modular framework offering a fully dynamic page compositing model, multi-site support, designer friendly templates (skins), and extensibility via third party modules.

To learn more about Oqtane see What is Blazor Oqtane?

Install A Development Version Of Oqtane

image

To follow along with this tutorial, you will need to install Visual Studio 2022 (or later) with the ASP.NET and web development, and the .NET Core workloads.

image

Go to https://github.com/oqtane/oqtane.framework/releases and download and unzip the latest release version of the Oqtane source code.

image

Open the Oqtane.sln solution file in Visual Studio.

image

Select Build then Build Solution.

image

Select Oqtane.Server as the Startup Project, and then select Debug, then Start Without Debugging to run the application.

image

Fill in the information for the installer and click the Install Now button.

The account you enter for Username will be the Host (Administrator) account.

image

The site will install.

image

Your web browser will open, and the site will display.

Click the Login button to log in.

image

Log in with the Host account you created earlier using the installer.

Create an Oqtane Module

Creating an Oqtane module allows you to create a module that can be distributed to other Oqtane instances.

An Oqtane module, created using the Oqtane Module Creator, is created outside of the Oqtane solution in a folder at the same level as Oqtane. It contains all of the project and solution files configured so that it can build and deploy the artifacts to the Oqtane instance. It also has the ability to package a module into a Nuget package (when the Solution is built in Release mode).

image

Click the Develop tab to open the OqtaneModule Creator.

Fill in the form and click Create Module.

image

A message will display indicating the Module has been created.

image

Note the Location that indicates where the module is located on your local hard drive.

image

We can look at the file system to see that a new folder was created.

This folder contains all the code created by the wizard, including a Visual Studio solution file.

Open that file in another instance of Visual Studio (keep the instance of Visual Studio that contains the Oqtane solution open).

image

The solution will display in the Visual Studio Solution Explorer.

image

Select Build then Build Solution.

image

The module will build.

image

If you get “The file is locked by IIS Express Worker Process” build error messages, close the web browser running Oqtane and try to build again.

If that doesn’t work, close the Visual Studio instance that contains Oqtane and re-open the Oqtane solution.

image

When you return to the web browser running Oqtane (or if you re-started Oqtane in the first Visual Studio instance using Start Without Debugging), it will now display the Activate Module button on the Develop tab.

Click the Activate Module button.

Explore The Module

image

The module will display under the Module Creator banner.

It will display a Add HelloWorld button.

image

Clicking the Add HelloWorld button will bring up a popup that allows us to add items.

image

Clicking the Edit button next to an existing item will bring up a popup that allows us to edit the selected item.

image

Any changes can be made and saved.

image

Clicking the Pencil icon will take us into tab edit mode. This allows access to the module instance settings.

image
This provides access to Manage Settings.

image

This provides access to the custom Settings for the module instance.

image
You can enter a custom settings value.

Explore The Module Definition

image

Click the Gear icon to open the Control Panel.

image

Select the Admin Dashboard.

image

Select Module Management.

image

Click the Edit button to view the Module definition.

image

Click the Permissions tab…

image

…and control the default visibility and access to the module.

Note: A module can have several instances in the Oqtane application, each with its own permissions.

Place The Module On A Page

image

We can click on the Home tab to navigate to the main page, ensure we are in edit mode, and delete the existing instances of the HtmlText modules.

image

We can then select the Gear icon to open the Control Panel.

image

We can then add an instance of the module to the page.

image

We can then close the Control Panel.

URL Routing

image

We will note that the instance of the module on the Home tab has no records.

image

If we return to the module instance on the Develop tab, we see that there are records.

image

If we click on the Add HelloWorld button, on each instance of the module, and examine the URL, it provides insights into how this works.

image

On the Develop tab the URL resembles the image above.

image

On the Home tab the URL resembles the image above.

image

The URL is composed of the following parts that help segment data for each module instance:

  • Site - An Oqtane installation is composed of one or more tenants, each with its own site URL
  • Path - An Oqtane tab can have a custom name for the URL path to load the page otherwise, the tab name is used for the URL path. If the path is omitted, the Home tab will be displayed.
  • Module Instance Id - An Oqtane module can have multiple instances, each with a unique module ID
  • Module Component - Typical controls are Edit and Settings. Additional controls can be created and loaded.

Looking At The Code and Data

image

In Visual Studio, we can see that the wizard created a lot of code that gives us a good starting point for our custom module.

image

We can open the database using the Visual Studio SQL Server Object Explorer.

image

When we look in the database, we see that the table has been created to hold the data for the module code was created.

image

We can also look at the data in the Settings table.

image

We see the value we entered in the Settings popup earlier.

Update The Data Layer

image

We can update the data layer of the module to an a IpAddress column.

Add a new file called 01000100_AddIpAdressColumn.cs using the following code:

								
									using
									Microsoft.EntityFrameworkCore.Infrastructure;
								
									using
									Microsoft.EntityFrameworkCore.Migrations;
								
									using
									Oqtane.Databases.Interfaces;
								
									using
									Oqtane.Migrations;
								
									using
									ADefWebserver.HelloWorld.Migrations.EntityBuilders;
								
									using
									ADefWebserver.HelloWorld.Repository;
								

								
									namespace
									ADefWebserver.HelloWorld.Migrations
								
{
								
									[DbContext(typeof(HelloWorldContext))]
								
									[Migration("HelloWorld.01.00.01.00")]
								
									public
									class
									AddIpAddressColumn : MultiDatabaseMigration
								
									{
								
									public
									AddIpAddressColumn(IDatabase database) :
									base(database)
								
									{
								
									}
								

								
									protected
									override
									void
									Up(MigrationBuilder migrationBuilder)
								
									{
								
									var helloWorldEntityBuilder =
									new
									HelloWorldEntityBuilder(migrationBuilder, ActiveDatabase);
								

								
									//Add IpAddress column to ADefWebserverHelloWorld table
								
									//ADefWebserverHelloWorld table is declared in HelloWorldEntityBuilder
									
								
									helloWorldEntityBuilder.AddStringColumn("IpAddress", 200,
									true,
									true);
								
									}
								

								
									protected
									override
									void
									Down(MigrationBuilder migrationBuilder)
								
									{
								
									var helloWorldEntityBuilder =
									new
									HelloWorldEntityBuilder(migrationBuilder, ActiveDatabase);
								

								
									helloWorldEntityBuilder.DropColumn("IpAddress");
								
									}
								
									}
								
}

image

In the Client project, open the ModuleInfo.cs file and update the Version property to "1.0.1" and the ReleaseVersions property to "1.0.0,1.0.1" so the code reads:

								
									using
									Oqtane.Models;
								
									using
									Oqtane.Modules;
								

								
									namespace
									ADefWebserver.HelloWorld
								
{
								
									public
									class
									ModuleInfo : IModule
								
									{
								
									public
									ModuleDefinition ModuleDefinition =>
									new
									ModuleDefinition
								
									{
								
									Name = "HelloWorld",
								
									Description = "HelloWorld",
								
									Version = "1.0.1",
								
									ServerManagerType = "ADefWebserver.HelloWorld.Manager.HelloWorldManager,
									" +
								
									"ADefWebserver.HelloWorld.Server.Oqtane",
								
									ReleaseVersions = "1.0.0,1.0.1",
								
									Dependencies = "ADefWebserver.HelloWorld.Shared.Oqtane",
								
									PackageName = "ADefWebserver.HelloWorld"
								
									};
								
									}
								
}

image

Also, go into the Properties of the Client, Server, and Shared projects, in the Visual Studio Solution Explorer, and update the Package Version to 1.0.1.

image

Finally, open the ADefWebserver.HelloWorld.nuspec file and change the version to "1.0.1".

image

Switch the ADefWebserver.HelloWorld solution to Release mode and then rebuild it.

image

This will cause a new, updated NuGet package to be created and deployed to the Oqtane site.

image

Switch the ADefWebserver.HelloWorld solution back to Debug mode

image

Close the web browser running the Oqtane site, and in the Visual Studio instance running the Oqtane site, restart the Oqtane site (using Start Without Debugging).

The Oqtane site will detect the updated NuGet package and run the updated .sql script.

We can confirm this because the version for the module, in Module Management, will have changed to 1.0.1.

image

In the Visual Studio SQL Server Object Explorer Refresh the database.

image

You will see that the new field has been added to the database table.

Update The Code

image

We will now update the code to save and display the IpAddress.

Open the HelloWorld.cs file (in the Shared project), and add a property for the IpAddress so the code now reads:

								
									[Table("ADefWebserverHelloWorld")]
								
									public
									class
									HelloWorld : IAuditable
								
									{
								
									[Key]
								
									public
									int
									HelloWorldId {
									get;
									set; }
								
									public
									int
									ModuleId {
									get;
									set; }
								
									public
									string
									Name {
									get;
									set; }
								
									
										public
										string
										IpAddress {
										get;
										set; }
								

								
									public
									string
									CreatedBy {
									get;
									set; }
								
									public
									DateTime CreatedOn {
									get;
									set; }
								
									public
									string
									ModifiedBy {
									get;
									set; }
								
									public
									DateTime ModifiedOn {
									get;
									set; }
								
									}

image

Next, open the Edit.razor control (in the Client project), and alter it, (changes are in yellow highlight), to record and display the IpAddress:

								
@using
									Oqtane.Modules.Controls
								
@using
									ADefWebserver.HelloWorld.Services
								
@using
									ADefWebserver.HelloWorld.Models
								

								
@namespace
									ADefWebserver.HelloWorld
								
@inherits ModuleBase
								
@inject IHelloWorldService HelloWorldService
								
@inject NavigationManager NavigationManager
								
@inject IStringLocalizer<Edit> Localizer

								
									<
									form
									@ref="form"
									class="@(validated ? "
									was-validated" : "needs-validation" )"
									novalidate
									>
								
									<
									div
									class="container"
									>
								
									<
									div
									class="row mb-1 align-items-center"
									>
								
									<
									Label
									Class="col-sm-3"
									For="name"
									HelpText="Enter a name"
									ResourceKey="Name"
									>Name:
									</
									Label
									>
								
									<
									div
									class="col-sm-9"
									>
								
									<
									input
									id="name"
									class="form-control"
									@bind="@_name"
									required
									/>
								
									</
									div
									>
								
									</
									div
									>
								
									
										<
										div
										class="row mb-1 align-items-center"
										>
									
								
									
										<
										Label
										Class="col-sm-3"
										>IP Address:
										</
										Label
										>
									
								
									
										<
										div
										class="col-sm-9"
										>
									
								
									
										<
										Label
										Class="col-sm-3"
										>@_ipAddress</
										Label
										>
									
								
									
									
										</
										div
										>
									
								
									
									
										</
										div
										>
									
								
									</
									div
									>
								
									<
									button
									type="button"
									class="btn btn-success"
									@onclick="Save"
									>@Localizer["Save"]</
									button
									>
								
									<
									NavLink
									class="btn btn-secondary"
									href="@NavigateUrl()"
									>@Localizer["Cancel"]</
									NavLink
									>
								
									<
									br
									/>
									<
									br
									/>
								
									@if (PageState.Action == "Edit")
								
									{
								
									<
									AuditInfo
									CreatedBy="@_createdby"
									CreatedOn="@_createdon"
									ModifiedBy="@_modifiedby"
									ModifiedOn="@_modifiedon"
									>
									</
									AuditInfo
									>
								
									}
								
									</
									form
									>
								

								
@code {
								
									public
									override
									SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
								

								
									public
									override
									string
									Actions => "Add,Edit";
								

								
									public
									override
									string
									Title => "Manage HelloWorld";
								

								
									public
									override
									List<Resource> Resources =>
									new
									List<Resource>()
								
									{
								
									new
									Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
								
									};
								

								
									private
									ElementReference form;
								
									private
									bool
									validated =
									false;
								

								
									private
									int
									_id;
								
									private
									string
									_name;
								
									
										private
										string
										_ipAddress;
								
									private
									string
									_createdby;
								
									private
									DateTime _createdon;
								
									private
									string
									_modifiedby;
								
									private
									DateTime _modifiedon;
								

								
									protected
									override
									async Task OnInitializedAsync()
								
									{
								
									try
								
									{
								
									if
									(PageState.Action == "Edit")
								
									{
								
									_id = Int32.Parse(PageState.QueryString["id"]);
								
									HelloWorld HelloWorld = await HelloWorldService.GetHelloWorldAsync(_id, ModuleState.ModuleId);
								
									if
									(HelloWorld !=
									null)
								
									{
								
									_name = HelloWorld.Name;
								
									_ipAddress = HelloWorld.IpAddress;
								
									_createdby = HelloWorld.CreatedBy;
								
									_createdon = HelloWorld.CreatedOn;
								
									_modifiedby = HelloWorld.ModifiedBy;
								
									_modifiedon = HelloWorld.ModifiedOn;
								
									}
								
									}
								
									}
								
									catch
									(Exception ex)
								
									{
								
									await logger.LogError(ex, "Error Loading HelloWorld {HelloWorldId} {Error}", _id, ex.Message);
								
									AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
								
									}
								
									}
								

								
									private
									async Task Save()
								
									{
								
									try
								
									{
								
									validated =
									true;
								
									var interop =
									new
									Oqtane.UI.Interop(JSRuntime);
								
									if
									(await interop.FormValid(form))
								
									{
								
									if
									(PageState.Action == "Add")
								
									{
								
									HelloWorld HelloWorld =
									new
									HelloWorld();
								
									HelloWorld.ModuleId = ModuleState.ModuleId;
								
									HelloWorld.Name = _name;
								
									HelloWorld.IpAddress = PageState.User.LastIPAddress;
								
									HelloWorld = await HelloWorldService.AddHelloWorldAsync(HelloWorld);
								
									await logger.LogInformation("HelloWorld Added {HelloWorld}", HelloWorld);
								
									}
								
									else
								
									{
								
									HelloWorld HelloWorld = await HelloWorldService.GetHelloWorldAsync(_id, ModuleState.ModuleId);
								
									HelloWorld.Name = _name;
								
									HelloWorld.IpAddress = PageState.User.LastIPAddress;
								
									await HelloWorldService.UpdateHelloWorldAsync(HelloWorld);
								
									await logger.LogInformation("HelloWorld Updated {HelloWorld}", HelloWorld);
								
									}
								
									NavigationManager.NavigateTo(NavigateUrl());
								
									}
								
									else
								
									{
								
									AddModuleMessage(Localizer["Message.SaveValidation"], MessageType.Warning);
								
									}
								
									}
								
									catch
									(Exception ex)
								
									{
								
									await logger.LogError(ex, "Error Saving HelloWorld {Error}", ex.Message);
								
									AddModuleMessage(Localizer["Message.SaveError"], MessageType.Error);
								
									}
								
									}
								
}

image

Build the Solution and then close the web browser running the Oqtane site, and in the Visual Studio instance running the Oqtane site, restart the Oqtane site (using Start Without Debugging).

The Edit control will now record and display the IP Address.

Debugging Oqtane Modules

image

Debugging Oqtane modules is easy, you will want to open the Oqtane solution in one instance of Visual Studio, and the Oqtane module in a separate instance of Visual Studio.

image

In the Visual Studio instance that contains the external module, select Debug mode.

image

Then Build Solution.

image

You will see that the .pdb files, required for debugging, have been copied to the Oqtane solution.

image

In the Visual Studio instance that contains the Oqtane solution, rebuild the solution, then select Debug, then Start Without Debugging.

image

This will load Oqtane in the web browser.

image

In the Visual Studio instance that contains the external module, set a breakpoint.

image

Select Debug, then Attach to Process.

image

Attach to the iisexpress.exe process that Oqtane is running under.

image

Return to the web browser, and refresh the page and navigate to a page that contains the module.

image

Perform an action that will trigger the code being debugged.

image

When using the module, in the web browser, your break points will be hit, allowing you to debug the module.

Distribute Your Oqtane Custom Module

image

We want to build the project, create the NuGet package for the Module, and put that Nuget package in the Module folder of the Oqtane solution.

Switch to Release mode (By default, the Nuget package is only set to be created when building in Release mode).

image

Build the Solution.

image

It should Build without errors.

image

When we view the Oqtane.Server/wwwroot/Modules directory of the Oqtane solution, we see the NuGet package for the Blog module has been copied to that location.

image

You can install the .nupkg module in other Oqtane installations using Module Management, either by uploading it, or downloading it from Nuget in the Available Modules section (if you first upload the module to Nuget with the tag: Oqtane

Note: the module creator creates a *.nuspec for your module which contains the required Nuget tags automatically.

Download

The sample project is available on the Downloads page on this site.

You must have Visual Studio 2022 (or higher) installed to run the code.

Links

www.oqtane.org

oqtane.framework

What is Blazor Oqtane?

Upgrading a Blazor Oqtane Module

Blazor Oqtane Survey Module

Using Syncfusion In Oqtane Modules

Using Radzen In Oqtane Modules

Using Custom JavaScript in Blazor Oqtane

Creating a Custom Distribution of Blazor Oqtane Using Site Templates

Upgrading a Blazor Oqtane Module

Focus on Module Development #348

Cross Platform Database Support On Overdrive

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