Zotonic: the Erlang Content Management System

It's more than just a CMS. Create complicated Web sites quickly with Zotonic.
Templates

Create a new subdirectory in mod_guestbook called templates:

mkdir priv/sites/default/mod_guestbook/templates

Using your favorite text editor, create the following file named guestbook.tpl:


{% extends "base.tpl" %}
    
{% block content %}
<h1>Guestbook</h1>
<ul id="guestbook-posts" class="guestbook-posts">
{% with 
    m.search[
        {query cat='gp' sort='-publication_start'}
    ] as posts %}
    {% for post in posts %}
        {% include "_guestbook_post.tpl" %}
    {% endfor %}
{% endwith %}
</ul>
{% include "_guestbook_form.tpl" %}
{% endblock %}

This template fetches the pages of category guestbook_post in order of publication date; it extends the base template of the site in which it is used and overrides the “content block” of that base template.

I also am including two other templates, _guestbook_post.tpl and _guestbook_form.tpl. I'll create these templates later.

Next, you need a dispatch rule. Create a new subdirectory of mod_guestbook named dispatch:

mkdir priv/sites/default/mod_guestbook/dispatch

Using a text-editor, create a file called dispatch (with no extension) in the dispatch folder. It should contain the following dispatch rule:

[
    {guestbook, ["guestbook"],    
                resource_template,   
                [ {template, "guestbook.tpl"}]}
].

The first parameter above is the name of the rule. This is followed by a list containing the URI scheme; in this case, it's simply /guestbook. Let's use a premade Zotonic resource called resource_template to do the rendering, and the template that you actually will be rendering is called guestbook.tpl.

Save everything, run make and then restart Zotonic. When you navigate to 127.0.0.1:8000/guestbook, you will see a page that simply contains the heading Guestbook.

In the template above, I included another template called _guestbook_post.tpl, which I did not create yet. This template will contain the details of each guestbook post and be rendered once for every guestbook post. Let's create it now in the templates subdirectory of mod_guestbook:


    <li class="guestbook-post">
        <p>{{ post.title }}-{{post.summary}}</p>
    </li>

Run make and reload Zotonic. You now should see the guestbook posts you created earlier in the admin.

The next step is to allow users to sign the guestbook by creating the _guestbook_form.tpl template:


{% wire id="guestbook-form" 
            type="submit" 
            postback={np} 
            delegate="mod_guestbook" %}
<form id="guestbook-form" 
            method="post" action="postback">
  <div>
    <div class="form-item">
      <label for="title">Title</label>
        <input type="text" name="title" id="title" />
        {% validate id="title" type={presence} %}
    </div>
    <div class="form-item">
      <label for="summary">Summary</label>
      <input type="text" name="summary" id="summary" />
      {% validate id="summary" type={presence} %}
    </div>
    <div class="form-item button-wrapper">
      <button type="submit">{_ Post _}</button>
    </div>
  </div>
</form>

You use the “wire” scomp to specify that the form with the id="guestbook-form" will be handled by the event function of mod_guestbook. You also use the “validate” scomp to check for the presence of the required fields. If you wanted the summary field to be optional, you could leave out the validate scomp for the summary field. Here, you just use the presence validator, but there are others, such as numericality, length, confirmation (for making sure two fields match) and the very useful format validator, which takes a regular expression.

Now, you need to implement the event function of mod_guestbook to handle this post:

%% @doc Handle the submit event of guestbook
event({submit, {np, _}, _TriggerId, _TargetId}, C) ->
    T = z_context:get_q_validated("title", C),
    S = z_context:get_q_validated("summary", C),
    CatId = 
    m_category:name_to_id_check(gp, C),
    AC = z_acl:sudo(C),
    Props = [
         {category_id, CatId},
         {title, T},
         {summary, S},
         {is_published, true}],
     {ok, RscID} = m_rsc:insert(Props, AC),
     Post = m_rsc:get(RscId, C),
     TemplateProps = [
         {post, Post}
     ],
     Html = z_template:render("_guestbook_post.tpl",
                                  TemplateProps, AC),
     z_render:insert_top("guestbook-posts", 
                                  Html, AC).

Don't forget to export event/2. Now you are writing Erlang code and making use of some support functions that come with Zotonic.

If you are new to Erlang, the first thing to note is that once a variable has been bound to a value, it cannot be changed. This may seem strange, but the idea is to avoid side effects, so that you can write distributed applications. A nice side effect (I know) is that it makes Erlang easier to debug.

In Erlang, you use lists and tuples for storing aggregates of data. A list is enclosed in square brackets and a tuple in curly brackets. Variables in Erlang start with a capital letter (Props), and you also use atoms, which are lowercase. Atoms do not have any value associated with themselves; in essence, they are the value.

Functions that relate to access control are found in z_acl, and in this case, you use z_acl:sudo to gain superuser rights. z_context:get_q_validated allows you to get the contents of a validated field from the post; z_template:render returns a rendered template, and z_render:insert_top inserts some text at the top of an HTML element with a given ID. More support functions can be found in src/support.

The code for accessing the database is found in src/models. Here, I accessed the database to check the ID of a category (m_category:name_to_id_check) and also to insert a new resource (m_rsc:insert).

The guestbook is not completely finished. You still need to add the name of the person that signed it. This is easy, however, and you don't need to go near the database to do it. Simply add a new field to your form template, modify your event function to handle that field, and your guestbook will be complete.

Michael Connors is a freelance software developer from Ireland, but he currently lives in Normandy, France. These days, he mostly develops software in Erlang.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Quitting Out of the Erlang Interpreter

Thomas Legg's picture

Instead of ctrl-C + abort, the faster and cleaner way out of the erlang interpreter command prompt >:
q().
Kind of like typing quit() at a python interpreter command prompt, but shorter and ending with a period/full stop. (Don't all good sentences end with a period? They do in Erlang.)

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix