Loading...
August 17, 2010#

Creating Admin Templates in eZ Publish

Adding your own tabs and content to the CMS is essential for some extensions you build. This enables you to isolate functionality for authorised users and embed this functionality into the back office they are familiar with. This tutorial will cover the basics for creating CMS based views and modules which you can use as a base for your own extensions.

To provide some example usage for this, we will create two pages to be used within the CMS. The first will provide a list of all users who have forgotten their passwords. The second will list the users’ who have logged in over the past month (we will also allow the admin user to pick a start and end point to check between). We will ensure that our new options are accessible through a tab bar at the top of the page and we will also ensure a left navigation bar is setup for them as well.

All the code for this tutorial is available through github and the code is written for eZ Publish 4.3 so beware that the screenshots will look different to what you see if you are using a previous version (there is also a slight code change required for versions before this version when you are creating your php code).

Setting up

We will create our new CMS content within a extension, so it is easy to isolate the code and use it in future projects. If you have not set one up before, I’d recommend glancing through my previous tutorial which covered their basic setup (click here to view). I’m actually going to use the extension I created in my previous tutorial for this as the examples here will use the custom extended attribute filters created for the tutorial. You can download the source code for the extension here: http://github.com/davidlinnard/eztutorials or you can download the code directly here.

The extension we will be using for the rest of this tutorial is called onequarterenglish so replace this with the name of your extension in all of the code blocks.

There are a couple of additional settings files we need to update to ensure content for the CMS can be created:

extensions/onequarterenglish/settings/module.ini.append.php

Custom views are created through modules. Each module can hold multiple views. First of all we need to tell eZ Publish to look for modules within the extension. Since we are dealing with user information we are going to call the module we are using for dealing with the user based content “userspecific”. This will be used in the URL whenever we are dealing with views for the module.

We need to set two things in this file. Firstly we add our ExtensionRepositories[] setting which ensures our extension is included when eZ searches for modules, secondly we add ModuleList[] to ensure we can find the specific modules (this will be explained further when we create our first module).

<?php /*
[ModuleSettings] 
ExtensionRepositories[]=onequarterenglish
ModuleList[]=userspecific
*/
?>
extensions/onequarterenglish/settings/design.ini.append.php

The second settings file we need to create ensures eZ Publish looks within our extension for template files. In our case these templates will be created in the admin design folder.

<?php /*
[ExtensionSettings]
DesignExtensions[]=onequarterenglish
*/ ?>

Creating our module

To create our module, firstly created a folder named “modules” in the root of your extension. Within this create a folder with the name of the module. In our case the module is called “userspecific”. All our custom PHP code will be stored within this directory. The custom PHP code can do what you like. Each view you create requires a single PHP file to be built and it also needs to be defined within a common file named “module.php”. The beauty of allowing custom PHP for each template is that you can process any custom logic that is best suited to PHP before you display your template. For instance, you may need to update a custom database table or to customise the view based on what the user’s action was or who the user is.

Let’s create our module.php file and define our first view. it’s a basic definition file which just tells eZ details about the module, views available and also permissions required by the module.

Our first view will provide the user with a list of people who have forgotten their passwords, using the functionality created in my previous tutorial (this is implemented in the source code available at the start of this tutorial).

extensions/onequarterenglish/modules/userspecific/module.php
<?php
 
$module = array( 'name' => 'userspecific' ); //the name of our module
 
$ViewList = array();//add as many views as you want here
$ViewList['forgottenpasswords'] = array( 'script' => 'forgottenpasswords.php', 
                           'functions' => array( 'read' ));//the script used to setup the template plus the user permissions required for it (also see below)
 
//setting user permissions required by our module:
$FunctionList = array(); 
$FunctionList['read'] = array(); 
 
?>

Creating our first view

Now that we’ve defined our view, let’s create it. We’ll start by creating the custom PHP file which will define any custom variables available within the template and define which template file(s) will be loaded. This file will define not only the main template used by the page but it is also where you specify which navigation will be used on the left hand side of the screen.

