Oct
10
Creating a Mephisto Theme using Liquid


Posted: 10th October 2007
Tags: , , , ,
Posted in Ruby on Rails
Comments: 11 Comments »

Mephisto is an excellent blogging platform written in Ruby on Rails by Rick Olsen and Justin Palmer both of whom are behind the excellent Lighthouse Issue Tracking Software . This article / tutorial takes you step by step through the process of creating a custom theme / templates.

To an extent I am going to assume you are already familiar with Ruby and the Rails Web Development Framework, but where possible I hope to keep things as simple as possible.

First of all one of the best ways to go about learning how to create a custom template / theme is to download one of the existing themes you like from the Mephisto Themes Gallery and look inside the .zip file and see how these template authors have gone about implementing their themes.

To get started you will need a copy of Mephisto to test your themes against. You can easily checkout the latest version from subversion. Follow the instruction from the Mephisto Site . Mephisto themes are stored under the /themes/ directory in your Rails application root. If you have a default single host installation, you need to create a new empty folder under /themes/site1/ . You can call the folder whatever you like, this is where we will create our theme.

Mephisto themes are made up of Liquid template files (basically html files with extra markup tags to inject content into your template), images used by your template as well as CSS stylesheets and Javascript files.

Understanding liquid files

Liquid files were created by the developers at Shopify so that their users could customize their shops. These templates work exactly as Rails .rhtml files do however they are safe in the sense that if a variable is nil or undefined they don’t throw an exception or server error, they simply do not generate any content. For non Rails developers .rhtml files store the html for pages and have special <% > tags where Ruby code can be entered and <= > tags where we can output to the page. Liquid files are pretty much the same, however instead of < > tags we have { } and instead of <= %> we have {{ }} tags.

More information can be found on Shopify’s Site and I recommend you read through this, in addition to this article.

Mephisto provides a variety of variable that can be used not only to put content on the page, but also to generate the navigation links, to link between pages. For example in the template that is used to display an article such as this, we have a {{ }} tags which specify to output the article body text like this {{ article.body }}.

Iteration

If the item is an array we can loop through each element using the following:

  1. {% for blog_section in site.blog_sections }
  2. {{ blog_section.name }}
  3. { endfor %}

Conditions

We can also test the values in these Liquid variables and conditionally display content:

  1. {% if section.is_home }
  2. Welcome to the site…
  3. { endif %}

Variable Reference

Before we get into creating templates there are two places to look for the Liquid variables available in Mephisto, directly on Mephisto’s Site and also on Mephisto’s old Wiki

There are various examples on how to do things like formatting dates etc., especially in the latter link above.

Structure of Mephisto

In addition to being simply a blog, Mephisto provides a Content Management System, this allows you to create content pages, such as an about page or portfolio (At the time of writing, something I am still meaning to do ;) )

When we design a theme, we need to bear these different types of content (blog / pages) in mind.

Required Liquid Files

To make up a theme, Mephisto looks for a minimum of the following files, which need to be stored in a folder called ‘templates’ within the folder you created above under /themes/site1/. You need to be careful the folder is spelt exactly as written here as this is where Mephisto will look.

layout.liquid

In addition to the above templates, we have an overall template which provides content for every page called layout.liquid where we can put our main look and feel. This file is located, not in ‘templates’ as above, but in a subfolder (again within out theme) called ‘layouts’.

Creating a layout template (layout.liquid)

As mentioned above, this file is located within the layouts folder in our theme folder. This file is used no matter where we are in the blog so we can use it to put any content that appears on every page, for example the header with logos and in my case my search form, our side bars and footer. This page is also the only page that holds the html head and body tags, so at a minimum we need:

  1. <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
  2. <html>
  3. <head>
  4. <title>My Blog</title>
  5. </head>
  6. <body>
  7. {{ content_for_layout }}
  8. </body>
  9. </html>

Head Section

Instead of having a static page title, we can dynamically insert the site title or if we are looking at a blog article, the article’s title is displayed. This not only gives the users a better experience but also shows as the title for search engine listings and in stats services such as Feedburner

  1. <title>{{ site.title }} {% if article } – {{ article.title }} { else } {{ site.subtitle }} { endif %}</title>

