Archive for the 'DNN' Category

DNN Liquid Content – Getting the content item id in a visualizer

To get a unique id for a content item, you simply use {{id}}, in the template part of the visualizer editor.

For example:

<article class="news-headline" id="{{id}}">

It’ll come out as something like:

<article class="news-headline" id="aa1a93b2-cca4-4a32-bc0a-2c52a1b28019">

If you need to link to it, e.g. for a Bootstrap modal, use something like this:

<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#{{id}}">

It comes out like:

<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#aa1a93b2-cca4-4a32-bc0a-2c52a1b28019">

The ID is tied to the specific content item, so you can use it across multiple visualizers (e.g. put all the buttons in one visualizer, and all the modals into another one).

This might seem obvious, but I didn’t find it in the documentation.

DNN Liquid Content quick tips

In DNN Evoq, there’s a structured content solution called Liquid Content. The visualizers used to put that content on a DNN web page use the Liquid template language (I believe it uses dotliquid under the hood). Here’s a few things that’ve been useful to me:

If statement with a checkbox value

Sometimes my users want to be able to choose whether a link opens in a new tab. So I added a checkbox to the content type. Now, I expected it’d be as simple as {% if {{imgOpenInNewTab.first}} == 'Yes' %}target="_blank"{% endif %}. Sadly it wasn’t.

After quite a lot of fiddling, I discovered I had to use the first filter, like so:

{% if {{imgOpenInNewTab.first}} == 'Yes' %}target="_blank"{% endif %}

Working with images

When you want to add an image to the template, add one to your content type and then add it to the template, like so: {{ image }}. It’ll output the entire image tag for you.

But what if you want to do something with the img element? Maybe set your own alt text, or specify other HTML attributes on there. This is where the image_url filter comes in. It returns the URL of the image. You’d use it like this:

<img src="{{ image | images_url }}" alt="" />

Working with absolutely massive images

Sometimes the people putting content into the system will upload a 3000px image from somewhere like Unsplash. DNN has the facility to scale images using the DNN Image Handler (and cache them on the server). You’d set the img src to something like /DnnImageHandler.ashx?mode=file&w=700&resizemode=fit&file=path-to-file. How do we use this with Liquid Content? It’s rather tricky, but it can be done, making use of lots of Liquid Template loops, variables and filters:

Firstly, the Image Handler doesn’t work with SVG images, so first we’ll need to figure out if the image is an SVG:

{%- assign file_extension = image | images_url | split: "." | last | split: "?" | first -%}

Then we need to get the server-local path to the image, (i.e. strip the https://example.com/ off the front). This is a bit complicated:

{%- assign local_image_url = image | images_url | remove_first: "http://" | remove_first: "https://" | split: "/" -%}

Then, pass it to the DNN image handler, stripping off the querystring as we go:

<img src="/DnnImageHandler.ashx?mode=file&w=700&resizemode=fit&file={%- for item in local_image_url -%}{%- unless forloop.first -%}/{%- if forloop.last -%}{%- assign not_qs = item | split: "?" -%}{{ not_qs.first }}{%- else -%}{{item}}{%- endif -%}{%- endunless -%}{%- endfor -%}" alt="" />

Finally, sometimes when an image has funny chracters in the filename it ends up as an aspx file, so we can’t do this at all. So all in, we end up with this:

{%- assign file_extension = image | images_url | split: "." | last | split: "?" | first -%}
{%- assign local_image_url = image | images_url | remove_first: "http://" | remove_first: "https://" | split: "/" -%}
{%- if file_extension == "svg" -%}
    <img src="{{ image | images_url }}" alt="" />
{% elsif file_extension == "aspx" %}
    <img src="{{ image | images_url }}" alt="" />
{%- else -%}
    <img src="/DnnImageHandler.ashx?mode=file&w=700&resizemode=fit&file={%- for item in local_image_url -%}{%- unless forloop.first -%}/{%- if forloop.last -%}{%- assign not_qs = item | split: "?" -%}{{ not_qs.first }}{%- else -%}{{item}}{%- endif -%}{%- endunless -%}{%- endfor -%}" alt="" />
{%- endif -%}

Now that might all seem a bit overkill, but if your content editors are prone to uploading 3000px images from Unsplash, this helps deal with the problem.

A DNN shout-out

I’ve started building websites using DNN recently. There’s been quite a learning curve, but there’s some amazing tools and resources which have made life a hell of a lot easier for me. So here’s a shout out:

  • nvQuickSite is a brilliant little tool which makes firing up new installations of DNN a breeze
  • nvQuickTheme (from the same people) is a great place to start when building a new DNN Theme or Skin
  • OpenContent (by Sacha Trauwaen) is a brilliant module for building structured content in DNN. If, like me, you’ve spent a lot of time working with SharePoint, you can think of OpenContent as Content Types, Lists and the Content Query Web Part, but a hell of a lot nicer. As much as I loved XSLT, I don’t miss it using it – I can choose between Handlebars and Razor here, depending on how complex things need to get. I’ve used it for all sorts of stuff – from news feeds to video galleries and “meet the team” modules.
  • Matt Rutledge’s Yo DNN Generator has come in really handy for building new DNN modules from scratch. Same is true of Chris Hammond’s Visual Studio Templates.
  • The DNNConnect group on Facebook is full of really helpful people who know DNN inside out.
  • Aderson Oliviera’s Youtube Channel and Open Friday initiative.

I’m sure there’s more I’ve forgotten. I feel pretty proficient with DNN now, but I couldn’t have got there without the help from all of the people and tools here. Thanks y’all.

Using the DNN Services Framework API with fetch

When building an SPA mdoule in DNN (formerly Dotnetnuke), you’ll likely find yourself using the DNN WebAPI. It turns out the Javascript API for this is very XHR-centric. I wanted to use the fetch API instead.

When using fetch, instead of using beforeSend: setModuleHeaders() you’ll need to build the headers object yourself:

fetch(url, { headers: {
    "ModuleId": your-module-id-here,
    "TabId": your-tab-id-here,
}})

I modified this dotnetnuclear code to look more like this:

let service = {
    path: "PathToMyModule",
    framework: $.ServicesFramework(sbgQuestionnaireModule_Context.ModuleId),
    controller: "MyController"
}
service.baseUrl = service.framework.getServiceRoot(service.path);
// Set the headers
service.headers = {
    "ModuleId": service.framework.getModuleId(),
    "TabId": service.framework.getTabId()
};
// Set the anti-forgery token
if (service.framework.getAntiForgeryValue()) {
    service.headers["RequestVerificationToken"] = service.framework.getAntiForgeryValue();
}
service.loadUrl = service.baseUrl + "/" + service.controller + "/load";
fetch(service.loadUrl, {
    headers: service.headers
})
// etc

(Thanks to Daniel Valadas for his help getting started with this)