Home DNN Persona Bar Component Using Uno Platform
Post
Cancel

DNN Persona Bar Component Using Uno Platform

In DNN the Persona Bar is the admin control bar for managing sites. The Persona Bar is highly customizable from the top-level admin controls to the individual admin modules. The nice thing about developing modules for the Persona Bar is you do not need to use a specific development technique. The platform allows you to inject whatever HTML, JS, and CSS you choose, as long as you follow a few basic rules. Let’s look into adding an Uno Platform app into DNN as a Persona Bar Component.

DISCLAIMER The content of this article is a proof of concept and not intended to be followed for production use

Goals

My research with Uno Platform as a DNN Persona Bar Module is focused on loading the WASM (Web Assembly) target platform of an Uno Platform App into DNN. Currently the Persona Bar Modules allows developers to use any technique they prefer. Popular techniques in the DNN Community are

  • React
  • Angular
  • jQuery and Native HTML

All of these techniques have their pro and cons but I personally struggle with each one of them. The entire development story with Persona Bar Modules currently needs some work as there is no easy developer loop with these modules.

My Theory

The working theory is if I use a WASM (Web Assembly) target of Uno Platform, I could then dynamically load that into a Persona Bar Module. This will allow me to write my entire Persona Bar Module using WinUI & UWP code which enables a faster developer loop (for me, your mileage may vary). The new Uno Platform Persona Bar Module will still communicate with DNN the same way any other Persona Bar Modules, via Web APIs installed on the DNN site.

This model fits perfectly for anyone that has developed a mobile app or desktop app. They always interact with the back-end via API calls.

Let’s see if my theory works out

Persona Bar Basics

This article is focused on using Uno Platform with the Persona Bar and not all the details of configuring a DNN Persona Bar Module. Let’s get the most simple module created for development without an installer.

What you need to load a persona bar component

  • A single html file
  • A single javascript ifle
  • A single css file
  • An App Resource file

HTML file

1
<div id="my-persona-bar-component"></div>

Javascript file

This is the standard javascript code required by all Persona Bar Modules. These functions are executed by core platform code to load your module, it is safe to leave them all empty.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'use strict';
define(
    function () {

        return {
            init: function (wrapper, util, params, callback) {
            },

            initMobile: function (wrapper, util, params, callback) {
            },

            load: function (params, callback) {

            },

            loadMobile: function (params, callback) {
            }
        };
    });

CSS file

1
// any control specific css would go here

Resx file

The resx file is use for localization and is important to have. I don’t recommend editing this file in an xml editor, instead you should use the resx editor in Visual Studio.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<root>

  <!-- omitted xsd schema code -->

  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="nav_MySamplePersonaBarModule.Text" xml:space="preserve">
    <value>My Module</value>
  </data>
</root>

Once you create your files you create a new Persona Bar Module, by navigating to the following path on your website.

  1. Navigate to /DesktopModules/Admin/Dnn.PersonaBar/Modules
  2. Create a new folder - MyPersonaBarSample

Once you have your new folder MyPersonaBarSample you can start adding the files above.

  • Add Sample.html file
  • Add scripts/Sample.js file
  • Add css/Sample.css file
  • Add App_LocalResources/Sample.resx file

You should have a file tree that looks something like this:

/assets/img/2020-08-28/sample-personabar-module-structure.jfif

Configure Database

Since this is a rapid prototype we are going to update the database to notify DNN to render our new Persona Bar Module. Execute the following script to add it to DNN

1
2
3
4
5
6
7
8
insert into PersonaBarMenu (
    Identifier, ModuleName, ResourceKey,
    [Path], ParentId, [Order],
    AllowHost, [Enabled])
values(
    'MyPersonaBarSample', 'Sample', 'nav_MyPersonaBarSample',
    'Sample', 1, 100,
    1, 1)

Install Complete

At this point you have successfully completed a manual install of a Persona Bar Module. When you login to your DNN site you should have a new Sample Module option under the Content section

/assets/img/2020-08-28/persona-bar-sample-module.jfif

When you open the module there should be a blank white page. We haven’t added any content to our module so this is normal.

Create an Uno Platform App

Now we can create our Uno Platform Code that we want to render inside of a DNN site. If you haven’t installed the Uno Platform Visual Studio extension, go ahead and get it installed.

