O'Reilly logo

Orchard CMS: Up and Running by John Zablocki

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Creating Themes

In the previous chapter, we saw that it’s possible to customize the look and feel of an Orchard site by creating alternate templates for pieces of content. While this feature does provide some flexibility over how content is rendered on pages, it doesn’t easily allow for wholesale changes to the way a site looks.

In order to achieve this broader goal, we’ll look at creating our own themes. We’ve already taken a brief tour of the default theme—“TheThemeMachine”—that is part of a standard Orchard installation. In this chapter, we’re going to take a look inside of that theme to understand how to create our own.

At this point, our Orchard development has been limited to editing a couple of view files. We wrote some C# code in those Razor templates and learned a little about how the content is modeled and displayed in a view. However, we haven’t really been exposed to the Orchard development experience. To gain this exposure, we first need to learn about a couple of tools that make Orchard development easier.

The Orchard Command-Line Interface

It probably seems strange to consider using a command-line interface (CLI) with a web-based CMS. However, the Orchard CLI offers quick access to many common admin functions without the need to open up a browser and navigate to different property pages. Assuming you’ve been working in the Orchard solution, the CLI has been ready for use since you first compiled your app and set up your recipe.

Getting Help

To get started with Orchard’s CLI, open up a command window (or PowerShell) and navigate to the bin directory of your Orchard site. There you’ll find file Orchard.exe. Execute that file. After a few moments, you’ll see an Orchard prompt.

PS C:\dev\Orchard> cd .\src\Orchard.Web\bin
PS C:\dev\Orchard\src\Orchard.Web\bin> .\Orchard.exe
Intializing Orchard Session. (This might take a few seconds...)
Type “?” for help, “exit” to exit, cls to clear screen
orchard>

There are a number of commands you could execute. To get CLI help, there are two commands you should know. The first simply tells you how to perform tasks like quitting, clearing the screen, and getting more help:

orchard> help

One of the items listed when you execute the help command explains how to get help for the commands that allow you to work with your Orchard site:

orchard> help commands

Executing help commands gives you a list of CLI commands ranging from user creation to page creation. You’ll also see how to go one level deeper for command help:

orchard> help page create

To test your session, enter the command that simply lists the site cultures, which are used for the internationalization of your site:

orchard> cultures list
Listing Cultures:
en-US

Code Generation Tools

If you ran the help commands, you probably saw that the Orchard CLI has commands for many of the common tasks you’d normally perform in the admin pages, including enabling and disabling features. We’re going to use this command to enable a feature that will give us additional command line tools:

orchard> feature enable Orchard.CodeGeneration
Enabling features Orchard.CodeGeneration
Code Generation was enabled

Once the code generation tools are enabled, they will provide useful shortcuts for creating and managing themes and modules. Orchard.CodeGeneration is an example of the Orchard’s extensibility. In fact, you can build your own command-line tools for Orchard. It’s as simple as creating a class that extends the base class DefaultOrchardCommandHandler.

Note

The code generation module is not installed by default with Orchard, but it was found in the source when you downloaded the zip or cloned the repository. If you are not working with the solution, you might need to install this module from the Orchard Gallery.

Code Generating a Theme

In the previous chapter, we saw that a theme is a collection of files contained in a directory in the Themes project in the Orchard solution. There’s nothing special about a theme other than it follows a set of conventions and is stored in the Themes directory under the Orchard.Web project.

To start building a new theme, you could simply copy the directory structure for the “TheThemeMachine” theme and paste it into a new sibling directory. We’ll instead use the command line code generation options to create our theme. Later we’ll learn how these tools can help us avoid starting from scratch.

Note

The Orchard solution is organized using solution folders, so it appears that the Themes project lives outside of the web project’s directory structure. However, the Themes project and theme files are actually nested in the filesystem under the Orchard.Web directory.

Return to the CLI and your Orchard prompt. We’re going to create a new theme named “DaisysTheme.” There are three options we’ll want to consider before we create our new theme:

CreateProject

Whether to create new project for this theme. The default is false.

IncludeInSolution

Include this theme in the solution. The default is true.

BasedOn

Inherit default templates from an existing theme.

For our new theme, we’re neither going to inherit from an existing theme nor create a new project. We’ll simply run the theme code generation without any arguments:

