Case study: creating a website on DotNest with heavily customized content

Brace yourselves, this is going to be a long one: in this blogpost we're going to demonstrate the process of customizing the content structure of an actual website running on DotNest, currently having ~30 regular users. Seriously, grab some popcorn, you'll need it. Wall of text incoming.


The website is created for the Hungarian World of Warcraft guild "Elemental Gankery". The structure of a guild is partially represented in the roles in the system to provide higher-level permissions to the leaders of guild, who are able present and edit content that is visible to members of the guild. Members have access to view almost any content on the website and the ability to comment on content items, as well as communicate with each other using the forums. Newly registered users are able to submit an application to the guild, but cannot access the content on the site.

Since the site is targeted at users speaking Hungarian, most of the hints and notification texts are written in Hungarian - when used as an example in this blogpost, these will be translated to English.


To be able provide generic permissions for members, two new roles have been added to system and some changes were made to existing ones. The Content Item Permissions module is also heavily used for many of the content items, as you will see later.

The roles and their permissions in the system are as follows:

  • Administrator: the leader of guild (aka Guild Master) and part of the leadership (aka Officers) are given administrative permissions to be able manage the important aspects of the site.
  • Officer: the other part of the leadership and some distinctive members of guild (aka Core Unit) are added to this Role to be able to edit any content on the website.
  • Ganker: regular members with the ability to view almost all of the site's content and communicate through comments and the forums.
  • Authenticated: registered users, who are not (yet) members of guild. They can submit (and see) guild applications and are able to submit comments on them in case they need to answer further questions during the application process. This role is set up in a way that they cannot access content in general, only those that are targeted towards them (using Content Item Permissions).
  • Anonymous: a role that is even more restricted than "Authenticated". Only a small set of public content is visible to them (also using Content Item Permissions).

Public content

The first type of content represented to anonymous users is a menu that contains links to sign in and register. There are some widgets on the home page with useful links related to the game and the guild, as well as some hints for visitors in case they would like to submit an application. The home page is actually a public blog, where the leadership can make announcements when the guild defeats a powerful enemy for the first time in the form of a blog post. All these content is configured using Content Item Permissions.

Restricted content

Submitting an application

Let's see what happens when you register on the website: a new menu item will appear (only visible to registered users) that leads you to the application form as well as another one that leads you to the page where you can see previously submitted applications. The application process is very similar to a job interview where you submit some information about you and your gaming experience in a written form, that may lead to a verbal interview with the leadership who will then decide to accept the application or not.

The application form is driven by the Custom Forms module (altough with the upgrade to Orchard 1.9, we will probably migrate it to Dynamic Forms). It uses a content type called "Guild Application" that has TitlePart, AutoroutePart (hidden using placement) and a bunch of fields (most of them are required) that applicants can use to answer questions about e.g. themselves, their previous gaming experience, motivation to join the guild and some technical stuff. Authenticated users are given permission to submit forms of this type. Upon submitting the form a content item is saved (but not yet published) and the user will see a notification on the site with a message that basically says to wait for further instructions.

Reviewing an application

At this point a workflow will activate to notify the leadership about the submitted application. The workflow definiton is fairly simple: the trigger is the "Form Submitted" event for the application form. It leads to the "Send Email" activity, which (not so surprisingly) sends an email to a predefined set of email addresses (GM and Officers) with instructions to review the application and then publish it if it's valid. A link to the content item is included in the email using the combination of the "Site.BaseUrl" and "Content.EditUrl" tokens.

Upon publishing this content item, it will be displayed among the other applications in a Projection and the applicant recieves a notification about that - let's see how that's set up! Sending the notification also involves a workflow, but this time the trigger is the "Content Published" event for the "Guild Application" content type and the email message contains a display link to the content item. The recipient of the email is of course the applicant, using the "Content.Author" and "Content.Author.Email" tokens. As for the list of applications, the Query we need to create filters content items by their content type (Guild Application), sorts items in a descending order by their creation date and then displays them in an unordered HTML-list. Then we just create a Projection using this Query and set up content permissions so that authenticated users can see it.

Some of the fields' display shapes are overridden using Liquid templates to display an actual link for URLs that were entered using an Input Field. This is simply done by enabling the "Liquid Markup Templates" feature and creating a template with a name matching the field (see the list of conventions), e.g.: "Fields_Input__IncombatUIscreenshot" and its content is:

<p><a href="{{ Model.ContentField.Value }}" target="_blank">Screenshot link</a></p>

At this point the members of the guild and the applicant can discuss further questions in the form of comments on the application and upon coming to a conclusion regarding accepting or declining it, the application is closed. This is simply done by disabling comments on the content item and adding some text at the end of the title, like "DECLINED" or "ACCEPTED" to distinguish it from the open ones.