Now you can open Visual Studio and create a new Uno Platform project. For our example we are going to create the basic Hello World Sample. Once the new Uno Platform project has been created we can try running the WASM option. It should launch IIS Express and open the Hello World app.

/assets/img/2020-08-28/uno-hello-world.jfif

Troubleshooting

If the loaded app does not look like my screenshot above you may be running into problems with how the app is compiling. You can’t just skip ahead here as if it doesn’t load in IIS Express, it won’t load in DNN. Take a look at the official Uno Platform docs

Configure DNN

Now that the Uno Platform App is running in IIS Express we can start adding it to our DNN site. We will need to complete the following actions to get Uno Platform working

  1. Copy dist folder of Uno Platform into DNN
  2. Update web.config
  3. Add Uno Platform App to Persona Bar Module

Copy dist Folder

After a successful build, go and find the dist in the WASM project.

1
Dnn.PersonaBar.Uno.Wasm\bin\Debug\netstandard2.0\dist

Copy the entire dist folder to your persona bar modules location in DNN

1
~/DesktopModules/Admin/Dnn.PersonaBar/Modules/MyPersonaBarSample/dist

Update web.config

The lat step is to update the web.config. When Uno Platform creates it’s own index.html for hosting the entire website, it adds some custom rules to the web.config that need to be copied over to the DNN web.config.

****

Add a urlCompression node

1
2
<!-- Disable compression as we're doing it through pre-compressed files -->
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" />

Add the following StaticContent, don’t worry about duplicates

1
2
3
4
5
6
7
8
9
<remove fileExtension=".dll" />
<remove fileExtension=".wasm" />
<remove fileExtension=".woff" />
<remove fileExtension=".woff2" />
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
<mimeMap fileExtension=".clr" mimeType="application/octet-stream" />
<mimeMap fileExtension=".pdb" mimeType="application/octet-stream" />
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
<mimeMap fileExtension=".woff2" mimeType="application/font-woff" />

****

Add the following rewrite rules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<rule name="Lookup for pre-compressed brotli file" stopProcessing="true">
  <match url="(.*)$"/>
  <conditions>
    <!-- Match brotli requests -->
    <add input="{HTTP_ACCEPT_ENCODING}" pattern="br" />

    <!-- Match all but pre-compressed files -->
    <add input="{REQUEST_URI}" pattern="^(?!/_compressed_br/)(.*)$" />

    <!-- Check if the pre-compressed file exists on the disk -->
    <add input="{DOCUMENT_ROOT}/_compressed_br/{C:0}" matchType="IsFile" negate="false" />
  </conditions>
  <action type="Rewrite" url="/_compressed_br{C:0}" />
</rule>

<rule name="Lookup for pre-compressed gzip file" stopProcessing="true">
  <match url="(.*)$"/>
  <conditions>
    <!-- Match gzip requests -->
    <add input="{HTTP_ACCEPT_ENCODING}" pattern="gzip" />

    <!-- Match all but pre-compressed files -->
    <add input="{REQUEST_URI}" pattern="^(?!/_compressed_gz/)(.*)$" />

    <!-- Check if the pre-compressed file exists on the disk -->
    <add input="{DOCUMENT_ROOT}/_compressed_gz/{C:0}" matchType="IsFile" negate="false" />
  </conditions>
  <action type="Rewrite" url="/_compressed_gz{C:0}" />
</rule>

Add the following outboundRules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<outboundRules>
  <rule name="Adjust content encoding for gzip pre-compressed files" enabled="true" stopProcessing="true">
    <match serverVariable="RESPONSE_CONTENT_ENCODING" pattern="" />
    <conditions>
    <add input="{REQUEST_URI}" pattern="/_compressed_gz/.*$" />
    </conditions>
    <action type="Rewrite" value="gzip"/>
  </rule>
  <rule name="Adjust content encoding for brotli pre-compressed files" enabled="true" stopProcessing="true">
    <match serverVariable="RESPONSE_CONTENT_ENCODING" pattern="" />
    <conditions>
    <add input="{REQUEST_URI}" pattern="/_compressed_br/.*$" />
    </conditions>
    <action type="Rewrite" value="br"/>
  </rule>
</outboundRules>

Once you have added all of the web.config statements you can load up your DNN site and test if Uno Platform is working correctly. Navigate directly to your index.html inside of DNN

  • ~/DesktopModules/Admin/Dnn.PersonaBar/Modules/MyPersonaBarSample/dist/index.html