extensions/onequarterenglish/modules/userspecific/forgottenpasswords.php
<?php
/**
 * File containing the eZ Publish upload view implementation.
 *
 * @copyright Your Name here
 * @license http://ez.no/licenses/gnu_gpl GNU GPL v2
 * @version 1.0.0
 * @package onequarterenglish
 */
 
include_once( 'kernel/common/template.php' );
 
//setting up the eZ Objects required:
$http = eZHTTPTool::instance();
$tpl = eZTemplate::factory();//this line of code is for ez publish 4.3, replace it with the following line for versions prior to that
//$tpl = templateInit();//version <4.3 of eZ Publish should use this line of code instead
 
// Process template and set path data:
$Result = array();
$Result['content'] = $tpl->fetch( 'design:userspecific/forgottenpasswords.tpl' );//main tpl file to display the output
$Result['left_menu'] = "design:userspecific/leftmenu.tpl";
$Result['path'] = array( array( 'url' => 'userspecific/forgottenpasswords',
                                'text' => 'Forgotten Passwords' ) );
?>

The forgottenpasswords.php script does not do much in our example. If you required the page you are loading to carry out additional functionality (such as storing data to the database) that would all happen in here. For our example, however, we only need a very basic script.There are a couple of parts to the script. Firstly the required eZ Publish Objects are setup. Once this has been done we use the $Result array to tell eZ Publish several things:

  • ‘content’ – This tells eZ which template needs to be loaded. We’ll cover the code for this next.
  • ‘left_menu’ – Simply defines what to display in the left column. We will create this once we have a fully working example.
  • ‘path’ – The url used here will be the url your page is accessed through. In this case we are just using the same name for all files to make the name consistent. The text will be used in the title bar.

We can now create the template file we have instructed eZ to look for in our PHP file. This will be a straightforward template file. The fetch call at the top of the code is the most important bit for extracting the information. The other code ensures we duplicate the structure of other pages within the CMS (don’t forget this example is for version 4.3 of the CMS).

extensions/onequarterenglish/design/admin/templates/full/forgottenpasswords.tpl
{def $fetch_params = hash( 'parent_node_id', 5,
              			   'extended_attribute_filter',hash( 'id', 'forgottenPasswords', 'params', hash( ) ),
              			   'depth', 2  )}{*we need to use these twice so let's store them in a variable before we use them:*}
{def $users = fetch( 'content', 'list',$fetch_params)}
{def $num_forgotten = fetch( 'content', 'list_count',$fetch_params)}
{*we don't need to look up the number forgotten here since we are not using a limit on the fetch rows returned
but extended attribute filters can be used for them using the same syntax *}     
 
<div class="box-header">
	<h1>Forgotten Passwords</h1>
	<div class="header-mainline"></div>
</div>
<div class="block">
<p>There are {$num_forgotten} who have forgotten their passwords:</p>
<ul>
	{foreach $users as $user}
	<li>
		<a href={$user.url_alias|ezurl()}>{*let the admin user link through to the user object in the CMS*}
			{$user.name}{*let's just print the user's name*}
		</a>
	</li>
	{/foreach}
</ul>

Now that you are finished you can access the page by entering “userspecific/forgottenpasswords” in the url string (after your admin site alias). You should see something like this:

You will notice the big white space to the left which is usually filled by an additional menu. We’ll create a menu to go here next.

Updating the left navigation

You may remember that in our forgottenpasswords.php file we specified a custom left navigation template file:

extensions/onequarterenglish/modules/userspecific/forgottenpasswords.php
...
// Process template and set path data:
$Result = array();
$Result['content'] = $tpl->fetch( 'design:userspecific/forgottenpasswords.tpl' );
$Result['left_menu'] = "design:userspecific/leftmenu.tpl";
$Result['path'] = array( array( 'url' => 'userspecific/forgottenpasswords',
                                'text' => 'Forgotten Passwords' ) );
?>

Ensuring the left menu is created is as simple as creating the template. Again, we are going to emulate the styling used in the other sections of the CMS to ensure our new section integrates well:

extensions/onequarterenglish/design/admin/templates/full/leftmenu.tpl
<div class="box-header"><div class="box-tc"><div class="box-ml"><div class="box-mr"><div class="box-tl"><div class="box-tr">
<h4>OneQuarterEnglish User Info</h4>
</div></div></div></div></div></div>
 
<div class="box-bc"><div class="box-ml"><div class="box-mr"><div class="box-bl"><div class="box-br"><div class="box-content">
<ul>
    <li><div><a href={'/userspecific/forgottenpasswords'|ezurl}>Forgotten Passwords</a></div></li>
</ul>
</div></div></div></div></div></div>

When you reload the page the left menu will now have appeared. Now that we have created the menu file, you can easily add new items as your list of views gets bigger, simply by adding another list item.

Adding a new tab to the CMS

Our views are now easy to navigate around once we are in the module but how can the user reach the module in the first place? Our final task for making the module accessible is to add a tab to the top bar in the CMS so users can access the content wherever they are in the back office.

This is a very simple process and again it involves creating a configuration file. The Menu.ini file stores the details of all tabs along the top of the page as well as their behaviour in edit, browse and navigation modes.

<h5>extensions/<strong>onequarterenglish</strong>/settings/menu.ini.append.php</h5>
<?php /*
[NavigationPart]
Part[userspecificnavigationpart]=Forgotten Passwords
 
[TopAdminMenu]
Tabs[]=userspecific
 
[Topmenu_userspecific]
NavigationPartIdentifier=userspecificnavigationpart
Name=Forgotten Passwords
Tooltip=Forgotten Passwords
URL[]
URL[default]=userspecific/forgottenpasswords
Enabled[]
Enabled[default]=true
Enabled[browse]=false
Enabled[edit]=false
Shown[]
Shown[default]=true
Shown[edit]=true
Shown[navigation]=true
Shown[browse]=true
 
*/ ?>

Once you reload the tab will now have been added, it’s a straightforward settings file and you can add other items to the same file, if necessary but I would recommend against adding an item for everything due to the limited screen space available on a lot of machines. It’s much better to group the content and link through to other views with the left menu where possible.

Creating a second view – taking parameters and custom user input

Our second view will display when users’ have last logged in based on user defined date ranges. We will allow these to be provided as eZ Publish page parameters, or if they are set, user form values. This will allow us to customise the view according to our needs but it will also let a user override the dates if required.

Updating our module.php file

There are several additional features we need to add to our code for this example. We are going to assume:

  • The view always requires a start date
  • An end date may be provided, if not we will take today as being the end date
extensions/onequarterenglish/modules/userspecific/module.php

Below is our updated code for the module.php file, including our new view (it is straight after the end of the declaration of our first view). Most of the code is identical to the code we used last time however this time there are a couple of additional lines which deal with the parameters we are passing in.

<?php
 
$module = array( 'name' => 'userspecific' ); 
$ViewList = array();
 
$ViewList['forgottenpasswords'] = array( 'script' => 'forgottenpasswords.php', 
                           'functions' => array( 'read' ));
$ViewList['lastlogin'] = array( 'script' => 'lastlogin.php', 
                           'functions' => array( 'read' ),
                           'params' => array( 'startdate' ),
    					   'unordered_params' => array( 'enddate'		=> 'enddate' )
                           );//params must be included when the filter is called, unordered params are optional                           
 
 
// The entries in the user rights are used in the View definition,
// we are using this to ensure you can limit who can see this information to only certain users (under Roles and Policies)
// in the user roles
$FunctionList = array(); 
$FunctionList['read'] = array(); 
?>

There are two ways we can pass parameters into our views and we are using both. If a parameter is always provided, eZ Publish allows us to declare the variable as an ordered parameter. This allows us to pass parameters in without a parameter name. Consider the normal edit path of eZ Publish (for example http://mysite.com/content/edit/57/2/eng-GB). This has three ordered parameters:

  • 57 is the object ID
  • 2 is the version
  • That leaves the final parameter which is our language (eng-GB)

In the case of our second parameter where we may be passing in our end date, the ordered parameter approach is not ideal. We may at some point decide we want to allow users to specify the number of days to check rather than an explicit end date. In this case the ordered parameters would need to provide some ind of null value for each parameter which does not exist. To get around this we can instead use unordered parameters, in this case we also include the name of the parameter in the url.

Ordered parameters always come before the unordered parameters so our url will have the following structure:

http://www.onequarterenglish.co.uk/admin/userspecific/lastlogin/1280617200/enddate/1281901277

In this URL:

  • admin
  • userspecific/lastlogin provides the link to our module (we define this in our next step)
  • 1280617200 is our ordered parameter which comes first (this is the start date)
  • /enddate/1281901277 is our unordered parameter for the end date.

Creating our new module

Now we have defined our new module, let’s create it. Create the following file with the following code

extension/onequarterenglish/modules/userspecific/lastlogin.php
<?php
/**
 * File containing the eZ Publish view implementation for finding out users who have forgotten passwords.
 *
 * @copyright David Linnard
 * @license http://ez.no/licenses/gnu_gpl GNU GPL v2
 * @version 1.0.0
 * @package onequarterenglish
 */
 
include_once( 'kernel/common/template.php' );
 
$http = eZHTTPTool::instance();
$tpl = eZTemplate::factory();//this line is for versions from 4.3
//$tpl = templateInit();//use this for versions prior to 4.3
 
$viewParameters = array();
 
if ( $Params['startdate'] !== false and is_numeric( $Params['startdate']) )
{
	//if start date is set, we need to pass this into the template
	$viewParameters['startdate'] = $Params['startdate'];
}
 
if ( $enddate !== false and is_numeric( $enddate ) )
{
	//The same for the enddate
	$viewParameters['enddate'] = $enddate;
}
 
$tpl->setVariable( 'view_parameters', $viewParameters );
$tpl->setVariable( 'current_url', $Params['Module']->Functions['lastlogin']['uri'] );//we'll use this to keep track of the current page
 
 
// Process template and set path data:
$Result = array();
$Result['content'] = $tpl->fetch( 'design:full/lastlogin.tpl' );
$Result['left_menu'] = "design:userspecific_leftmenu.tpl";
$Result['path'] = array( array( 'url' => 'userspecific/lastlogin',
                                'text' => 'Users Most Recent Login' ) );
?>

There are a couple of things to note:

  • We can access eZ Publish parameters using the $Params array (both ordered and unordered parameters can be accessed in the same way)
  • We can pass variables directly into our template by using the setVariable method on our template object ($tpl->setVariable( ‘view_parameters’, $viewParameters );).

The code in this case is extremely basic but it can be as complex as you need. If you need to handle form validation or storage then that can all be done through this file. Our basic example here will not currently handle custom dates from the user but we will add this once we have set up a basic view.

Creating our view

Our parameters are handled as if we had created them in the template itself (in our case we stored the parameters within an array called $view_parameters and so our start date could be accessed with the code $view_parameters['startdate']). We can therefore just pass the variables into fetch statements as we can with any other information. The following code will use the parameters passed into the page as parameters for a Fetch statement and display the results. We’ll later add a form for handling user input into this template.

/extension/onequarterenglish/design/admin/templates/userspecific/lastlogin.tpl
 
{if is_set($view_parameters['startdate'])}
	{if eq(is_set($view_parameters['enddate']),false())}
		{def $enddate = currentdate()}
	{/if}
 
	{def $fetch_params = hash( 'parent_node_id', 5,
	              'extended_attribute_filter',
	                            hash( 'id', 'lastLogin',
	                                          'params', hash('start_date',$view_parameters['startdate']|wash(),
	                                          				 'end_date',$view_parameters['enddate']|wash() ) ),
	              'depth', 10  )}
 
	{def $users = fetch( 'content', 'list',$fetch_params)}
	{def $num_logged_in = fetch( 'content', 'list_count',$fetch_params)}
 
	<div class="box-header">
		<h1>Logged in users between {$view_parameters['startdate']|datetime('custom','%d/%m/%Y')}-{$view_parameters['enddate']|datetime('custom','%d/%m/%Y')} </h1>
	</div>
	<div class="block">
	    {if is_set( $feedback )}
	       <div class="message-warning">
	       	<h2>{$feedback}</h2>
	       </div>
	    {/if}
 
		<p>{$num_logged_in} users last logged in during this period</p>
		<ul>
			{foreach $users as $user}
			<li>
				<a href={$user.url_alias|ezurl()}>
					{$user.name}
				</a>
			</li>
			{/foreach}
		</ul>
	</div>
{/if}

Updating our menu

We now have a template that works so let’s add our view to the left hand menu we created previously. We will work out the first date of the current month and take this as the start date, up until the current day of the month. The code is quite straightforward but look at how we create the address for the new page.

extensions/onequarterenglish/design/admin/templates/full/leftmenu.tpl
...
<ul>
    <li><div><a href={'/userspecific/forgottenpasswords'|ezurl}>Forgotten Passwords</a></div></li>
 
{def $enddate = currentdate()}
{def $startdate = makedate( $enddate|datetime('custom','%n'),1)}
    <li><div><a href={concat('/userspecific/lastlogin/',$startdate,'/enddate/',$enddate)|ezurl()}>Logins this month</a></div></li>
</ul>
...

The URL will come out as we looked at earlier:

http://www.onequarterenglish.co.uk/admin/userspecific/lastlogin/1280617200/enddate/1281901277

Handling user input

The issue with the example we have at the moment is that although we can pass parameters in via the standard eZ Publish URL, we cannot handle form submissions of custom dates. Let’s update our template to allow users to customise the dates and then pass this back to the page.

Updating the template

Add the following code to add a basic form to the bottom of the lastlogin.tpl file:

extensions/onequarterenglish/design/admin/templates/full/lastlogin.tpl
<div class="box-header">
	<h1>Logins between dates</h1>
</div>
<div class="block">
	<form action={$current_url|ezurl()} method="POST">{* $current_url is stored within the module logic*}
		<fieldset>
			<label>Start Date (dd/mm/YYYY): </label>
			<input type="text" name="startdate" value="{$view_parameters['startdate']|datetime('custom','%d/%m/%Y')}" />
		</fieldset>
		<fieldset>
			<label>End Date (dd/mm/YYYY): </label>
			<input type="text" name="enddate" value="{$view_parameters['enddate']|datetime('custom','%d/%m/%Y')}" />
		</fieldset>
		<fieldset>
			<input class="button" type="submit" name="updatedates" value="Update Dates" title="Update Dates' )}"/>
		</fieldset>
	</form>
</div>

Updating the module logic

We now need to update the page logic, we need to make sure we check for a value passed in by the user, and if this doesn’t exist we will use the value submitted through with the parameters (if a value exists). There are a couple of things to look out for in the code:

  • Using eZHttpTool methods hasPostVariable(“variable”) and postVariable(“hasPostVariable”) rather than accessing the Post fields directly.
  • For eZ Publish, we pass in a Unix date value so we need to convert the user input into this format if the user input exists.

The final code

From the last login page, you will now be able to update the date values and the template will update accordingly. Uusing the form variables as you have in the lastlogin.php file above can be extended in any way so whatever you want to do with the form information can be done.

Conclusions

This tutorial should have outlined to you the basics for creating your own admin based extensions for eZ Publish. From this you should be comfortable with extending the system however you need to produce templates for any purpose. Please get in touch if you need any guidance with anything that is covered, or are unsure how you can use this code in your extensions.

Click here to download the final code for this tutorial or get it from Github here.

2 Comments

  1. > //$tpl = templateInit();

    This line will work at least a couple of more versions, just remember to do the include as that will be removed from index.php at some point but the file and function will persist for several versions:
    include_once( ‘kernel/common/template.php’ );

    Same with ezi18n(), just remember to include the file if you use it over 4.3+ syntax ( ezpI18n::tr() )
    require_once( ‘kernel/common/i18n.php’ );

Leave a Comment