A beginner's Zotonic tutorial on custom types

If you are new to Zotonic, one of the first questions you might have is how to create custom types, how to edit them from the Zotonic Administrator and how to display them to your visitors. In this short tutorial we are going to create a simple HOTEL resource type with one custom property and link the resource to another through predicates.


Specifically, here's what we'll do:

  • Create a new category for our resource type (Hotel).
  • Extend the admin UI to allow additional custom properties to be maintained for that type (in our case only one: the number of rooms, the hotel has).
  • Display the number of rooms when visitors view a hotel page.
  • Create another category (Hotel Operator) as well as the predicate 'operated_by' to be able to link the two.
  • Display the Hotel Operator's name on the hotel page.

I am a Zotonic novice myself and would like to thank Andreas and Jeff for their help in the Zotonic dev group. Thanks a lot, guys.

Adding a Category

You probably already know how to create categories in Zotonic but if you don't please follow the steps below:

  • Logon to Zotonic Admin at http://yoursite/admin
  • Select Structure -> Categories from the menu
  • Create the new "Hotel" category (I chose to make mine a subcategory of "Location")
    • Title: Hotel
    • Category: Category
    • Name: hotel
    • Published: true

Extending the Admin UI

At this stage we could create new Hotel resources and programmatically attach arbitrary properties such as 'number of rooms' to it, but we want to be able to edit such properties from the admin directly. Here's how:

  • Create the file zotonic/priv/sites/<yoursite>/templates/_admin_edit_content.hotel.tpl (see source below)
  • Create a new page from the Dashboard and place it into the "Hotel" category
{% extends "admin_edit_widget_std.tpl" %} 
{% block widget_title %}{_ Hotel Details _}{% endblock %} 
{% block widget_show_minimized %}false{% endblock %} 
{% block widgett_content %} 
  <fieldset class="admin-form"> 
    <div class="form-item clearfix"> 
       <label for="hotel-number-of-rooms">
         {_ Number of rooms _}
       </label> 
      <input type="text" id="hotel-number-of-rooms" 
             name="hotel_number_of_rooms"
             value="{{ id.hotel_number_of_rooms }}" /> 
   </div>
  </fieldset> 
{% endblock %}

You should see a new box to maintain the hotel details. Nice :) This was easy enough. Thanks to Andreas for pointing me at this Gist.

Extended Zotonic Admin UI

Note: I was a little surprised that I didn't have to recompile anything but this is probably because I activated the"Development" module under System -> Modules. If you don't see the box try to activate that module, restart Zotonic or run z:m() from the Zotonic debug shell. Better yet, if you know when one needs to recompile what, please leave a comment - I would love to find out too.

Displaying custom properties to site visitors

If you preview your hotel you will find that the new property is not shown. Let's fix that.

  • Create the file templates/page.hotel.tpl with the following contents.
{% extends "page.tpl" %}

{% block below_summary %}
{% if m.rsc[id].hotel_number_of_rooms %}
  {_ Number of rooms _}: {{ m.rsc[id].hotel_number_of_rooms }}
{% endif %}
{% endblock %}

If you reload a hotel page you should now see something like this:

Zotonic page view

What's going on here?

Let's briefly recap and look at what we've done or, just as importantly, at what we haven't. We haven't created a controller, a model or a dispatch rule. All we've created are a category and two templates. One for the back-, one for the front-end. Maybe other frameworks do the same - I don't know - but it certainly seems easy and powerful. A frontend developer could work on her stuff without depending on the backend to provide the data first (although I would like to find out how to be alerted whether the front-end team tries to read undefined values. Some logs? - if you know please comment)

Please take a look at the catinclude tag documentation to learn why this works.

Linking resources

Please create the category 'Hotel Operator'. We are not going to add any custom properties so it's enough to use the admin as we don't need any custom code. Once you've created the category please add a page to it with some title such as "Zotonic Hotels Incorporated".

Predicates

Predicates are used to express relations of the form Subject > Predicate > Object. In our example, we would like to express Hotel > operated by > Hotel Operator. Please create the predicate operated_by and a matching relation as follows:

  • Select Structure -> Predicates from the admin menu.
  • Click on 'Make a new predicate'.
    • Title: operated_by
    • Click 'Make predicate'
    • Check From Category 'Hotel'
    • Check To Category 'Hotel Operator'
    • Save
  • Load the admin page for the hotel you created previously.
  • Add a 'operated_by' connection to 'Zotonic Hotels Inc.' towards the bottom of the right content bar and save.

Displaying the relation

Now that the relationship is known to Zotonic let's display it on the hotels page. Here's our updated page.hotel.tpl. Look at the last three lines in particular.

{% extends "page.tpl" %}

{% block below_summary %}
{% if m.rsc[id].hotel_number_of_rooms %}
  {_ Number of rooms _}: {{ m.rsc[id].hotel_number_of_rooms }}
{% endif %}
<br />
Hotel operator: {{ id.o.operated_by[1].title }}
{% endblock %}

A few things to note about this code:

  1. I dind't check whether the relation is defined using 'if' because I was lazy.
  2. I used the shorthand notation for m.rsc[id] - namely just id by itself.
  3. We access the predicate's object via the o.predicate_name accessors
  4. Zotonic appears to use a 1-based index

Here's what the output looks like:

Zotonic page view with predicate relation

Conclusions

I read a comment on stackoverflow with respect to Zotonic that claimed that CMS systems are for non-programmers. It was probably just a joke but I couldn't agree less. CMS systems are for programmers that don't like to reinvent the wheel and I for one think I might have found my new favourite framework in Zotonic. Sure, I am Erlang/Zotonic novice suffering from Erlang fever and I am not quite sure yet that I like how Zotonic appears to depend on Postgresql and that a lot of logic can leak into the views but I cannot help feeling that Zotonic is awesomely powerful. We'll see. Exciting times ahead.

Thanks for reading and please leave a comment, suggestion or correction (except for spelling mistakes :). Thanks, HC

 

 



blog comments powered by Disqus
10 October 1990 - 10:11:12 10 October 1990