If the Uno Platform hello world app loads, then we are ready to finish the Persona Bar Module configuration!

Add Uno Platform App to Persona Bar Module

Now that we our new Uno Platform App copied over to the DNN site and the web.config updated, we can configure the Persona Bar Module we created earlier to load the Uno Platform App.

  1. Add iFrame to Sample.html
  2. Apply styles to make it full screen

Add iFrame

Navigate to ~/DesktopModules/Admin/Dnn.PersonaBar/Modules/MyPersonaBarSample and open the Sample.html file. Let’s update the markup that will be injected to load our Uno Platform App as an iFrame.

1
<iframe src="/DesktopModules/Admin/Dnn.PersonaBar/Modules/MyPersonaBarSample/dist/index.html"></iframe>

Apply Styles

Just adding the iFrame to the page is going to make the Uno Platform App look small. We need to add some styles to ensure that it takes up the entire screen space provided by the Persona Bar. Update your html to include a container element and add some iframe properties.

1
2
3
<div class="uno-container">
    <iframe src="/DesktopModules/Admin/Dnn.PersonaBar/Modules/MyPersonaBarSample/dist/index.html" frameborder="0" marginheight="0" marginwidth="0" width="100%" height="100%" scrolling="auto" ></iframe>
</div>

Once that is done, open up your css file located in css/Sample.css and add the following code

1
2
3
4
5
6
7
8
9
10
11
iframe {
    display: block;
    width: 100%;
    border: none;
    overflow-y: auto;
    overflow-x: hidden;
}

.uno-container {
    height: 100vh;
}

Test the Persona Bar Module

Everything is all configured and you can test your new Persona Bar Module to see if it works! In the browser of your choice, navigate to your DNN site and login as a Host user. Open the content display and see if your new Uno Platform Persona Bar Module loads.

/assets/img/2020-08-28/uno-dnn-prototype.gif

Conclusion

This has been an experiement and published findings of research on integrating Uno Platform WASM (Web Assembly) into DNN as a new way to build Persona Bar Modules. The techniques described here should not be used without considering security implications.

  • Hosting the Uno Platform App via index.html creates a security vulnerability as anyone that can guess that address can navigate to it.
  • You shouldn’t manually install a Persona Bar Module using this technique. Package an installer with a DNN manifest file to prevent errors

Security

The security vulnerability with this experiment is a big problem, but nothing DNN can’t solve. It goes out of scope of this article, but it is still useful to share the general technique to solve it. Follow the steps below and your Uno Platform App will be secured to only Host users of the Persona Bar.

  1. Update your dist/index.html to dist/index.html.resources which will block any requests trying to load it.
  2. Add a new Web API controller that will check DNN permissions for a host user and then stream the html page as the response.
  3. Update the iFrame in your Persona Bar Module to load the [HttpGet] request that loads the HTML.

If you implement this technique correctly, I would be safe hosting an Uno Platform based Persona Bar Module in a DNN Production site today.

Findings

This experiment was a huge success, we can now load an Uno Platform App into a DNN Persona Bar Module which really elevates a lot of the problems developing Persona Bar Modules. Here is a list of some observations I made during this experiment.

  • Using Uno Platform to isolate your Persona Bar Module creates a powerful development experience. Now you can focus on how the UI instead of getting all the components to work nicely.
  • Full Debugger Support - This is true with any module loaded by iFrame, you will get full debugger support.
  • Out of the box there is no support for using the dnn.utility.sf (service framework) which provides a nice set of utilities for communicating with the Web API controller of a Persona Bar Module. You will either need to build something your Uno Platform App or create a bridge to the javascript. My recommendation is just build one in the Uno Platform App.
  • Longer load times - Since the browser needs to load all of Uno Platform it has a longer load time than traditional html based Persona Bar Modules. This is okay since these pages are used by host users and not a general site user. Also, the Persona Bar has an aggressive caching mechanism so it should only be loaded once per session.
  • Using an iFrame is a major limitation, I hope in the future we can inject the Uno Platform App as a component on the page instead of loading an entire iFrame element.

Bottom Line - Will I use this technique on my next Persona Bar Module?

100% - Yes

I think this is a great way to develop Persona Bar Modules and plan to use it on my next Persona Bar Module.

-Happy Coding

This post is licensed under CC BY 4.0 by the author.