Member content


Most of the member-only content consists of forums: the first thing to do here is to enable the Forums and Watcher modules. Then the WatchablePart should be added to the content type definition of the Forum and Thread content types. We'll also add ContentPermissionsPart to the Forum content type to able to restrict access to certain forums so they are only visible to the guild's leadership. We use a Taxonomy called "Forum Category" with a few terms to categorize forums, so we can display them in separate Projections - we'll add a TaxonomyField to the Forum content type with this Taxonomy selected. Each of these categories have their own Query and Projection set up, so we can display them separately. When DotNest is upgraded to Orchard 1.9 we'll use a Layout to display all of these Projections on one page.

Each Query is configured to filter content items by content type (Forum) and a specific term of the a ForumCategory Taxonomy. They are ordered by their Title (ascending) and are listed in a grid with some reponsive styling added: col-lg-3 col-md-4 col-sm-6 col-xs-12. Of course each of the Queries have a corresponding Projection with a nice URL, Content Item Permissions set up to restrict access to members (except for the one that is displaying Officer-related forums) and a Content Menu Item in a dropdown menu under a (text-only) Custom Link called "Forums".

Watching Forums and Threads

As mentioned earlier, members are able to use most Forums, create Threads and post replies in them. Since we added the WatchablePart to Forums and Threads, users can subscribe to notifications if there's new content: this is simply done by clicking on the "Watch" link that is displayed for these items. For these notifications to be sent, we'll once again use Workflows: you can check it out the Watcher module's docs for a general example and below are the ones we have on this website specifically to work with Forums.

One of them sends a notification if a new Thread is created - this will be activated if the Forum it's created in is one of those that you are "watching". The starting point of the workflow is the "Content Created" event set up for Thread. When sending an email chained after a "Run for next watcher" activity (in the loop of the users watching the content item) we can use the User-tokens to define the recipient like this: {User.Name} <{User.Email}>. Then the email template contains a link to the Forum where the new Thread is opened: <p>{Content.Author} user opened a new Thread in the <a href="{Site.BaseUrl}{Content.Container.DisplayUrl}" target="_blank">{Content.Container.DisplayText}</a> Forum.</p>.

The other one sends a notification when there's a new Post in one of the watched Threads or Forums (so this will be triggered if you're watching the Forum the Thread is in, even if you're not watching the Thread) with small differences: the starting point is the "Content Created" event for the Post content type and the email template is the following: <p>{Content.Author} user posted a new reply in the <a href="{Site.BaseUrl}{Content.Container.DisplayUrl}" target="_blank">{Content.Container.DisplayText}</a> Thread of the <a href="{Site.BaseUrl}{Content.Container.Container.DisplayUrl}" target="_blank">{Content.Container.Container.DisplayText}</a> Forum</p>.

Displaying YouTube videos

Videos recorded during gaming are for perpetuating great moments (like first kills), so we upload them to YouTube to be able to watch them later. For this purpose we have a content type called "YouTube Video" with TitlePart, AutoroutePart, BodyPart, CommentsPart and an InputField set up to enter a 11-character-long string, which is the ID of a YouTube video (so you don't have to enter a full URL, since it's always the same, except for the ID, of course). Not only that, but this field has an overridden template to automatically display the embedded video with adaptive sizing:

{% if Model.ContentField.Value != empty %}
    <div class="embed-container"><iframe src="//{{ Model.ContentField.Value }}?html5=1&autoplay=0" allowfullscreen="" frameborder="0"></iframe></div>
{% endif %}

Then we set up a Query with content type filtering (for YouTubeVideo) and descending order (by creation date) and a Projection to display them a simple, paged list.

Character roster

Currently we don't have a module providing profile editing functionality (though this may change very soon!), so we have something similar in the meanwhile. We've attached a new content part called GuildMemberPart to the User content type that has a few fields for entering data regarding the players and their characters: a TextField for character name, a single-select EnumerationField for guild rank (this is not connected to the Role), a multi-select EnumerationField to list the days tha player is available and a BooleanField to deselect a checkbox for hiding inactive players. The downside is that these data can only be edited by administrators, since using the built-in functionailities there is no way to display the editor of an actual user (and make sure that only that user can access it).

Like in many cases before, we set up a Query: we're filtering items based on their content type (User) and the value of the BooleanField that was mentioned before. Then we order items by the value of the field for the character name and display them in a grid using a Projection.


This post demostrates pretty well how you can display rich, customized content to your users on DotNest - and we haven't even used all of the available features!

Happy customizing!

No Comments

Add a Comment