orchard> codegen theme DaisysTheme
Creating Theme DaisysTheme
Theme DaisysTheme created successfully

Return to Visual Studio where you’ll be prompted to reload the solution. The codegen utility modified the Themes project and forced a reload of the solution. After reloading, we can start to inspect the anatomy of our new theme.

The Structure of a Theme

The structure might look somewhat familiar to an experienced ASP.NET MVC developer. As is the case with the standard Visual Studio ASP.NET MVC project template, there are directories for Scripts, Styles, and Views. The purpose of each of these and the other directories is as follows:

Scripts

Directory for JavaScript files

Styles

Directory for CSS files

Views

Directory for Razor template (*.cshtml) files

Content

Directory for images and other static content

Zones

Directory for templates that wrap zones

Additionally, the generated theme template includes a Placement.info file. Recall that this is the XML file that instructs Orchard as to how or whether to layout fields, parts, and items. There are also numerous Web.config files that are used by ASP.NET to set up some configuration plumbing for ASP.NET and ASP.NET MVC. There are two other files worth noting, namely Theme.txt and Theme.png. Both of these files are used by Orchard to describe your theme to the admin pages.

Back in the admin dashboard, select ThemesInstalled. You’ll see three themes listed (Figure 4-1). The current theme will be the default “TheThemeMachine.” There’s a second theme called “The Journalist” and our new theme named “DaisysTheme.” However, some things don’t look quite right with our new theme.

Installed themes

Figure 4-1. Installed themes

Notice that the name is “DaisysTheme” without a space. Authorship is attributed to “The Orchard Team.” The version is already set to 1.0 and the description and URL are also wrong. As we’ll see shortly, the preview image also fails to accurately represent our new theme at this stage. You might have guessed that Orchard is using Theme.txt and Theme.png to determine what values to plug into this admin page.

Before returning to Visual Studio to look at these files, let’s first make our new template the current template by clicking “Set Current.” Once the switch is complete, refresh your site. What you’ll see is that your site is suddenly without any discernible structure or styling (Figure 4-2). Also notice that our alternates are gone. We’ll add some of those pieces before we modify the metadata that’s used by the Dashboard.

Note

The purpose of this chapter is to introduce theme development. As I am not a designer, I am intentionally keeping the theme we will build simple, including all graphical treatments, styles, and HTML.

A new theme without any styles or structure

Figure 4-2. A new theme without any styles or structure

Default Content Templates

If you view the source for the home page, you’ll notice that there is a full HTML document wrapping the content. This might seem strange, since we haven’t actually defined any master page or layout files. These default template files do exist, though. Orchard includes them in the Orchard.Core project.

If you expand that project in Visual Studio’s Solution Explorer, you’ll see a Views directory nested under a Shapes directory. The view files inside this directory are used by Orchard as safe defaults for displaying content when no suitable template alternates are found in a theme for a given piece of content.

Open the files Document.cshtml and Layout.cshtml to see the HTML that is wrapping the content in our home page. Document.cshtml defines the basic structure of an HTML document, including the html, head, title, and body tags. Layout.cshtml defines a very basic arrangement of div elements on the page. Notice that the template for the title of the page is actually found in the Parts.Title.cshtml file in the Views directory under Title in the Orchard.Core project.

As is hopefully now clear, Orchard uses a hierarchy of templates when determining how to render content. In the previous chapter, when we defined alternate templates, we simply added files to the current theme and those files took precedence over those found in the default templates directories. When creating a theme or module (in the next chapter), you could choose to inherit a template or override it at any level (item, type, part, or field).

Working with Views

In Chapter 3, we created alternate templates in the form of Razor template files. However, we didn’t explore these templates in any detail. Before we build a theme and continue our Razor efforts, it’s worth a quick look at some of the functionality that Orchard adds to the standard base class used by Razor views. For more on Razor, see Programming Razor by Jess Chadwick (O’Reilly).

Orchard extends the System.Web.Mvc.WebViewPage base class used by Razor views with its own WebViewPage. We’ve already seen a method from this subclass. When we created our zones, we used its Display method. There are other useful helper methods in this base class.

The Style property of WebViewPage has an Include method that will render a link tag that points to the filename provided as its argument. It assumes that your file is in the Styles directory of your theme. Similarly, there is a Script property with methods related to including JavaScript blocks and files.

