Real Content in PHP5 CMS: Part 3

Exclusive offer: get 50% off this eBook here
PHP 5 CMS Framework Development

PHP 5 CMS Framework Development — Save 50%

Expert insight and practical guidance to creating an efficient, flexible, and robust framework for a PHP 5-based content management system

£21.99    £11.00
by Martin Brampton | September 2009 | MySQL Content Management Open Source PHP

Read Part One of Real Content in PHP5 CMS.

Read Part Two of Real Content in PHP5 CMS.

Administering text items—viewer

Generating the XHTML is handled in a separate class, thus implementing the principles of the MVC pattern. The viewer class constructor establishes strings for translation in a way that will allow them to be picked up by gettext, as well as invoking the constructor in the parent class basicAdminHTML, which will provide useful methods and also transfer information such as the page navigation object from the controller object passed as a parameter:

class listTextHTML extends basicAdminHTML
{
public function __construct ($controller)
{
parent::__construct($controller);
$lang_strings = array(T_('Simple Text'),T_('Title'),
T_('Byline'),T_('Version'),
T_('Publishing'),T_('Published'),
T_('Start date'),T_('End date'),
T_('Article text'),T_('Metadata'),
T_('Keys'),T_('Description'),
T_('Hits'),T_('ID'));
$this->translations = array_combine(
$lang_strings, $lang_strings);
}

The actual display of a list of text items is then quite simple, involving the creation of a heading first, followed by a loop through the text items, and then some final XHTML including hidden fields that allow for effective navigation. Note that the parent class will have set up $this->optionurl and $this->optionline to help in the construction of links within the component and a hidden variable to identify the component respectively.

public function view ($rows)
{
$mainhtml = $this->listview($rows);
echo <<<ALL_HTML
$mainhtml
<div>
<input type="hidden" name="task" value="" />
$this->optionline
<input type="hidden" name="boxchecked" value="0" />
<input type="hidden" name="hidemainmenu" value="0" />
</div>

ALL_HTML;

}

The view method does very little, relying on the listview method for most of the work, and only adding hidden fields needed to ensure that navigation and the toolbar will work correctly. Note that the parent class helps us by setting $this->optionline with a hidden input field for the critical option variable needed to ensure the correct component is invoked when the form is submitted. Actual XHTML form tags are created by the CMS framework so that every administrator page is a form. The reason for splitting the page creation in this way will become apparent later, when we look at menu creation. So, moving on to the listview method, we find quite a lot of simple code, which is mainly just a definition of the page in XHTML. The second and third parameters will be set differently from their default values when we come to menu creation.

public function listview ($rows, $showlinks=true, $subhead='')
{
$rowcount = count($rows);
$html = <<<ADMIN_HEADER

{$this->header($subhead)}
<table class="adminlist" width="100%">
<thead>
<tr>
<th width="3%" class="title">
<input type="checkbox" name="toggle" value=""
onclick="checkAll($rowcount);" />
</th>
<th>
{$this->T_('ID')}
</th>
<th width="50%" class="title">
{$this->T_('Title')}
</th>
<th>
{$this->T_('Byline')}
</th>
<th>
{$this->T_('Hits')}
</th>
<th align="left">
{$this->T_('Published')}
</th>
</tr>
</thead>
<tbody>

ADMIN_HEADER;

$i = $k = 0;
foreach ($rows as $i=>$row)
{
if ($showlinks) $title = <<<LINK_TITLE

<a href="{$this->optionurl}&amp;task=edit&amp;
id=$row->id">$row->title</a>

LINK_TITLE;

else $title = $row->title;
$html .= <<<END_OF_BODY_HTML

<tr class="row$k">
<td>
{$this->html('idBox', $i, $row->id)}
</td>
<td align="center">
$row->id
</td>
<td>
$title
</td>
<td>
$row->byline
</td>
<td align="center">
$row->hits
</td>
<td align="center">
{$this->html('publishedProcessing', $row, $i )}
</td>
</tr>
END_OF_BODY_HTML;

$i++;
$k = 1 - $k;
}
if (0 == $rowcount) $html .= <<<NO_ITEMS_HTML

<tr><td colspan="6" class="center">
{$this->T_('No items')}
</td></tr>

NO_ITEMS_HTML;

$html .= <<<END_OF_FINAL_HTML

</tbody>
</table>
{$this->pageNav->getListFooter()}

END_OF_FINAL_HTML;

return $html;
}

When it comes to adding a new item or editing an existing one, no looping is required, and the WYSIWYG editor is activated to provide a helpful interface for the administrator who is editing a text item. Note that the use of PHP heredoc allows the XHTML to be written out quite plainly, with the PHP insertions unobtrusive but effective. Actual text for translation is shown in its correct place (in the base language) by using the T_ method that is inherited from aliroBasicHTML via basicAdminHTML.

public function edit ($text)
{
$subhead = $text->id ? 'ID='.$text->id : T_('New');
$editor = aliroEditor::getInstance();
echo <<<EDIT_HTML

{$this->header($subhead)}
<div id="simpletext1">
<div>
<label for="title">{$this->T_('Title')}</label><br />
<input type="text" name="title" id="title" size="80"
value="$text->title" />
</div>
<div>
<label for="byline">{$this->T_('Byline')}</label><br />
<input type="text" name="byline" id="byline" size="80"
value="$text->byline" />
</div>
<div>
<label for="version">{$this->T_('Version')}</label><br />
<input type="text" name="version" id="version" size="80"
value="$text->version" />
</div>
<div>
<label for="article">{$this->T_('Article text')}</label><br />
{$editor->editorAreaText( 'article', $text->article,
'article', 500, 200, 80, 15 )}
</div>
</div>
<div id="simpletext2">
<fieldset>
<legend>{$this->T_('Publishing')}</legend>
<div>
<label for="published">{$this->T_('Published')}</label><br />
<input type="checkbox" name="published" id="published"
value="1" {$this->checkedIfTrue($text->published)} />
</div>
<div>
<label for="publishstart">{$this->T_('Start date')}</label><br />
<input type="text" name="publish_start" id="publishstart"
size="20" value="$text->publish_start" />
</div>
<div>
<label for="publishend">{$this->T_('End date')}</label><br />
<input type="text" name="publish_end" id="publishend"
size="20" value="$text->publish_end" />
</div>
</fieldset>
<fieldset>
<legend>{$this->T_('Metadata')}</legend>
<div>
<label for="metakey">{$this->T_('Keys')}</label><br />
<textarea name="metakey" id="metakey" rows="4"
cols="40">$text->metakey</textarea>
</div>
<div>
<label for="metadesc">{$this->T_('Description')}</label><br />
<textarea name="metadesc" id="metadesc" rows="4"
cols="40">$text->metadesc</textarea>
</div>
</fieldset>
<input type="hidden" name="task" value="" />
$this->optionline
</div>
<div id="simpletext3">
<input type="hidden" name="id" value="$text->id" />
<input type="hidden" name="boxchecked" value="0" />
<input type="hidden" name="hidemainmenu" value="0" />
</div>

EDIT_HTML;

}

Finally, there is a common method to deal with the creation of the heading. It uses the addCSS method provided by the parent class to link to a small amount of CSS that is held in a separate file. Although the list of text items defined in the XHTML above is perfectly legitimate as a table, since it really is a tabular structure, the heading would be better built out of other XHTML elements. The only reason for using a table here is that it is one of the features retained from earlier systems for the sake of backwards compatibility:

private function header ($subhead='')
{
$this->addCSS(_ALIRO_ADMIN_DIR.'/components
/com_text/admin.text.css');
if ($subhead) $subhead = "<small>[$subhead]</small>";
return <<<HEAD_HTML

<table class="adminheading">
<tr>
<th class="user">
{$this->T_('Simple Text')} $subhead
</th>
</tr>
</table>

HEAD_HTML;

}
}

PHP 5 CMS Framework Development Expert insight and practical guidance to creating an efficient, flexible, and robust framework for a PHP 5-based content management system
Published: June 2008
eBook Price: £21.99
Book Price: £30.99
See more
Select your format and quantity:

Showing text to visitors

Actually showing text items to people who visit the site is much easier than building the code to administer them. This time, we will omit unnecessary code. We need a simple manager class to take control from the general framework—its name is defined in the packaging XML.

class textUser extends aliroComponentUserManager
{
public function __construct ($component, $system,
$version, $menu)
{
$alternatives = array ();
parent::__construct ($component, 'task', $alternatives,
'display', T_('Information'),
$system, $version, $menu);
}
}

In this case, there are no alternative task names, so the $alternatives array is empty. We receive information from the general framework as a component object (defining our component), the name of the CMS and its version, and a menu item object if our component has been triggered by an identifiable menu item. The same information is passed to the parent class's constructor, supplemented by the name of the request variable that will be used to control our logic, in this case task, the default for it, which is display, and a default title for use in the browser title bar (in fact, we will override it).

Next, there is a class textUserControllers, which extends aliroComponentControllers but otherwise does nothing. It is only present in case the component develops to a stage where common methods are needed across various controller classes. There is a controller class for each main action, although in this case we have only one action. Thus, since we have determined that the default value of the control variable is display we need a text_display_Controller class (text derives from the name of the component):

class text_display_Controller extends textUserControllers
{
private static $instance = null;
// The controller should be a singleton
public static function getInstance ($manager)
{
return is_object(self::$instance) ? self::$instance :
(self::$instance = new self($manager));
return self::$instance;
}
// The actual value of the control variable
is received as a parameter
function display ($task)
{
$viewer = new HTML_text;
$database = aliroDatabase::getInstance();
$id = $this->getParam($_REQUEST, 'id', 0);
$texts = $database->doSQLget("SELECT *
FROM #__simple_text
WHERE published !=0 AND publish_start < NOW() "
."AND (publish_end = '0000-00-00 00:00:00' OR
publish_end > NOW()) AND id = $id", 'textItem');
if (count($texts)) $text = $texts[0];
else
{
new aliroPage404();
return;
}
$text->hits++;
$text->store();
if ($text->metakey) $this->addMetaTag('keywords',
$text->metakey);
if ($text->metadesc) $this->addMetaTag('description',
$text->metadesc);
$pathway = aliroPathway::getInstance();
$pathway->reduceByOne();
if (!$this->isHome()) $pathway->addItem($text->title);
$this->setPageTitle($text->title);
$viewer->view($text);
}
}

As with the administrator services, the controller is a singleton with a getInstance static method. The related viewer class is HTML_text. The first real action of the controller is to read the required text item from the database, checking that it is published within its publication dates. If nothing is found, then a 404 error is raised, otherwise the number of hits is incremented. The controller has inherited many useful methods, including addMetaTag, which is used to put the metadata into the XHTML header.

The "bread crumb trail" or "pathway" will have the name of the component automatically added, but we do not want that, so it is reduced by one. If this text is forming the home page, then we leave it at that, otherwise add the title of this text. Likewise, the browser title is set to the title of the text item before invoking the viewer class, which is extremely simple:

class HTML_text
{
public function view ($text)
{
echo <<<TEXT_HTML
<!-- Start of HTML for simple text component -->
<div id="simpletext">
<h2>$text->title</h2>
<div>
$text->article
</div>
</div>
<!-- End of HTML for simple text component -->

TEXT_HTML;

}
}

All the hard work was done in creating a text item, and all that is left is to display it! But one other facility remains to be described.

Menu building

To make the simple text component useful, it is important to be able to add text items into a menu. The general framework does not know what detailed information a component may need for the construction of menu entries. Handling only a single entry into the component is inadequate, as this example clearly shows, since we want our menu entries to identify a particular item of text, not simply invoke the component in general. Of course an administrator could build a complete URI and store that, but it is far more helpful if guidance can be given to make menu construction easy.

The framework therefore provides for a component to specify its own menu building class, and the class for the simple text application is (unoriginally) simpleTextMenu. It has to provide a perform method, which is passed an object to carry the cumulative information about choices made to define a menu entry. In this case, the issue is quite simple: we want the administrator to select a particular text item for display. So the perform method calls a method according to the stage reached, or raises an error:

class simpleTextMenu extends aliroFriendlyBase
{
public function perform ($mystuff)
{
$method = 'processStage'.$mystuff->stage;
if (method_exists($this, $method))
$this->$method($mystuff);
else trigger_error(T_('Invalid stage
indicator in Simple Text
menu creation class'));
}

The first stage is where we set up the options for the administrator to select a text item, and the logic is necessarily almost identical to the preparation of data in the administrator controller listTask method. (It might be worth considering relocating most of the duplicated logic into a static helper method in the textItem class). Now, we can see why the viewer was written as it was, as its listview method can be used to display the list of text items. In this case, we do not want the hidden fields, since setting up the navigation is the responsibility of the menu manager which has invoked this method. All that is needed here is the XHTML to define the choices for the menu item and to offer page control links. To help with this, the menu manager passes its own controller object for use here:

private function processStage0 ($mystuff, $controller)
{
// Initial entry - user must choose an article
// get the total number of records
$database = aliroDatabase::getInstance();
$database->setQuery("SELECT COUNT(*)
FROM #__simple_text");
$total = $database->loadResult();
$controller->makePageNav($total);
// get the subset (based on limits) of required records
$query = "SELECT * FROM #__simple_text "
. "n LIMIT {$controller->pageNav->limitstart},
{$controller->pageNav->limit}";
$rows = $database->doSQLget($query, 'textItem');
$view = new listTextHTML($controller);
$mystuff->html = $view->listview($rows, false,
T_('Please choose a text item: '));
$mystuff->link = '';
$mystuff->stage = 1;
$mystuff->save = false;
$mystuff->finished = false;
}
private function processStage1 ($mystuff, $controller)
{
// Text now chosen - finished
$cid = $this->getParam($_REQUEST, 'cid', array(0));
$id = isset($cid[0]) ? $cid[0] : 0;
$text = new textItem();
$text->load($id);
if ($text->id)
{
$mystuff->link = "index.php?option=
com_text&task=display&id=$text->id";
$mystuff->xmlfile = '';
$mystuff->name = $text->title;
$mystuff->html = $this->menuSummary($mystuff);
}
else $this->processStage0($mystuff);
}
private function menuSummary ($mystuff)
{
$mystuff->stage = 99;
$mystuff->finished = true;
$mystuff->save = true;
return 'Finished - nothing to display - this is an
error condition';
}
}

In the second stage (that is to say, Stage 1), the given ID is checked for validity, and the administrator is returned to the first stage if the check fails. Provided it succeeds, final values are set including the required menu link and control is returned to the framework. By this means our component is able to interact with the administrator to create a menu link, and the complexity of the interaction can be increased if further options are needed. The menu manager does not need to know about the details of menu link construction, something it cannot do for extensions not yet written. Instead, it connects with the menu class in the extension to achieve maximum flexibility.

Simple developments of text items

As it stands, the text component is useful. It is suitable for creating text pages, exploiting the capabilities of WYSIWYG editors, and relying on the publication controls that ensure that material is only shown when relevant.

We have not used the "byline" which tells us the public name of the author of the text, or the dates of origination or modification. These could be added, either in a standard format or in a format that is controlled by parameters set by the administrator. Or another alternative would be to use a simple template for each item, so as to define the relative position of each available element. This might be supplemented by a default template, set at the component level for all items.

Nor have we used the version field for anything. It was deliberately left as a large variable length character field rather than a number so as to be capable of handling a variety of versioning mechanisms, not just a simple number. Controlling versions of an item is a major topic in its own right, and would really count as an advanced development of the simple system. Providing for the recording of a version identifier as part of the administration of the system would be a much simpler development.

Another useful extension would be to use the framework's RBAC system to determine who can edit the items. With that in place, it might make sense to provide for editing on the user side of the site, not just for administrators. Similar code to that shown above would need to be deployed within the user related classes.

A substantial extension would be achieved by adding a "tagging" system whereby each item could be "tagged" with one or more attributes. This provides many more possibilities for organizing the text items or searching for them. It would be feasible to adopt a filtering approach whereby an initial search list is refined by excluding items that do not have particular tags. The tagging could also be used as a basis for extending the access controls.

Building advanced text systems

Going beyond basic additions to the simple text system, it is easy to envisage possibilities such as the creation of pages that make use of multiple text items or incorporate other elements. An obvious way of combining text items would be a blog. This would benefit from the tagging mechanism mentioned above and would typically combine texts or portions of texts for the initial presentation of a particular blog.

Another application would be a product catalogue where the information might include one or more pictures of a product, a tabular specification, and one or more text items. These various elements can be combined by using an internal template.

Building an online magazine or newspaper would be another way in which text items could be combined into larger units. In this case, it is quite likely that the access control system would be used more fully to cater for a variety of roles in the creation, editing, and publication of material.

These are merely suggestions of a few directions. The possibilities are endless, and no single mechanism will ever cater for all website possibilities. A framework is inevitably a compromise, and concentrates on providing a good range of basic capabilities to ease the development of more specific services.

Summary

When it comes to dealing with the actual content that defines a website, the possibilities are limited only by our imaginations.

We have reviewed the main categories of content provision, including brief discussions of the issues that arise with them. More obscure categories were briefly considered.

The workings of a simple text handling system have been investigated in detail so that we can see how content management works out in practice. It still takes a significant amount of code to achieve this simple system, a fact that illustrates the theme of concern over the productivity of the Web development process.

But the simple text system provides a working base and we have looked at a number of relatively easy developments that could make it more useful for the solution of particular problems. We also concluded with indications of more advanced developments that would incorporate the basic system while building much more extensive capabilities. We finally have some content in our content management system!

[ 1 | 2 | 3 ]
PHP 5 CMS Framework Development Expert insight and practical guidance to creating an efficient, flexible, and robust framework for a PHP 5-based content management system
Published: June 2008
eBook Price: £21.99
Book Price: £30.99
See more
Select your format and quantity:

About the Author :


Martin Brampton

Now primarily a software developer and writer, Martin Brampton started out studying mathematics at Cambridge University. He then spent a number of years helping to create the so-called legacy, which remained in use far longer than he ever expected. He worked on a variety of major systems in areas like banking and insurance, spiced with occasional forays into technical areas such as cargo ship hull design and natural gas pipeline telemetry.

After a decade of heading IT for an accountancy firm, a few years as a director of a leading analyst firm, and an MA degree in Modern European Philosophy, Martin finally returned to his interest in software, but this time transformed into web applications. He found PHP5, which fits well with his prejudice in favor of programming languages that are interpreted and strongly object oriented.

Utilizing PHP, Martin took on development of useful extensions for the Mambo (and now also Joomla!) systems, then became leader of the team developing Mambo itself. More recently, he has written a complete new generation CMS named Aliro, many aspects of which are described in this book. He has also created a common API to enable add-on applications to be written with a single code base for Aliro, Joomla (1.0 and 1.5) and Mambo.

All in all, Martin is now interested in many aspects of web development and hosting; he consequently has little spare time. But his focus remains on object oriented software with a web slant, much of which is open source. He runs Black Sheep Research, which provides software, speaking and writing services, and also manages web servers for himself and his clients.

Books From Packt

Drupal 6 Site Blueprints
Drupal 6 Site Blueprints

Solr 1.4 Enterprise Search Server
Solr 1.4 Enterprise Search Server

Alfresco 3 Enterprise Content Management Implementation
Alfresco 3 Enterprise Content Management Implementation

Plone 3 Theming
Plone 3 Theming

Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs
Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs

Scratch 1.4: Beginner’s Guide
Scratch 1.4: Beginner’s Guide

WordPress 2.7 Cookbook
WordPress 2.7 Cookbook

PHP Team Development
PHP Team Development

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software