Wednesday, 11 May 2011

Real world Orchard CMS – part 2 - creating the theme

As I said in my earlier post, this series is about creating a real world site with Orchard CMS, I’m not covering using Orchard to manage the site, just the technical development parts. All of the code is available on codeplex here : http://orchardsamplesite.codeplex.com/

Preamble
This post is about skinning your first site with your own custom theme, it will cover the basics of how to generate a theme project, open it in visual studio and code up the relevant aspects to make your site look how you want it.

Goal
Our objective is to define and apply a theme for the site as per this mockup;

orchard-kitbag-screenshot

Background reading and preparation
Before you start, you need to get a copy of orchard 1.1 from codeplex, and unzip it to a fresh, empty directory and set it up as per the documentation at http://www.orchardproject.net/docs/Manually-installing-Orchard-zip-file.ashx (follow the section titled “Running the site using visual studio and the visual studio development server”.

After doing that, you should have a working Orchard CMS implementation with the default “theme machine” skin;

Image

Before you start, it’s definitely a good idea to familiarise yourself with how orchard works and the basics of themes and shapes – further reading;

Finally, before beginning you need to have enabled the code generation module as per the following documentation;

Lets get started - codegen the theme scaffolding.
Open a command line and navigate to the bin directory of the site source. Here you will find orchard.exe, a command line tool for managing orchard and where we will generate our scaffolding for our custom theme. Run orchard.exe. Sometimes I get an exception when I try to run the tool, if the same happens to you, just run it again and it should fire up second time round and you will be presented with the orchard shell;

image

Use codegen to generate the boilerplate code for your theme by executing the command;

codegen theme SampleSite /CreateProject:true

Open the project in visual studio
Back in visual studio you can now add the theme project to the orchard solution (Right click the solution name in solution explorer and select Add –> existing project). Find the newly created theme project under orchard/themes/SampleSite/SampleSite.csproj and select it, you should then have the theme project in your solution;

image 

Create the initial code layout
First things first, the main template for the theme will be in the Views folder and will be named layout.cshtml. This will be combined with the default document.cshtml which controls the html/head/body rendering before deferring to your layout.cshtml (this can be overridden in your theme if you feel so inclined, although its rare this would be required).

Our general layout is quite simple, it’s made up of a 960px grid type layout and will expose zones as follows (zones are places where Orchard can push content);

image 

The zones will only be shown if there is some content to show in the zone. As such, the content zone will expand right to fill the side bar zone if there is nothing in there. The layout.cshtml (create it in the views directory) to support this layout is as follows;

   1:  @{
   2:      // Add the site CSS
   3:      Style.Include("site.css");
   4:      
   5:      // Determine which zones are going to be shown
   6:      var displayBasket = (Model.BasketArea != null);
   7:      var displayBodySideBar = (Model.BodySideBar != null);
   8:      var displayBeforeBody = (Model.BeforeBody != null);
   9:      var displayTriPanel = (Model.TriPanelLeft != null) || (Model.TriPanelCenter != null) || (Model.TriPanelRight != null);
  10:      var displayMessages = (Model.Messages != null);
  11:      var displayContent = (Model.Content != null);
  12:      
  13:      // Work out if we need to change the class of the main body area        
  14:      var bodyClasses = displayBodySideBar ? "" : "without-sidebar";
  15:      
  16:      // Adds user sign in, dashboard links etc to the bottom of the page
  17:      WorkContext.Layout.BottomDweller.Add(New.User(), "1");
  18:  }
  19:   
  20:  <div id="layout-header">
  21:      <div id="layout-branding">
  22:          <a href="@Href("~/")" id="layout-branding-home"></a>
  23:          @if( displayBasket )
  24:          {
  25:          <div id="layout-basket-area">@Display(Model.BasketArea)</div>
  26:          }
  27:      </div>
  28:  </div>
  29:   
  30:  <div id="layout-navigation">
  31:      @Display(Model.Navigation)
  32:  </div>
  33:   
  34:  @if( displayMessages )
  35:  {
  36:      <div id="layout-messages" class="zone">
  37:      @Display(Model.Messages)
  38:      </div>
  39:  }
  40:   
  41:  @if (displayBeforeBody)
  42:  {
  43:      <div id="layout-before-body" class="zone">
  44:          <div id="zone-before-body">
  45:              @Display(Model.BeforeBody)
  46:          </div>
  47:      </div>
  48:  }
  49:   
  50:  @if (displayContent || displayBodySideBar)
  51:  {
  52:      <div id="layout-body" class="zone">    
  53:          <div id="zone-body" class="@bodyClasses">
  54:              @Display(Model.Content)
  55:          </div>
  56:   
  57:          @if (displayBodySideBar)
  58:          {
  59:              <div id="zone-body-sidebar">
  60:              @Display(Model.BodySideBar)
  61:              </div>
  62:          }
  63:      </div>     
  64:  }
  65:   
  66:  @if (displayTriPanel)
  67:  {
  68:      <div id="layout-tripanel" class="zone">
  69:          <div id="zone-tripanel-left">
  70:          @Display(Model.TriPanelLeft)
  71:          </div>
  72:          <div id="zone-tripanel-center">
  73:          @Display(Model.TriPanelCenter)
  74:          </div>
  75:          <div id="zone-tripanel-right">
  76:          @Display(Model.TriPanelRight)
  77:          </div>
  78:      </div>
  79:  }
  80:   
  81:  <div id="layout-footer-pad"></div>
  82:  @Display(Model.AfterContent)
  83:  @Display(Model.BottomDweller)

If the template syntax looks strange, go read up about the new Razor view engine in MVC 3, which Orchard uses by default, it’s a much cleaner markup than classic aspx/ascx (although you can still use these if you like). Notice we have two additional zones at the bottom of the layout – AfterContent and BottomDweller – the AfterContent zone is used by a number of modules we will be using to throw javascript into, and so it’s included for this purpose. BottomDweller, you can see we populate this zone with a User shape on line 17, which will render the links to get to the dashboard and sign in etc – I’ve put this in place for convenience, but would probably remove it once I’d finished working on the site.

Define the styles
Next, we need a stylesheet – create “site.css” in the styles folder; (Note you will also need the logo.png file from the source code).

/*  Color Palette
**************************************************************
Top: #848975
Dark: #27320A
BG: #f6fafa
White
Black
Link: #0099ff;
*/
/* Resets
***************************************************************/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    font-weight: inherit;
    font-style: inherit;
    font-size: 100%;
    font-family: inherit;
    vertical-align: baseline;
}
:focus { outline: 0; }
ol, ul { list-style: none; }
table { border-collapse: separate; border-spacing: 0; }
caption, th, td { text-align: left; font-weight: normal; }
blockquote:before, blockquote:after,
q:before, q:after { content: ""; }
blockquote, q { quotes: "" ""; }
header, footer, aside, nav, article { display: block; }

/* Float clears
******************************************************************/
.group:after, .zone:after, .widget-control:after
{
    content: ".";
    display: block;
    height: 0px;
    clear: both;
    visibility: hidden;
}
/* Widgets
***************************************************************/
.widgets {}
.widget h1 { font-size: 1.077em; }
.widget + .widget
{
    margin-top:18px;
}
/* Edit Mode Widgets */
/* These are the edit controls that appear when you're logged-in */
.widget-control { position: relative; border: 1px dotted #5f97af; }
.widget-control .manage-actions { position:absolute; top: 0px; right: 0px; }
.widget-control .manage-actions a { display: block; background-color: #dbdbdb; color: #434343; padding: 3px 6px;  }
.widget-control .manage-actions a:hover { background-color: #434343; color: #fff; text-decoration: none; }

.widget-nav-list{ margin: 0px 0px; padding: 0px 0px; }
.widget-nav{ display: block; background-color: #e9eaeb; padding: 5px 5px; margin-bottom: 3px; }
.widget-nav-active{ background-color: #ff9900; color: White; font-weight: bold; }

/* Content Mode */
.content-control { position: relative; border: 1px dotted #5f97af; }
.content-control .manage-actions { position:absolute; top: 0px; right: 0px; }
.content-control .manage-actions a { display: block; background-color: #dbdbdb; color: #434343; padding: 3px 6px;  }
.content-control .manage-actions a:hover { background-color: #434343; color: #fff; text-decoration: none; }

/* General styling
******************************************************************/
body 
{
    background-color: #f6fafa;

    font-size: 81.3%;    /* Sets 1em = 13px/10pt */
    color: black; 
    font-family: Tahoma, "Helvetica Neue", Arial, Helvetica, sans-serif;
}
h1{ font-size: 2em; }
h1.widget-title{ font-size: 1.1em; font-weight: bold; border-bottom: 1px dotted black; margin-bottom: 8px; padding-bottom: 2px;}
h1.page-title{ font-size: 2em; border-bottom: 1px dotted black; margin-bottom: 8pt; padding-bottom: 2px; }
h2{ font-size: 1.9em; }
h3{ font-size: 1.8em; }
h4{ font-size: 1.5em; }
h5{ font-size: 1.4em; }
h6{ font-size: 1.2em; }

p{ margin: 0 0 1em; line-height: 1.538em; }


a{ color: #0099ff; text-decoration: none; }
a:focus, a:hover{ text-decoration: underline; }

.publishinfo
{
    color: #333;
    font-size: 0.9em;
    margin-bottom: 5px;
}

UL.content-items LI
{
    border-bottom: 1px dotted #333;
    padding-bottom: 10px;
    margin-bottom: 10px;
}
UL.content-items LI.last
{
    border-bottom: none;
    padding-bottom: 0px;
    margin-bottom: 0px;
}

/* Layout
******************************************************************/
#layout-header
{
    background-color: #848975;
    height: 125px;
}
    #layout-branding
    {
        width: 960px;
        height: 125px;
        margin: 0px auto 0px auto;
    }
        #layout-branding-home
        {
            display: block;
            position: relative;
            left: 11px;
            top: 21px;
            width: 235px;
            height: 80px;
            background-image: url(logo.png);
            background-repeat: no-repeat;
        }
        #layout-basket-area
        {
            float: right;
            margin: -30px 20px 0px 0px;
        }

    #layout-navigation
    {
        background-color: #27320A;
        height: 35px;
    }
        #layout-navigation ul 
        {
            display: block;
            padding: 0px;
            margin: 0px auto 0px auto;
            width: 960px;
        }
        #layout-navigation ul li
        {
            display: inline-block;
            padding: 5px 12px;
            margin: 5px 15px;
        }
        #layout-navigation ul li.current 
        {
            background-color: #848975;
            border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.5)            
        }
        #layout-navigation ul a 
        {
            font-weight: bold;
            font-size: 1.0em;
            color: white;
            text-decoration: none;
        }

#layout-body, #layout-before-body, #layout-tripanel, #layout-messages
{
    width: 920px;    /* 960 with 20px padding */
    margin: 0px auto 0px auto;
    background-color: White;
    padding: 20px 20px 0px 20px;
}
#layout-messages{ width: 940px; padding: 10px 10px 0px 10px; }
.message, .validation-summary-errors { margin:10px 0 4px 0; padding:4px; }
.messages a { font-weight:bold; }
.message-Information { background:#e6f1c9; /* green */ border:1px solid #cfe493; color:#062232; }
.message-Warning { background:#fdf5bc; /* yellow */ border:1px solid #ffea9b; }
.critical.message, .validation-summary-errors, .message-Error { background:#e68585; /* red */ border:1px solid #990808; color:#fff; }


#layout-footer-pad
{
    width: 960px;
    height: 20px;
    background-color: white;
    margin: 0px auto 0px auto;
}

#zone-before-body
{
    width: 920px;
}

#zone-body /* Default body zone accommodates a right hand 290px zone */
{
    float: left;
    width: 610px;
}
#zone-body.without-sidebar /* Without the sidebar, body fills page */
{
    width: 920px;
}
#zone-body-sidebar
{
    float: right;
    width: 290px;
    margin-left: 20px;
}

#zone-tripanel-left
{
    float: left;
    width: 290px;
    margin-right: 20px;
}
#zone-tripanel-center
{
    float: left;
    width: 300px;
    margin-right: 20px;
}
#zone-tripanel-right
{
    float: left;
    width: 290px;
}

This should all be fairly self explanatory.

Update the theme.txt file
The theme.txt exposes information about this theme to orchard. Open it up and change it as follows;

Name: Sample Site
Author: You
Website: http://www.deepcode.co.uk
Description: My sample site theme
Version: 1.0
Zones: Content,BodySideBar,BeforeBody,TriPanelLeft,TriPanelCenter,TriPanelRight,BasketArea

The format should be quite self explanatory, the Zones section is the only thing that needs some explanation – basically this tells orchard what zones are in use in this layout. This will integrate with the widget system etc to allow you to drop content into these various zones.

Enable the theme
Open the orchard dashboard and navigate to “Themes”. What you should find now is you have a new theme available;

image

Click set current and navigate back to your site and voila, you should have your new theme in all it’s glory;

image

Fill it with some standard content
Go ahead and add some content to your site – add an about page (and add it to the menu), a blog (and a couple of posts) and use the HTML widget to add some content to the various zones on the homepage (there is a widget layer specifically for the homepage) – maybe add content as placeholders for the twitter feed we will add to the homepage and add a recent blog post widget to the central tri panel zone. You should then have something like this;

image

Next
In the next post, I’ll look at building the twitter widget for the homepage.

12 comments:

  1. Hello,

    Im trying to follow along with your tutorial and run into a problem when trying to add a layout page. For whatever reason Razor pages are not an option when trying to add a new file to the create theme project. any idea why?

    ReplyDelete
  2. You should be able to just add new item -> and select text file/html/aspx etc and just give it an extension of .cshtml, then empty any contents and write your razor code.

    Alternatively use the tooling?, try right click, add view and tell it to create a razor page.

    But importantly, you don't need to use the tooling to create a razor file, just create a file with a .cshtml extension.....

    HTH

    ReplyDelete
  3. change Model.TriPanelLeft
    to Model.TripelFirst

    change Model.TriPanelCenter
    to Model.TripelSecond

    change Model.TriPanelRight
    to Model.TripelThird

    ReplyDelete
  4. Why would I want to do that? Feel free to name your zones whatever you like. :)

    ReplyDelete
  5. You would do that because later you might change your mind about the CSS and it may no longer make sense for them to be "left", "center" and "right"; for example you might stack them vertically in a different layout.
    See CSS Zen Garden for more...
    Nice start though. I look forward to reading the rest :)

    ReplyDelete
  6. So far, this series is a lifesaver for me. I rarely comment, but want you to know how helpful this is.

    ReplyDelete
  7. Thank you SO much. I'm now 5 days into working with Orchard and you have tipped me the final piece I was missing in implementing zones in my theme!

    ReplyDelete
  8. Thanks it's very helpful for newbie like me....

    ReplyDelete
  9. I must be missing something really obvious -- Is there any way I can add a link from one page to another page? Like click one button and pick an already created page and link to that.

    I've been Googling this now for more than an hour and I can't find anything. I don't want to use the normal link button in the default editor because it's too error prone to have to look up the URL of a page I previously created.

    I feel crazy that there seems to be no easy way to insert a link to previous content.

    ReplyDelete
  10. I did this walkthrough, however, my layout looks nothing like the screen shot, and when I try to add a widget to the page, nothing shows up. Is there a way to do this and show the layout of the page when the widget section like the other themes do (the ones you download from the site)

    ReplyDelete
  11. Fantastic. I followed exactly your steps and at the end I managed to add a widget (JWPlayer widget) to display a YouTube video on one of the zones. Very helpful. Thank you so much!

    ReplyDelete
  12. Olá Tony Johnson,
    Tenho um sitem orchard, gostaria de te um tema personalizado, vocÊ pode me ajudar. tenho o modelo do site que quero.
    Aguardo seu contato. grato sandro

    ReplyDelete