Both the Script and Style properties are of type ResourceRegister, which provides an additional method named Require. Given a resource defined in a manifest class (we’ll learn more about this class in Creating Widgets), Require will find a script or style and ensure that it’s included only once.

WebViewPage also includes some convenience methods, such as the null and whitespace checking HasText method. Orchard also provides a StringExtensions class in the Orchard.Utilities.Extensions namespace. This class has methods such as CamelFriendly, Ellipsize, and HtmlClassify, all of which may be useful in views.

Layouts

For our theme, we’ll consider this document wrapper sufficient and won’t override it with a new Document.cshtml. We’ll instead start our theme with a new layout file. Add a new Razor file named Layout.cshtml to the Views folder in the “DaisysTheme” theme. If you save this file with no content (an empty HTML file) and refresh any page on your site, you’ll see that the content has disappeared.

By including a Layout.cshtml file in our template, we’ve instructed Orchard not to use its default layout template. Instead we’ve instructed Orchard to use this new, empty file. Notice though that the page title still appears. As mentioned, the title was included in Document.cshtml, which we chose not to override.

Note

If you’ve viewed the source of any of your site’s pages as we’ve been making changes to the theme, you may have noticed a great deal of JavaScript content. The script is from the Shape Tracing module that we enabled in the previous chapter. It would not appear on production sites unless you left that module enabled.

We’ll add some code to Layout.cshtml to get some content back on our site. Start by adding the following snippet:

<div id="main">
@if (Model.Content != null) {
    @Display(Model.Content)
}
</div>

This simple chunk of Razor code demonstrates a couple of key patterns for building layouts in Orchard. We’re going to explore this pattern in more detail later in this chapter. For now, recognize that we null-check a property on our view’s Model and call Display on that property when it’s not null.

Recall that the data bound to our views are dynamic types known as shapes. The Model property of our view is a representation of these shapes. When you call the Display method, it’s going to check the runtime type of the argument you provided. In this case, the type will contain metadata indicating that it’s a “zone,” which will allow the Display method to properly render content in that given zone.

Note

The Display method is actually a read-only dynamic property that is defined in Orchard’s WebViewPage, which is the base page type for Razor views in Orchard. This property returns an instance of a callable dynamic object, which is why the method-call syntax works.

Saving the layout and refreshing the home page, we now see that the main content has been added. However, we’ve lost our navigation and other zones. Let’s add the navigation by placing the block of code that follows above the content snippet we previously entered:

@if (Model.Navigation != null) {
<div id="layout-navigation" class="group">
    @Display(Model.Navigation)
</div>
}

Once navigation has been added, we can now refresh the home page and click through each of the pages to see that content is in fact displaying on each page. While most pages look like their “TheThemeMachine” equivalents without any CSS, the home page is noticeably missing the widgets we’d previously added.

If we want the Bing Maps widget to show up in our new theme, we need to include a zone named TripelThird and a block of Razor/HTML as follows:

@if (Model.TripelThird != null) {
    <div id="tripel-third">
        @Display(Model.TripelThird)
    </div>
}

Zones and Layers

At this point we can see that zones are simply sections defined in our layout templates. We use the Display method of the view to create them. Adding widgets to zones in the admin tool creates the relationship that allows the null-checks that surround the Display calls to evaluate properly. Had we not added a widget to the zone TripelThird, then Model.TripelThird would evaluate to null.

Let’s take a quick detour from Daisy’s Theme to explore zones a little deeper. Start by opening up Theme.txt in our theme’s root directory. The “Zones” entry in this file is used by Orchard to display the list of zones that appear when you click “Widgets” on the admin menu. That list is currently populated by zones defined in other installed themes and Orchard will tell you as much when you visit that page.

Add a “Zones” section to your Theme.txt named “MoreContent”:

Zones: MoreContent

Next return to Layout.cshtml and add a new zone:

@if (Model.MoreContent != null) {
    <div id="more-content">
        @Display(Model.MoreContent)
    </div>
}

If you refresh the “Widgets” admin page, you’ll now see a zone named “MoreContent” above the zones defined in “TheThemeMachine.” Click AddHtml Widget, add some content, and save. Next, refresh the home page (or any other page). You’ll now see that the zone is displaying on each page (Figure 4-5).

The MoreContent zone

Figure 4-3. The MoreContent zone

Let’s limit this new widget so that it appears only on the home page. Click on the “More Daisy’s Content” link (the name of the HTML widget) listed with the “MoreContent” zone. On the property page for that widget, choose the layer named “TheHomepage” and click Save. Click through to each of the pages to see that the layer rule has enabled this zone only for the home page.

Alternate Templates

As you navigate around the site, you’ll notice that we’ve lost the customization built in Chapter 3 for rendering bios and events. If we want to get these templates back, all we need to do is add those Razor files into our new template.

If you move or copy Content-Bio.Summary.cshtml to the Views directory of our “DaisysTheme” theme and refresh the bio page, you’ll see the listed bios are displaying content as they were previously. Of course, without any CSS in our theme you’ll notice that the rendering lacks any style (Figure 4-4).

An alternate template in the DaisysTheme theme

Figure 4-4. An alternate template in the DaisysTheme theme

Theme Inheritance

At this point, our theme isn’t particularly stylish or interesting. Our layout is pretty limiting as well. What we really want is some HTML that’s easily styled by a skilled designer. Fortunately, the work for that has already been done.

The theme “TheThemeMachine” defines a very flexible layout file. We could simply copy that into our theme, but instead we’re going to inherit it into our theme. In “DaisysTheme” open Theme.txt and add the line that follows. Then delete Layout.cshtml (the one we created):

BaseTheme: TheThemeMachine

After you refresh the home page, you’ll see that our site has returned to its “TheThemeMachine” roots. However, we obviously want to customize our look and feel a bit. To deviate from the inherited theme, we need to override the default styles found in “TheThemeMachine.”

DaisysTheme inheriting layout from TheThemeMachine

Figure 4-5. DaisysTheme inheriting layout from TheThemeMachine

Create a new file Site.css in the Styles directory of the “DaisysTheme” theme. After you create the empty stylesheet, you’ll see after refreshing your site that we’ve again lost our styling, but maintained our layout and alternate templates (Figure 4-5).

Unfortunately, there’s no way for our theme to inherit both the layout and stylesheet from the “TheThemeMachine” theme. Layout.cshtml explicitly includes only a single stylesheet named Site.css. If we want to inherit the entire layout file and customize the style, we have to copy the contents of Site.css from the Styles directory of “TheThemeMachine” into our new file. Otherwise, we have to modify the layout file to look for an additional stylesheet.

Basic Styling

Copy the stylesheet content over into our new theme (Site.css) and save the stylesheet file. Refresh the site to see that we’re now back to the “TheThemeMachine.” Again, we’ll leave the design lessons for the designers, but we’ll modify some of the basic UI to make our theme a little more unique.

Since we’re designing a site for a rock band, we’ll change the background color to a blackish color. Locate the body selector in the stylesheet and we’ll go from a light theme to dark simply by changing the background color:

body {
  line-height: 1;
  font-size: 81.3%;
  color: #434343;
  background: #303030;
  font-family: Tahoma, "Helvetica Neue", Arial, Helvetica, sans-serif;
}

Of course, it’s a bit hard to read the gray text on the dark-gray background. So let’s lighten up our content areas. We could go into the individual page sections and set each to have a white background, but there’s an easier way. We can take advantage of the fact that the layout from which we’re inheriting wraps various groups of zones in div elements with a class name of “group”:

.group {
    background: #fff;
}

Let’s also update the font that’s used for the header of the site. By default it uses a font named “Lobster.” You’re probably thinking that you don’t have “Lobster” installed, yet you’ve somehow been seeing the correct cursive font on the header. Orchard assumes modern web standards by default, so our theme is able to make use of the @font-face directive in CSS3. More specifically, it uses the Google Web Fonts API:

Style.Include("http://fonts.googleapis.com/css?family=Lobster");

If we want to change this font, we have a few options. We’re again faced with the dilemma of whether to modify or copy Layout.cshtml in “TheThemeMachine” to include our desired change. Since we’re just changing styles, we’re going to keep the layout in place and take a different approach.

We’ll simply import a new web font in our stylesheet and then set the branding element’s font-family to our new font. Start by adding a new @import directive to the top of our stylesheet:

