You are on page 1of 26

Digitalus CMS Tutorial: Creating Modules

1/26

Developing custom user modules for Digitalus CMS


Introduction
General The Digitalus CMS offers the possibility to create user custom modules. As the Zend Framework, these modules follow the MVC pattern (Model-View-Controller). The administration backend contains a separate tab to deal with modules. On this page all existing modules are listed. By choosing a specific module from the list, the options for this module appear and modifications can be made (creating an instance of a module, e.g. a specific blog). Digitalus CMS modules are linked to single pages. In the Pages tab one can select a module for a specific page from the list of existing modules. This means that the same module can be selected for different pages. This How-To shows how to create simplistic guestbook module; it refers to the Digitalus CMS version 1.9. The How-To should be thought of as a starting point and is in no way complete. It shows how the Digitalus CMS deals with modules, but it doesn't show the basics of Zend_Framework or PHP programming (also security aspects are only touched slightly). What do we want our module to do? A guestbook is often found on websites. It gives a website visitor the opportunity to leave a comment, e.g. whether he likes the site or not. Mostly, the comments are stored into a database and can be listed in the order of their submission date. So, what do we need? We need a public frontend to: show a list of already submitted guestbook entries provide an HTML form for users to submit a new entry create instances of the guestbook delete guestbook entries that have illegal or offending contents

We need an administration backend for our module to:

Digitalus CMS Tutorial: Creating Modules

2/26

Directory Structure
Digitalus CMS modules are found in the directory /application/modules. The way modules are handled by the CMS requires to have at least two controllers and view scripts, i.e. index and public. More controllers and view scripts can be created as we will see. To start with our module create a directory called guestbook. As modules follow the MVC pattern create the following subdirectories as proposed by the Zend Framework:
/application/modules/guestbook: controllers data forms models views helpers scripts index public

The data directory will later store several different data, e.g. translation files. Now already, the module guestbook appears on the admin tab Modules.

When clicking on the link an exception occurs:


Fatal error: Uncaught exception 'Zend_Controller_Dispatcher_Exception' with message 'Invalid controller specified (index)' in /var/www/...

As already mentioned the IndexController is needed for the backend.


Check the link! The reference is something like urlOfWebsite/mod_guestbook. Create a page called MyGuestbookPage with the default template that links to the guestbook module. On the more content pane You will see a module selector, but the guestbook module doesn't appear. In the frontend go to Your page just created! In th source code of that page You should find the following lines:
<div class="module"> </div>

Digitalus CMS Tutorial: Creating Modules

3/26

Start with the admin backend


Let's create an IndexController and a view: /application/modules/guestbook/controllers/IndexController.php:
<?php class Mod_Guestbook_IndexController extends Zend_Controller_Action { public function init() { } public function indexAction() { } }

/application/modules/guestbook/views/scripts/index/index.phtml: /application/modules/guestbook/views/scripts/index/index.options.phtml:

<?php echo 'main';

<?php echo 'options';

Okay, not bad. But what about the titles? There are three empty titles right now. Change Your already existing files as follows: /application/modules/guestbook/controllers/IndexController.php:
<?php class Mod_Guestbook_IndexController extends Zend_Controller_Action { public function init() { $this->view->breadcrumbs = array( $this->view->getTranslation('Modules') => $this->view->getBaseUrl() . '/admin/module', $this->view->getTranslation('Guestbook') => $this->view->getBaseUrl() . '/mod_guestbook' ); $this->view->toolbarLinks['Add to my bookmarks'] = $this->view->getBaseUrl() . '/admin/index/bookmark/url/mod_guestbook'; } public function indexAction() { } }

/application/modules/guestbook/views/scripts/index/index.phtml:

<?php $this->placeholder('formHeadline')->set($this->getTranslation('Guestbook Module')); echo 'main'

Digitalus CMS Tutorial: Creating Modules

4/26

/application/modules/guestbook/views/scripts/index/index.options.phtml:

<?php $this->placeholder('optionsHeadline')->set($this->getTranslation('My Guestbooks')); echo 'options';

Looks better, but do we do here? In the init() method of the IndexController we make use of two view helpers from the Digitalus library, i.e. Breadcrumbs and ToolbarLinks. We feed both with translatable texts and links that start with $this->view->getBaseUrl(). This is again a view helper that returns the base url of the website. This is required if Digitalus is not installed in the public root but in a sub-directory. The view scripts index.phtml and index.options.phtml make use of placeholders that are define within the Digitalus CMS, i.e. formHeadline and optionsHeadline. As the name says, they are the headlines.

Second step: the frontend


For the frontend the PublicController is needed as well as the view scripts in the public directory. A Digitalus CMS module can contain multiple frontend actions and views. For each action in PublicController there will be an entry in the backend's module selector. That means that You can choose what to display on Your module page. Create the PublicController: /application/modules/guestbook/controllers/PublicController.php:
<?php class Mod_Guestbook_PublicController extends Zend_Controller_Action { public function init() { } public function indexAction() { } public function index2Action() { } }

Digitalus CMS Tutorial: Creating Modules

5/26

/application/modules/guestbook/views/scripts/public/index.phtml: /application/modules/guestbook/views/scripts/public/index2.phtml:

<?php echo 'frontend1';

<?php echo 'frontend2';

If You select index to be the frontend action for Your page, the page will look something like this:

As our IndexAction is empty and the view script index.phtml simply echoes frontend1 the content of the page is simply the echoed string (apart from headers and titles). If Your module provides any options that should be selected by the administrator, You can (and You will) create a view script with the same name, but form before the file extension, e.g. index.phtml and index.form.phtml. Mostly, the latter will hold a form element to select Your options. We'll come to this point later. So far, this is not too complicated. Let's get into more detail. (Let's drop the index2Action as it was only for demonstrating purposes)

Digitalus CMS Tutorial: Creating Modules

6/26

Frontend entry form


Let's create a form for the website visitor to be able to send a new guestbook entry. Change Your already existing files as follows: /application/modules/guestbook/forms/Entry.php
<?php class Entry_Form extends Digitalus_Form { public function __construct($options = null) { parent::__construct($options); $view = $this->getView(); $id = $this->createElement('hidden', 'id'); $guestbook = $this->createElement('hidden', 'guestbook_id'); $author = $this->createElement('text', 'author'); $author->setLabel($view->getTranslation('Name') . ':') ->setRequired(true); $email = $this->createElement('text', 'email'); $email->setLabel($view->getTranslation('Email') . ':'); $content = $this->createElement('textarea', 'content'); $content->setLabel($view->getTranslation('Content')) ->setRequired(true); $submit = $this->createElement('submit', 'submit'); $this->setMethod('post') ->addElement($id) ->addElement($guestbook) ->addElement($author) ->addElement($email) ->addElement($content) ->addElement($submit); } }

/application/modules/guestbook/controllers/PublicController.php:

<?php require_once './application/modules/guestbook/forms/Entry.php'; class Mod_Guestbook_PublicController extends Zend_Controller_Action { public $moduleData; public $properties; public function init() { $module = new Digitalus_Module(); $this->moduleData = $module->getData(); $this->properties = Digitalus_Module_Property::load('mod_guestbook'); } public function indexAction() { $entryForm = new Entry_Form(); $entryForm->setAction($_SERVER['REQUEST_URI']); $submit = $entryForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Create Guestbook Entry')); $this->view->form = $entryForm; } }

Digitalus CMS Tutorial: Creating Modules

7/26

<?php

/application/modules/guestbook/views/scripts/public/index.phtml:

echo $this->form;

Again, it's not kind of magic. We create a form that extends Digitalus_Form. We add some form elements to the form - that's it. In our IndexAction of the IndexController we create an instance of the new form, modify it a bit (setAction(), etc.) and add it to our view. The view script simply echoes the form. That's how it looks by now:

Digitalus CMS Tutorial: Creating Modules

8/26

Do some action
Okay, but what happens when we push the button? We have set the following action in the PublicController:
$entryForm->setAction($_SERVER['REQUEST_URI']);

So, the action of the form is the url we come from. The PublicController must react differently depending on whether the form is submitted or not. What do we expect the form to do? It should validate the input and if valid send the data to the server and store it into a database. That means, first of all we need a model for the guestbook entry that extends Zend_Db_Table_Abstract somehow. The model The Digitalus CMS offers some basic classes that already extend this abstract class, but it also provides some models that could be extended for our specific use. The Model_Page model is a good base to build own classes on as it provides several useful methods. It is already linked to a database (pages). More complex modules might require very special databases, but that's not shown in this tutorial. The new model should assist us in creating new entries, deleting entries and fetching entries from the database. Let's create the model with these basic methods: /application/modules/guestbook/models/Entry.php:
<?php class Guestbook_Entry extends Model_Page { protected $_namespace = 'guestbook_entry'; public function getEntries($guestbookId) { } public function getEntry($entryId) { } public function createEntry($guestbookId, $title = null) { return $this->createPage($title, $guestbookId); } public function deleteEntry($entryId) { $this->deletePageById($entryId); } }

First of all, we simply want to create an entry and be able to delete it. We'll come to the other methods later. The createEntry method makes use of the createPage method of the Model_Page model as well as the deleteEntry method uses the deletePageById.

Digitalus CMS Tutorial: Creating Modules The controller Now we need to modify the PublicController. /application/modules/guestbook/controllers/PublicController.php:
<?php require_once './application/modules/guestbook/forms/Entry.php'; require_once './application/modules/guestbook/models/Entry.php'; class Mod_Guestbook_PublicController extends Zend_Controller_Action { public $moduleData; public $properties; public function init() { $module = new Digitalus_Module(); $this->moduleData = $module->getData(); $this->properties = Digitalus_Module_Property::load('mod_guestbook'); } public function indexAction() { $entryForm = new Entry_Form(); $entryForm->setAction($_SERVER['REQUEST_URI']); $submit = $entryForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Create Guestbook Entry')); if ($this->_request->isPost() && $entryForm->isValid($_POST)) { $values = $entryForm->getValues();

9/26

$mdlEntry = new Guestbook_Entry(); $entry = $mdlEntry->createEntry($values['guestbook_id'], substr($values['content'], 0, 250)); $this->_forward('index'); } $this->view->form = $entryForm; } }

The indexAction now checks whether the form is submitted and valid. If this is true, a new entry is created by using the createEntry method of the Guestbook_Entry model. You might wonder what the substr function in the indexAction is all about. Well, the Model_Page model requires a name attribute. What makes sense for pages doesn't necessarily make sense for guestbook entries. So, I decided to store the first 250 letters in the name attribute (we can use it later to generate a preview of the entry).
Go to the frontend page MyGuestbookPage You created earlier Submit an empty form; You will see some error messages occur. Submit the form with some content. Check Your database (e.g. by using phpMyAdmin). You will see that a new entry is created in the database pages.

Digitalus CMS Tutorial: Creating Modules

10/26

But what guestbook are the entries related to? And what about the content? When and how is it stored to the database?

Digitalus CMS Tutorial: Creating Modules

11/26

The guestbook
We want to create a guestbook in the administration backend. Then we want to assign a specific guestbook to a page. For the creation of guestbook we need an HTML form. It's almost the same as the Entry_Form. /application/modules/guestbook/forms/Entry.php:
<?php class Guestbook_Form extends Digitalus_Form { public function __construct($options = null) { parent::__construct($options); $view = $this->getView(); $id = $this->createElement('hidden', 'id'); $name = $this->createElement('text', 'name'); $name->setAttrib('size', 40) ->setRequired('true') ->setLabel($view->getTranslation('Guestbook Name') . ':'); $submit = $this->createElement('submit', 'submit'); $this->setMethod('post') ->addElement($id) ->addElement($name) ->addElement($submit); } }

What we need to do then, is to modify the IndexController as it's the starting point for the backend and the related view scripts. /application/modules/guestbook/controllers/IndexController.php:
<?php require_once APPLICATION_PATH . '/modules/guestbook/forms/Guestbook.php'; class Mod_Guestbook_IndexController extends Zend_Controller_Action { public function init() { $this->view->breadcrumbs = array( $this->view->getTranslation('Modules') => $this->view->getBaseUrl() . '/admin/module', $this->view->getTranslation('Guestbook') => $this->view->getBaseUrl() . '/mod_guestbook' ); $this->view->toolbarLinks['Add to my bookmarks'] = $this->view->getBaseUrl() . '/admin/index/bookmark/url/mod_guestbook'; } public function indexAction() { $guestbookForm = new Guestbook_Form(); $guestbookForm->setAction($this->view->getBaseUrl() . '/mod_guestbook/guestbook/create'); $submit = $guestbookForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Create Guestbook')); $this->view->form = $guestbookForm; } }

/application/modules/guestbook/views/scripts/index/index.phtml:

<?php $this->placeholder('formHeadline')->set($this->getTranslation('Guestbook Module'));?> <p><?php echo $this->getTranslation('The guestbook module enables you to publish multiple guestbooks on your site.');?></p> <fieldset> <legend><?php echo $this->getTranslation('Create a new guestbook');?></legend> <?php echo $this->form; ?> </fieldset>

Digitalus CMS Tutorial: Creating Modules

12/26

The action for the HTML form leads to the createAction of the GuestbookController. Both don't exist so far. Also, a guestbook model would be a good idea. Let's start with the model. /application/modules/guestbook/models/Guestbook.php:
<?php class Guestbook_Guestbook extends Model_Page { protected $_namespace = 'guestbook'; public function createGuestbook($name) { return $this->createPage($name); } public function updateGuestbook($id, $name) { $data = array( 'page_id' => $id, 'name' => $name ); return $this->edit($data); } public function deleteGuestbook($id) { $this->deletePageById($id); } public function getGuestbooks() { $select = $this->select(); $select->where('namespace = ?', $this->_namespace); $select->order('name'); $result = $this->fetchAll($select); if ($result->count() > 0) { return $result; } return null; } }

This looks very similar to the Model_Entry model. New is the getGuestbooks method, which retrieves all guestbooks from the database. We will use it later.

Digitalus CMS Tutorial: Creating Modules Now to the GuestbookController: /application/modules/guestbook/controllers/GuestbookController.php:


APPLICATION_PATH APPLICATION_PATH APPLICATION_PATH APPLICATION_PATH . . . . '/modules/guestbook/forms/Guestbook.php'; '/modules/guestbook/forms/Entry.php'; '/modules/guestbook/models/Guestbook.php'; '/modules/guestbook/models/Entry.php'; <?php require_once require_once require_once require_once

13/26

class Mod_Guestbook_GuestbookController extends Zend_Controller_Action { public function init() { $this->view->breadcrumbs = array( $this->view->getTranslation('Modules') => $this->view->getBaseUrl() . '/admin/module', $this->view->getTranslation('Guestbook') => $this->view->getBaseUrl() . '/mod_guestbook' ); $this->view->toolbarLinks['Add to my bookmarks'] = $this->view->getBaseUrl() . '/admin/index/bookmark/url/mod_guestbook'; } public function createAction() { $form = new Guestbook_Form(); if ($form->isValid($_POST)) { $values = $form->getValues(); $mdlGuestbook = new Guestbook_Guestbook(); $guestbook = $mdlGuestbook->createGuestbook($values['name']); $this->_request->setParam('id', $guestbook->id); $this->_request->setParam('isInsert', true); $this->_forward('edit'); } else { $this->_forward('index', 'index'); } } public function editAction() { $form = new Guestbook_Form(); $mdlGuestbook = new Guestbook_Guestbook(); $mdlEntry = new Guestbook_Entry(); if ($this->_request->isPost() && $form->isValid($_POST) && $this->_request->getParam('isInsert') != true) { $values = $form->getValues(); $guestbook = $mdlGuestbook->updateGuestbook($values['id'], $values['name']); $guestbook = $guestbook->page; } else { $id = $this->_request->getParam('id'); $guestbook = $mdlGuestbook->find($id)->current(); $form->populate($guestbook->toArray()); } $this->view->form = $form; $this->view->guestbook = $guestbook; $this->view->entries = $mdlEntry->getEntries($guestbook->id); $this->view->breadcrumbs[$guestbook->name] = $this->view->getBaseUrl() . '/mod_guestbook/guestbook/edit/id/' . $guestbook->id; $this->view->toolbarLinks['Delete'] = $this->view->getBaseUrl() . '/mod_guestbook/guestbook/delete/id/' . $guestbook->id; } public function deleteAction() { $id = $this->_request->getParam('id'); $mdlGuestbook = new Guestbook_Guestbook(); $mdlGuestbook->deletePageById($id); $this->_forward('index', 'index'); } }

Okay, now we're doing it a little quicker. The init() method is well known by now.

Digitalus CMS Tutorial: Creating Modules

14/26

The createAction checks whether the submitted form is valid. If so, a new guestbook is created via the Model_Guestbook and we redirect to the editAction If not, we redirect to the indexAction of the IndexController. We don't need a view script for the createAction because we do not display anything but redirect promptly to the editAction. The interesting things are done within the editAction. Again, we check whether the HTML form is submitted and valid. If not, we search the database for the current guestbook and populate it's content into the HTML form, so we can edit it. If it is submitted, we retrieve the contents of the HTML form and update the guestbook with it. Last but not least, the view script(s): /application/modules/guestbook/views/scripts/guestbook/edit.phtml:
<?php $this->placeholder('formHeadline')->set($this->getTranslation('Update Guestbook'));?> <fieldset> <legend><?php echo $this->getTranslation('Update Guestbook');?></legend><?php echo $this->form;?> </fieldset>

We're ready to create and update a guestbook. Using specific guestbooks for pages Now, we want to assign our freshly created guestbook to a specific page. What exactly do we want to do? We have created a page called MyGuestbookPage. And we have created a guestbook called MyGuestbook. When we are editing the page MyGuestbookPage, we want to select the module guestbook for the page and we want to select the guestbook MyGuestbook. First of all, we create a view helper for this task. In /application/modules/guestbook/views/helpers create the file SelectGuestbook.php with following content. /application/modules/guestbook/views/helpers/SelectGuestbook.php:
<?php class Zend_View_Helper_SelectGuestbook extends Zend_View_Helper_Abstract { public function selectGuestbook ($name, $value) { $mdlGuestbook = new Guestbook_Guestbook(); $guestbooks = $mdlGuestbook->getGuestbooks(); if ($guestbooks == null) { return $this->view->getTranslation('There are no guestbooks to view!'); } else { $options[] = $this->view->getTranslation('Select One'); foreach($guestbooks as $guestbook) { $options[$guestbook->id] = $guestbook->name; } return $this->view->formSelect($name, $value, null, $options); } } }

The view helper is automatically registered by the Digitalus CMS. It creates a dropdown menu with a list of all existing guestbooks. As mentioned above, we need the following view script to call an HTML form element.

Digitalus CMS Tutorial: Creating Modules

15/26

/application/modules/guestbook/views/scripts/index/index.options.phtml:

<?php $this->placeholder('optionsHeadline')->set($this->getTranslation('My Guestbooks')); if ($this->guestbooks != null) { echo '<ul class="padding-10">'; foreach ($this->guestbooks as $guestbook) { echo '<li>' . $this->link($guestbook->name, '/mod_guestbook/guestbook/edit/id/' . $guestbook->id, 'book.png') . '</li>'; } echo '</ul>'; } else { echo '<p class="padding-10">' . $this->getTranslation('You do not have any guestbooks currently') . '</p>'; }

But how does the page know the guestbook id? Well, that is magic! The id of the selected guestbook is stored in the database and can be retrieved with the Digitalus_Module's getData() method. Remember the init() method in our PublicController? That's exactly what we did there. Saving the entry's content The content of our entry, i.e. author name, email, and text are saved into the database as content nodes. They get the parent_id of the created entry and can be retrieved with the knowledge of this id. Modify the PublicController as follows. /application/modules/guestbook/controllers/PublicController.php:
<?php require_once APPLICATION_PATH . '/modules/guestbook/forms/Entry.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Guestbook.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Entry.php'; class Mod_Guestbook_PublicController extends Zend_Controller_Action {

Digitalus CMS Tutorial: Creating Modules


public $moduleData; public $properties; public function init() { $module = new Digitalus_Module(); $this->moduleData = $module->getData(); $this->properties = Digitalus_Module_Property::load('mod_guestbook'); } public function indexAction() { $entryForm = new Entry_Form(); $entryForm->setAction($_SERVER['REQUEST_URI']); $submit = $entryForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Create Guestbook Entry')); $mdlGuestbook = new Guestbook_Guestbook(); $mdlEntry = new Guestbook_Entry(); if ($guestbookId = $this->moduleData->guestbook > 0) { $this->view->guestbook = $mdlGuestbook->find($this->moduleData->guestbook)->current(); $this->view->entries = $mdlEntry->getEntries($this->moduleData->guestbook); $entryForm->getElement('guestbook_id')->setValue($this->moduleData->guestbook); }

16/26

if ($this->_request->isPost() && $entryForm->isValid($_POST)) { $values = $entryForm->getValues(); $entry = $mdlEntry->createEntry($values['guestbook_id'], substr($values['content'], 0, 250)); $arrayContent = array( 'page_id' => $values['guestbook_id'], 'id' => $entry->id, 'author' => $values['author'], 'email' => $values['email'], 'content' => $values['content'], ); $entry = $mdlEntry->edit($arrayContent); } $this->view->form = $entryForm; } }

Now that we know the guestbook id, we can use it to store the guestbook entry's content at the right place. We create an array with the values to be stored and use the edit() method to store it into the database. Also, we pass the entries for the current guestbook and the guestbook itself to the view. We will need it later.

Digitalus CMS Tutorial: Creating Modules

17/26

Extending the backend


Wouldn't it be nice to have a list of all existing guestbooks in the backend? Well, we will need it as soon as we want to modify a guestbook later. Let's modify our IndexController. /application/modules/guestbook/controllers/IndexController.php:
<?php require_once APPLICATION_PATH . '/modules/guestbook/forms/Guestbook.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Guestbook.php'; class Mod_Guestbook_IndexController extends Zend_Controller_Action { public function init() { $this->view->breadcrumbs = array( $this->view->getTranslation('Modules') => $this->view->getBaseUrl() . '/admin/module', $this->view->getTranslation('Guestbook') => $this->view->getBaseUrl() . '/mod_guestbook' ); $this->view->toolbarLinks['Add to my bookmarks'] = $this->view->getBaseUrl() . '/admin/index/bookmark/url/mod_guestbook'; } public function indexAction() { $guestbookForm = new Guestbook_Form(); $guestbookForm->setAction($this->view->getBaseUrl() . '/mod_guestbook/guestbook/create'); $submit = $guestbookForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Create Guestbook')); $this->view->form = $guestbookForm; $mdlGuestbook = new Guestbook_Guestbook(); $this->view->guestbooks = $mdlGuestbook->getGuestbooks(); } }

We added a variable to the view that contains all existing guestbooks. This variable will be used within the index.options.phtml to display a list with them. /application/modules/guestbook/views/scripts/index/index.options.phtml:
<?php $this->placeholder('optionsHeadline')->set($this->getTranslation('My Guestbooks')); if ($this->guestbooks != null) { echo '<ul class="padding-10">'; foreach ($this->guestbooks as $guestbook) { echo '<li>' . $this->link($guestbook->name, '/mod_guestbook/guestbook/edit/id/' . $guestbook->id, 'book.png') . '</li>'; } echo '</ul>'; } else { echo '<p class="padding-10">' . $this->getTranslation('You do not have any guestbooks currently') . '</p>'; }

Now let' display the entries when modifying a specific guestbook. We have to modify the edit.options.phtml in our guestbook directory for that task. /application/modules/guestbook/views/scripts/guestbook/edit.options.phtml:
<?php $this->placeholder('optionsHeadline')->set($this->getTranslation('Guestbook Entries')); if ($this->entries != null) { echo '<ul class="padding-10">'; foreach ($this->entries as $entry) { echo '<li>' . $this->link(date('d.m.Y', $entry->createDate) . ' - ' . $entry->author, '/mod_guestbook/entry/edit/id/' . $entry->id, 'page_white_text.png') . '</li>'; } echo '</ul>'; } else {

Digitalus CMS Tutorial: Creating Modules


echo '<p class="padding-10">' . $this->getTranslation('This guestbook does not have any entries') . '</p>'; }

18/26

We take the view variable entries and loop through it if it exists. We have defined the variable entries in our GuestbookController. For each entry a link is created. The reference of the link is something like /mod_guestbook/entry/edit/id/$entryId.

But wait, we don't have an EntryController! Time to create it. /application/modules/guestbook/controllers/EntryController.php:


<?php require_once APPLICATION_PATH . '/modules/guestbook/forms/Entry.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Guestbook.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Entry.php'; class Mod_Guestbook_EntryController extends Zend_Controller_Action { public function init() { $this->view->breadcrumbs = array( $this->view->getTranslation('Modules') => $this->view->getBaseUrl() . '/admin/module', $this->view->getTranslation('Guestbook') => $this->view->getBaseUrl() . '/mod_guestbook' ); $this->view->toolbarLinks['Add to my bookmarks'] = $this->view->getBaseUrl() . '/admin/index/bookmark/url/mod_guestbook'; } public function editAction() { $entryForm = new Entry_Form(); $entryForm->setAction($_SERVER['REQUEST_URI']); $submit = $entryForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Update Guestbook Entry')); $mdlGuestbook = new Guestbook_Guestbook(); $mdlEntry = new Guestbook_Entry(); if ($this->_request->isPost() && $entryForm->isValid($_POST)) { $values = $entryForm->getValues(); $guestbook = $mdlEntry->updateEntry( $values['id'], $values['author'], $values['email'], $values['content'] ); $entry = $mdlEntry->getEntry($values['id']); } else { $id = $this->_request->getParam('id'); $entry = $mdlEntry->getEntry($id); $entryArray['id'] = $entry->id; $entryArray['guestbook_id'] = $entry->guestbookId;

Digitalus CMS Tutorial: Creating Modules


$entryArray['author'] = $entry->author; $entryArray['email'] = $entry->email; $entryArray['content'] = $entry->content; $entryForm->populate($entryArray); } $guestbook = $mdlGuestbook->find($entry->guestbookId)->current(); $this->view->form = $entryForm; $this->view->guestbook = $guestbook; $this->view->entry = $entry; $this->view->breadcrumbs[$guestbook->name] = $this->view->getBaseUrl() . '/mod_guestbook/guestbook/edit/id/' . $guestbook->id; $this->view->breadcrumbs[$entry->author] = $this->view->getBaseUrl() . '/mod_guestbook/entry/edit/id/' . $entry->id; $this->view->toolbarLinks['Delete'] = $this->view->getBaseUrl() . '/mod_guestbook/entry/delete/id/' . $entry->id; } public function deleteAction() { $mdlEntry = new Guestbook_Entry(); $id = $this->_request->getParam('id'); $entry = $mdlEntry->getEntry($id); $mdlEntry->deletePageById($id); $this->_request->setParam('id', $entry->guestbookId); $this->_forward('edit', 'guestbook'); } }

19/26

Most of the stuff is the same or almost the same as for the GuestbookController. We have an init() method, an editAction and a deleteAction. Init() method and deleteAction should be clear. What's happening in the EditAction? Well, we display the Entry_Form if it is not submitted or if the form is not valid. Otherwise, the entry is updated using an updateEntry() method of the Guestbook_Entry model that doesn't exist by now. Let's create it. /application/modules/guestbook/models/Entry.php:
<?php class Guestbook_Entry extends Model_Page { protected $_namespace = 'guestbook_entry'; ... public function updateEntry($entryId, $author, $email, $content) { $data = array( 'page_id' => $entryId, 'author' => $author, 'email' => $email, 'content' => $content, ); $this->edit($data); } }

The method simply uses the edit() method of the parent class Model_Page ; it updates the author, email and content of the entry. The entry can be deleted by the Delete link at the top right of the page (that's the default place for all Digitalus deletion actions).

Digitalus CMS Tutorial: Creating Modules

20/26

Digitalus CMS Tutorial: Creating Modules

21/26

Extending the frontend


In the preliminary we said that we wanted to display existing guestbook entries in the frontend. At the moment the frontend only displays the HTML form to send new entries to the database. So, we have to modify our frontend view script, which is the public/index.phtml. /application/modules/guestbook/views/scripts/public/index.phtml:
<?php ?> <fieldset> <legend><?php echo $this->getTranslation('Create Guestbook Entry');?></legend><?php echo $this->form;?> </fieldset><?php if (isset($this->guestbook)) {?> <h3><?php echo $this->getTranslation('Current entries in guestbook ');?><strong><?php echo $this->escape($this->guestbook->name);?></strong></h3><?php if ($this->entries != null) {?> <h4><?php echo $this->getTranslation('Number of entries: ') . count($this->entries);?></h4> <div class="guestbook-entries"> <hr /><?php echo $this->partialLoop('partials/entry.phtml', $this->entries);?> </div><?php } }

Well, we still display the form at the top of the page. If a guestbook exists, we display a header and if entries exist we loop through them using a partialLoop. We simply pass the entries to our partial and let it to the rest. That's how our partial looks like. /application/modules/guestbook/views/scripts/partials/entry.phtml:
<?php ?> <h5 class="entry_title"><?php echo $this->escape(this->author) . ' ' . $this->getTranslation('wrote on'). ' ' . $this->renderDate($this->createDate);?></h5> <blockquote> <p><?php echo $this->content . PHP_EOL;?> </p> </blockquote>

We take the passed values and create a title for each entry followed by the content itself. To give the website visitor a visual note that his entry was stored to the database, we make some small changes to the PublicController In the indexAction. /application/modules/guestbook/controllers/PublicController.php:
<?php require_once APPLICATION_PATH . '/modules/guestbook/forms/Entry.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Guestbook.php'; require_once APPLICATION_PATH . '/modules/guestbook/models/Entry.php'; class Mod_Guestbook_PublicController extends Zend_Controller_Action { public $moduleData; public $properties; public function init() { $module = new Digitalus_Module(); $this->moduleData = $module->getData(); $this->properties = Digitalus_Module_Property::load('mod_guestbook'); } public function indexAction() { $entryForm = new Entry_Form();

Digitalus CMS Tutorial: Creating Modules


$entryForm->setAction($_SERVER['REQUEST_URI']); $submit = $entryForm->getElement('submit'); $submit->setLabel($this->view->getTranslation('Create Guestbook Entry')); $mdlGuestbook = new Guestbook_Guestbook(); $mdlEntry = new Guestbook_Entry(); if ($guestbookId = $this->moduleData->guestbook > 0) { $this->view->guestbook = $mdlGuestbook->find($this->moduleData->guestbook)->current(); $this->view->entries = $mdlEntry->getEntries($this->moduleData->guestbook); $entryForm->getElement('guestbook_id')->setValue($this->moduleData->guestbook); }

22/26

if ($this->_request->isPost() && $entryForm->isValid($_POST)) { $values = $entryForm->getValues(); $entry = $mdlEntry->createEntry($values['guestbook_id'], substr($values['content'], 0, 100)); $arrayContent = array( 'page_id' => $values['guestbook_id'], 'id' => $entry->id, 'author' => $values['author'], 'email' => $values['email'], 'content' => $values['content'], ); $entry = $mdlEntry->edit($arrayContent); $this->view->form = $this->view->partial('partials/message.phtml', $arrayContent); } else { $this->view->form = $entryForm; } } }

If the form is submitted successfully, not the form is displayed, but again a partial that looks as follows: /application/modules/guestbook/views/scripts/partials/message.phtml:
$this->getTranslation('thank You for <?php ?> <div class="message"> <p><?php echo '<em>' . $this->escape($this->author) . '</em>, ' . Your entry in the guestbook!');?></p> </div>

Very simple; we take the passed value author to create a short message. Partials have the advantage that You can split Your view scripts into smaller parts that can be used over and over again. The drawback is the worse performance.

Digitalus CMS Tutorial: Creating Modules

23/26

What about form validation?


Our two forms are still kind of raw without any form validation. It's good practice to let the Zend_Validators do the validation of forms. Let's add some validators, attributes and custom error messages to the Entry_Form: /application/modules/guestbook/forms/Entry.php:
<?php class Entry_Form extends Digitalus_Form { public function __construct($options = null) { parent::__construct($options); $view = $this->getView(); $guestbookId = $this->createElement('hidden', 'guestbook_id'); $author = $this->createElement('text', 'author'); $author->setLabel($view->getTranslation('Name') . ':') ->setRequired(true) ->addValidators(array( array('NotEmpty', true), array('Alpha', true), array('StringLength', false, array(6, 40)), )) ->setAttrib('size', 40) ->setErrorMessages(array( 'At least six and a maximum of fourty alphabetic characters are allowed!')

Digitalus CMS Tutorial: Creating Modules


); $email = $this->createElement('text', 'email'); $email->setLabel($view->getTranslation('Email') . ':') ->addValidator('EmailAddress') ->setAttrib('size', 40) ->setErrorMessages(array('Please provide a valid email address!')); $content = $this->createElement('textarea', 'content'); $content->setLabel($view->getTranslation('Content')) ->setRequired(true) ->addValidators(array( array('NotEmpty', true), array('Alnum'), array('StringLength', false, array(6, 1000)), )) ->setAttribs(array('cols' => 52, 'rows' => 16)) ->setErrorMessages(array( 'At least six and a maximum of thousand alphabetic characters are allowed!') ); $submit = $this->createElement('submit', 'submit'); $this->setMethod('post') ->addElement($guestbookId) ->addElement($author) ->addElement($email) ->addElement($content) ->addElement($submit); } }

24/26

Only alphanumeric characters allowed? Well, that doesn't really make sense, but it's only for demonstrating purposes.

Digitalus CMS Tutorial: Creating Modules

25/26

Translations
Finally, let's add a translation file. Create the file /application/module/guestbook/data/language/german.csv. If You're choosing German as Your administrator language, the contents of this file will appear. /application/modules/guestbook/data/language/german.csv:
# admin section -- Module guestbook -# GERMAN # admin section -- Entry -"Guestbook";"Gstebuch" "guestbook";"Gstebuch" "Update Guestbook Entry";"Gstebuch Eintrag aktualisieren "Guestbook Entry";"Gstebuch Eintrag" "Back to";"Zurck zu" "Update Entry";"Eintrag aktualisieren" # admin section -- Guestbook -"Guestbook Entries";"Gstebuch Eintrge" "This guestbook does not have any entries";"Dieses Gstebuch enthlt keine Eintrge" "Update Guestbook";"Gstebuch aktualisieren" "Guestbook Name";"Name des Gstebuchs" # admin section -- Index -"The guestbook module enables you to publish multiple guestbooks on your site.";"Das Gstebuch ermglicht es Ihnen, mehrere Gstebcher auf Ihren Seiten zu verffentlichen." "Create Guestbook";"Gstebuch erstellen" "My Guestbooks";"Meine Gstebcher" "You do not have any guestbooks currently";"Es gibt momentan keine Gstebcher" "Guestbook Module";"Gstebuch Modul" "The guestbook module enables you to publish multiple guestbooks on your site." "Create a new guestbook";"Erstellen eines neuen Gstebuchs" # admin section -- Public -"Create Guestbook Entry";"Gstebuch Eintrag erstellen" "Select a guestbook";"Gstebuch auswhlen" "Current entries in guestbook ";"Aktuelle Eintrge im Gstebuch " "Number of entries: ";"Anzahl der Eintrge: "

Digitalus CMS Tutorial: Creating Modules

26/26

Properties
Each module has a properties.xml file. This file can define any information that might be required for the module: /application/modules/guestbook/properties.xml:
<?xml version="1.0" encoding="UTF-8"?> <properties> <icon>book_open.png</icon> <label>Guestbook</label> </properties>

The icon is used in the backend for the module. The label is shown on the module tab in the backend. You load the properties using the Digitalus_Module_Property class:
$props = Digitalus_Module_Property::load('mod_guestbook');

ACL
You can tie your module into the core CMS access control system by adding an acl.xml file. There are 3 levels of control in this model: 1. Module level: you don't have to do anything for this 2. Controller level: you list the controllers 3. Action level: you list the controller actions as well /application/modules/guestbook/acl.xml:
<?xml version="1.0" encoding="UTF-8"?> <controllers> <controller name='index'> <action>index</action> </controller> <controller name='entry'> <action>delete</action> <action>edit</action> </controller> <controller name='guestbook'> <action>create</action> <action>delete</action> <action>edit</action> </controller> </controllers>

Summary
The Digitalus CMS provides a smooth way to enhance the base system with custom user modules. In this tutorial You learned how to build Your own simple guestbook module. Until now, we have a basic application. It is not perfect but a good starting point. Further enhancements could be: more advanced design custom form decorators notify admin for each new entry (per email) deploy publishing system allow HTML tags for content (perhaps using HTML Purifier)

You might also like