Above we are displaying the site title, and if we are viewing an article, the article object is available, so we can show the title or we show the site subtitle, as set in the Admin Settings.

We also want to make sure our RSS feed to be auto discoverable to browsers etc, so we want to add the following to our head section:

  1. {{ head.feed }}
  2. <! If you are using feed burner, set it up with your RSS feed http://yourdomain/feed then use the feedburner address >
  3. <link href=http://feeds.feedburner.com/miletbaker title=Jon Milet Baker’s Blog type=application/rss+xml rel=alternate/>

We also want to reference any external Javascript or Stylesheet files by using the following tags:

  1. {{ ‘stylesheetfilename’ | stylesheet }}
  2. {{ ‘javascriptfilename’ | javascript }}

Body Section

In layout.liquid we want to create our navigation links to access the different page or blog sections. I generally create navigation using unordered list tags , styling them using css. I can then generate the list items I need using a liquid iteration as below:

  1. <ul>
  2. {% for section in site.page_sections }
  3. <li><a href={{ section.url }}>{{ section.name }}</a></li>
  4. { endfor %}
  5. </ul>

The above, creates links to our page sections, we can also create links to the blog sections by changing line 2 to:

  1. {% for section in site.blog_sections %}

We also likely want to add a search box to every page, we want to add the following:

  1. <form method=get action=/search>
  2. <input type=text class=search value= name=q id=q size=15/>
  3. </form>

As we would likely have the side bar defined in this file we would need to use the technique we used to conditionally display the title, for conditionally displaying content in the side bar. One example for this is displaying related articles to the current article. My related articles technique lists other articles tagged with the same tags. We can do this as follows:

  1. {% if article }
  2. {{ article.tags | tagged_articles | assign_to: ‘rel_articles’}}
  3. { for rel_article in rel_articles limit: 5 }
  4. <a href={{rel_article.url}}>{{ rel_article.title }}
  5. { endfor }
  6. { endif %}

The above code only shows when an article is being displayed, we pass all the tags for the current article and send it to a Mephisto function which returns a collection of articles, which we in turn store in a variable called rel_articles.

In addition to showing related articles we can also show our archive links using the following (in this case we only want this to display in a section):

  1. {% for month in section.months }
  2. {{ section | monthly_articles: month | size | assign_to: ‘articles_count’ }}
  3. { if articles_count > 0 }
  4. <a href={{ section.path }}/{{ section.archive_path }}/{{ month | strftime: Y/m }}>{{ month | strftime: “%B %Y” }} ( {{ articles_count }} )</a>
  5. { endif }
  6. { endfor %}

You may also like to add a list of tags here. My site uses the Mephisto Tag Cloud plugin by Hank to generate different sized tag links based on their use, You could use the Technorati Widget or simply list all the site tags using:

  1. {{ site | linked_tag_list }}

Now we have a basic layout template that contains our common page content, we can get on with the individual templates.

Section template (section.liquid)

The sectiom template as mentioned before acts as the index or contents page for your blog items for each blog section, including the home section. Here you have access to not only the site level variable, but also the Section variables.

  1. {% if articles.size > 0 }
  2. <h1>Articles</h1>
  3. { for article in articles }
  4. <! Within this loop we have access to the article variables. We will need to display each article and some useful properties
  5. of the article variable are shown below:—>
  6. {{ article | link_to_article }}
  7. {{ article.published_at | date: ‘%b %d’ }}
  8. {{ article.author.login }}
  9. {{ article | linked_tag_list }}
  10. <a href={{ article.url }}#comments>{{ article.comments_count | pluralize: ‘comment’ }}</a>
  11. {{ article.body | textilize }}
  12. { endfor }
  13. { else }
  14. <p>No articles found.</p>
  15. { endif %}

Line 10 allows us to display the number of comments with a link to the part of the page where our comments are located, note the use of pluralize that automatically pluralizes the work comment, based on the number of comments, i.e. “1 comment” or “2 comments”.

Also as this is our contents page and we may have long articles rather than use the {{ article.body | textilize }} at line 11, Mephisto provides us with the functionality to write an excerpt. We can write a conditional statement that outputs the articles excerpt (if one exists) or the artilces body for shorter articles. This then requires you to Add an excerpt when writing longer articles, for example this article has all the text up to the ‘Understanding liquid files’ heading in the excerpt. The rest of the text is in the body.

We can then replace {{article.body | textilize }} with :

  1. {% if article.excerpt.size > 0 }
  2. {{ article.excerpt | textilize }}
  3. <a href={{ article.url }} >More…</a><br/><br/>
  4. { else }
  5. {{ article.body | textilize }}
  6. { endif %}

Note: I have found that the Mephisto feed outputs the article.excerpt followed by the article.body for each article. So I have found the best way to handle this is to use the excerpt to create a divider for long articles and not have the text in the excerpt repeated in the article. However it is up to you how you implement this, you could output the article excerpt for article in quotes at the top of your article, literally as an excerpt as an excerpt of the body text.

Single template (single.liquid)

The single.layout template is used to display a particular blog item. It is also used by default to display page items.

On this page we have access to the variables in the article object and want to have the following output to the page:

  1. <h1>{{ article.title }}</h1>
  2. <p>Posted: {{ article.published_at | date: ‘%d %B %Y’ }}, by {{ article.author.login }}<br/>
  3. Tags: {{ article | linked_tag_list }}<br/>
  4. Comments: <a href={{ article.url }}#comments>{{ article.comments_count | pluralize: ‘comment’ }}</a></p>
  5. {{ article.excerpt | textilize }}
  6. {{ article.body | textilize }}

Above we list the article title, when it was published and by who along with the tags. As I am using the excerpt as to separate large articles I simple output the excerpt, immediately followed by the body text. If there is no excerpt only the body text is output.

In addition to the above we will want the ability for people to add comments to our articles (and I suggest you sign up for and use Akismet).

We need to add the following code to display out comments:

  1. <a name=comments></a>
  2. {% if article.comments.size != 0 }
  3. { for comment in article.comments }
  4. {{ comment.author_link }}
  5. {{ comment.published_at | date: ‘%d %B %Y’ }}
  6. {{ comment.body | textilize }}
  7. { endfor }
  8. { endif %}

The a tag at the top is used so that when a user navigates to the article url #comments they are taken directle to the required location on the page. You can also include the Gravatar of the commentor, if they have set one up using the following tag within the for loop above:

  1. {{ comment | gravatar: 80, “/images/mephisto/avatar.gif” }}

The numeric value 80 is the size of the gravatar display, you can specify any size upto 80px

We also need to provide the user with a form to add comments if we are still accepting comments on that article, we do this as follows:

  1. {% commentform }
  2. <!- comment form ->
  3. <label for=comment_author>Nick Name</label><br/>{{ form.name }}<br/>
  4. <label for=comment_author_email>Email (optional)</label><br/>{{ form.email }}<br/>
  5. <label for=comment_author_url>Website (optional)</label><br/>{{ form.url }}<br/>
  6. <label for=comment_body>Let the thoughts flow….</label><br/>{{ form.body }}<br/>
  7. <input type=submit value=Add />
  8. { endcommentform %}

Tag template (tag.liquid), Archive template (archive.liquid) & Search template (search.liquid)

There are three ways users can find articles on your blog using Mephisto, using the tag facility to find articles tagged under a particular tag, the archive facility to find articles by date and the search facility.

All of these will generally look the same and will likely simply list page titles and dates and perhaps the body or excerpt content, as we did for the section.layout template.

In addition we may want to list the number of articles, for example using

  1. {{articles.size | pluralize: ‘article’ }} found.

Paging

Mephisto will only display the number of items on a page as specified in the Admin Settings. Unfortunately Mephisto only implements paging on the search template when used in conjunction with a search. Here we can add to search.liquid the following to allow users to navigate through the pages.

  1. {% if previous_page != “” }<a href={{ previous_page }}>< Previous Page </a>{ endif } { if next_page != “” }<a href={{ next_page }}>Next Page ></a>{ endif %}