@import url(http://fonts.googleapis.com/css?family=Frijole);

In our template, the header text is rendered in an h1 element named “branding.” We’ll simply set style for that element to use our newly imported font:

#branding a
{
    text-decoration:none;
    color: #434343;
    font-family: 'Frijole';
}

Styling Projections

Next we’re going to modify the event listing so that the event titles have a background color and text that’s in all caps. We could write our CSS selector expression to affect all a tags that follow h1 tags as that’s the way events are rendered, but such a selector would not be limited to events matching that pattern. Instead we’re going to inject a class name into each event row.

In the admin dashboard, navigate to “Queries” and select the row for “All Events.” Click to edit the “1 columns grid” that we created previously. In the section with the heading “Html properties,” enter a value “event-row” under the “Row class” and save. We could also have chosen from predefined dynamic expressions (in the drop-down menu for that field), but a static class is sufficient for our purposes.

After you save the new row class, add a new CSS rule to affect a elements that follow h1 elements that follow a tr element with a class name of “event-row.” We’ll style the anchors to be displayed as “block” so that we have equal length backgrounds:

tr.event-row h1 a {
    background-color:#BACEFF;
    padding:3px;
    color:#000;
    width:300px;
    display:block;
}

Figure 4-6 shows the template with our new styling.

A styled projection

Figure 4-6. A styled projection

Shape Wrapping

We’re going to add another template to our theme, but first we have to bring back the rest of our content customization. Copy or move Content-Bio.cshtml, Parts.Common.Body-11.cshtml, and Placement.info from “TheThemeMachine” to “DaisysTheme.”

After moving those files, add a new template named NewsAndNotes.Wrapper.cshtml. Unlike our other templates, this one will surround its target with HTML and won’t actually modify the shape template itself. To call attention to band activity, the code for this wrapper will simply add a div element with a yellowish background to our “News and Notes” HTML widget:

<div style="background:#FFE8A5;padding:2px;">
    @Model.Html
</div>

An additional step is required for this wrapper to be used on our site. We need to update Placement.info to instruct Orchard to use this template. The match constraint will cause this rule to apply to the HTML widget on our home page:

<Match ContentType="Widget" Path="~/">
    <Place Parts_Common_Body="Content:5;Wrapper=NewsAndNotes_Wrapper" />
</Match>

This scenario is admittedly slightly contrived, as we could have used an alternate template for our zone to accomplish the same thing. However, it does illustrate an additional layer of customization available to theme designers.

Theme Metadata

We’ll consider our theme sufficiently styled at this point (at least by developer standards). Now we’re ready to update the metadata used by the admin tool. Return to Visual Studio and open Theme.txt in the root of the theme. Most of the values are pretty obvious; Name, Author, Website, Description, and Version are included by default and shouldn’t merit any further description. If you personalize these values and return to ThemesInstalled in the Dashboard, you’ll see these updated values.

Theme Previews

There are two final files you need to know about when developing themes: Theme.png and ThemeZonePreview.png. These files both live at the root of a theme. The former is typically a screen-grab of your theme’s homepage that will be used in the Dashboard and the gallery to provide a preview of your theme. The latter is an image used on the Widget admin page to provide a preview of where zones are conceptually placed in layout files.

Daisy’s Theme with updated metadata

Figure 4-7. Daisy’s Theme with updated metadata

Theme Credits

The last update we’ll want to make is to modify the chunk of HTML on the bottom of the page that gives credit to “TheThemeMachine” as this site’s theme. While that statement is partially true, we’ll instead claim credit for our “Daisy’sTheme” theme. We’ll need to override a file that’s in “TheThemeMachine” named BadgeOfHonor.cshtml. Copy it from the Views directory of “TheThemeMachine” into our theme’s Views directory. Modify the content so that the span with the “copyright” class has the content as follows:

<span class="copyright">@T("&#169; Daisy's Theme 2012.")</span>

Summary

In our exploration of developing themes, we’ve seen that we don’t have to start from scratch when developing our site’s look and feel. In fact, this is a common way to develop themes. The “TheThemeMachine” theme provides a flexible, barebones layout that could easily be styled by a skilled designer.

There is little reason to create new layout files and complex zone schemes when most of what you need may be found in this theme. Simply inherit from it and create a new stylesheet with new graphical treatments. Of course, your needs may require that you copy the entire “TheThemeMachine” theme to get started. Moreover, the same approach we took to modifying “TheThemeMachine” applies to any theme you install into Orchard (theme license permitting).

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required