Because the articles.size contains the number of articles to output to the page, with searches that span multiple pages, we would not display the total number of articles found. In this case we need to chnage the example above from articles.size to search_count as follows:

  1. {{search_count | pluralize: ‘article’ }} found.

Error template (error.liquid)

The final template we need to create is the error.liquid template that displays whenever the user requests an article or resource that does not exist. It is good practice to give the user options to find what they are looking for, so I like to firstly display my recent articles, but also provide the search form (see layout.liquid) on the page too, allowing the user to search for the required content.

Partials

As with Rails if we have content that we will use on more than one Liquid template, we can store it in a partical and include it in the templates where needed, for example our tag, archive and search, all list our article listing in the same way. In this case, rather than repeat ourselves for each file we could include the heading in each of the tag, archive and search files and for the actual listing place the code that is repeated in a file called _articles.liquid, i.e.

  1. {{ article | link_to_article }}
  2. {{ article.published_at | date: ‘%b %d’ }}
  3. {{ article.author.login }}
  4. {{ article | linked_tag_list }}
  5. <a href={{ article.url }}#comments>{{ article.comments_count | pluralize: ‘comment’ }}</a>

and then in tag.liquid, archive.liquid and search.liquid include this file for each article in our articles object as follows:

  1. {% include ‘article’ with articles %}

Distributing Templates

Finally if we want to distribute our templates, or if you think you have a good template, why not upload it to the Mephisto Theme Gallery . To do this we need to package the contents of our theme direcory into a .zip file, but before we do this, we need to firstly create a preview image of our theme called preview.png in the root of our theme directory and also create a descriptor file that includes who you are, what the theme is called etc. This is stored in a file called about.yml and is defined in YAML as follows:

  1. title: ClockObj Theme
  2. author: Jon Milet Baker
  3. version: 1.0
  4. homepage: http://clockobj.co.uk
  5. summary: THe theme for clockobj.co.uk

This article has hopefully been a useful overview to creating your own Mephisto theme. There are lots of resources not only on Mephisto’s site but also on their Wiki

More Information

Comments below..

Advertisement

 




Comments

Note: You can leave a response, or trackback from your own site.

 
Comment:

Thanks so much! This is exactly what I’ve needed at the right moment! Wow, do share more. For example, I’m also interested in print, pdf and perhaps other types of output (using REST?).

 
 
Comment:

ROR?

 
 
Comment:

ROR = Ruby on Rails - Sorry for the acronym ;)

 
 
Comment:

This is what I was looking for. I was reading thru the article a few times and i hope I can jump in to the creation of a new theme for which I already have the design, I ‘just’ need to implement it with the things above. Probably I will come back to ask some questions regarding to this project of mine, if I can :)
Thanks a lot!!

 
 
Comment:

Fabulous resource. Thanks for putting this all down. I’ve tried to customize a few Mephisto templates and was a little frustrated by the absence of something like this.

 
 
Comment:

good article but there are several mistakes:
1. ‘%’ missing, like this {% for month in section.months }
2. Archives implementation is wrong, you when you click on the archive link and go to the archive page and then you will find the archive link will be wrong.
you should implement is like this:
{% for month in section.months %}
{{ section | monthly_articles: month | size | assign_to: ‘articles_count’ }}
{% if articles_count > 0 %}

{{section | link_to_month: month}} ({{articles_count}})

{% endif %}
{% endfor %}

use the {{section | link_to_month: month}} to generate the link

 
 
Comment:

Thanks for the corrections, You are correct coderay unfortunately did not like the liquid formatting and seems to have dropped certain % signs. Thanks for pointing out and fixing the issue with th archive link snippet.

 
 
Pingback:

[...] creating a liquid mephisto theme blog entry [...]

 
 
Comment:

Jackson Pires has translated this article into Brazilian Portuguese here:
http://jacksonpires.blogspot.com/2008/02/criando-um-tema-para-o-mephisto.html

Thanks, Jon

 
 
Comment:

Nice article, thank you. Please tell also how to use articles tags in meta keywords?

 
 
Comment:

i translate this article to russian - http://fakmak.com/2008/4/21/meqhisto-liquid

 
Add a comment: