Real Content in PHP5 CMS: Part 2

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.

Framework solution

To explore implementation details, we will look at an example that is simple enough to be shown in some detail. It is an application for handling pages composed largely of text and images. After studying the example, we will consider how the application could be made more advanced.

A simple text application

Here, we'll look at a component that can be used on its own but is also intended as a starting point for more sophisticated uses. Its essence is that it handles a piece of text, created using the site WYSIWYG editor by the administrator. The text can be displayed as the main portion of a Web page. Ancillary information is held about the text. Any particular text can be the target of a menu entry, so the component can be used for simple pages. The WYSIWYG editor provides for moderately complex text layout and the inclusion of images.

We shall see that writing a text handling extension is made very much simpler by the various elements of the CMS framework.

The database table for simple text

After the ID number that is used as the main key we have the primary constituents of a piece of text. They are the headline, the subheading, and the body of the article. Each of these will simply reflect whatever text is put in them by the author, who in this simple implementation must also be an administrator.

Next we have a couple of time stamps that can be automatically maintained by the software. Rather obviously, the created time stamp is set when the row is created, and the modified time stamp is set every time the row is updated.

We then have fields that control the publication of the text. First, there is a simple indicator, which is set to zero if the text is not published and is set to one if it is published. When set to unpublished, the indicator overrides the start and end dates, if they are present. If a non-zero start date is set, then the text will not be published before that date. Likewise, if a non-zero finish date is set, the article will cease to be published after that date. Publishing dates are very useful to control when text will appear as it is often helpful to time the start of publication, and it creates a bad impression if obsolete text is not removed.

Then we have data that describes who has worked on the text. The original creator is recorded as a user ID, and the last modifier is likewise recorded as a user ID. These fields are intended for tracking what is happening to the text rather than for display. On the other hand, the byline is entirely for display.

Version is a character field that has no defined structure in this simple component, but could be elaborated in many different ways.

Storage for metadata is provided as keys and description. This information is not for display on the browser page, but is used to generate meta information in the header of a page containing the text. Tags containing metadata can influence search engines used for indexing of pages, although description is much more influential than keywords, which are believed to be largely disregarded.

Finally, a hit counter is automatically maintained by the system, being set initially to zero and then updated every time the text is shown to a site visitor.

A text data object

When a text item is loaded into memory from the database, a class provides for the definition of the object will be created. For the simple text application, the class is:

class textItem extends aliroDatabaseRow
{
protected $DBclass = 'aliroDatabase';
protected $tableName = '#__simple_text';
protected $rowKey = 'id';
public function store ($updateNulls=false)
{
$userid = aliroUser::getInstance()->id;
if ($this->id)
{
$this->modified = date('Y-m-d H:i:s');
$this->modify_id = $userid;
}
else
{
$ this->created = date('Y-m-d H:i:s');
$this->author_id = userid;
}
parent::store($updateNulls);
}
}

Much of the hard work is done in the parent class, aliroDatabaseRow. Because the database framework derives information from the database itself, there is no need to specify the fields that are in the table, which makes it easier to cope with future changes. The minimum that has to be done is to specify the name of the singleton database class, the name of the table (using a symbol in place of the actual prefix), and to define the name of the primary key field.

In this case, the store method is also extended. This provides an easy way to maintain the time stamps on the text. The current user is found through the aliroUser singleton class. We know whether a text row is new from whether it already has a value for id. The correct date and user field can then be updated. Finally, the standard store method in the parent class is invoked.

Administering text items—controller

The administrator logic for handling simple text follows the usual pattern of first providing a list of items, paged if necessary, then allowing more detailed access to individual items, including the ability to edit. Logic for overall control is provided by the aliroComponentAdminManager class, and the aliroComponentAdminControllers class. In fact, we could nominate in the packaging XML aliroComponentAdminManager as the adminclass for our component, since the dedicated textAdmin class does nothing:

class textAdmin extends aliroComponentAdminManager
{
// This could be omitted - included here in case extra
code needs to be added
public function __construct ($component, $system, $version)
{
parent::__construct ($component, $system, $version);
}
// Likewise, this could be omitted unless extra code is needed
public function activate ()
{
parent::activate();
}
}

Why might we want to write a dedicated extension to aliroComponentAdminManager? Well, this is the common entry point for the administrator side of our component, so if we wanted any processing to exist that could affect every use of the component, this is the place to put it. The two possible locations are the constructor and the activation method. The constructor receives information from the CMS environment in the form of a component object (describing this component), the name of the system that is calling us, and its version. It is invoked as soon as the correct component has been determined. The standard processing in the aliroComponentAdminManager constructor includes creating the controller class, and acquiring some common variables from $_REQUEST. Once setup is completed, the activation method is invoked without any parameters. The activate method of the aliroComponentAdminManager class strips any magic quotes, and decides what method to call.

Of course, the framework allows us to construct a component completely differently if we choose. The only constraint is that we must write a class whose name is given in the packaging XML, and provide it with an activate method. But usually it is a lot easier to follow the standard construction, and the bare bones of a new component can be built and downloaded online from http://developer.aliro.org.

Nothing specific has been done yet, and we have to move into the controller code before we can find anything to do with handling text objects. The controller is subclassed from aliroComponentAdminControllers and starts off as shown:

class textAdminText extends aliroComponentAdminControllers
{
private static $instance = null;
// If no code is needed in the constructor,
it can be omitted, relying on the parent class
protected function __construct ($manager)
{
parent::__construct ($manager);
}
public static function getInstance ($manager)
{
return is_object(self::$instance) ? self::$instance :
(self::$instance = new self ($manager));
}
public function getRequestData ()
{
// Get information from $_POST or $_GET or $_REQUEST
// This method will be called before the toolbar method
}
// If this method is provided, it should return true if
permission test is satisfied, false otherwise
public function checkPermission ()
{
$authoriser = aliroAuthoriser::getInstance();
if ($test = $authoriser->checkUserPermission('manage',
'aSimpleText', '*'))
{
if (!$this->idparm) return true;
if ($authoriser->checkUserPermission('edit', 'aSimpleText',
$this->idparm)) return true;
}
return false;
}

Here, the constructor is not needed; it is shown only to indicate the possibility of having code at the point the controller object is created. The constructor receives the manager object as a parameter, in this case an instance of textAdmin, a subclass of aliroComponentAdminManager.

The controller is a singleton class, and here a form of the getInstance method is shown that can be used completely unchanged from component to component.

Then we have two methods that are standard. Neither has to be provided, and in this case, the getRequestData method is not needed since it does nothing. Its purpose is to run early on (it is called before the toolbar processing and well before the processing specific to the current request) to acquire information from $_REQUEST or $_GET or $_PUT (or possibly other super-globals). They can be saved as object properties so as to be available for toolbar construction or other processing. The checkPermission method provides the component with a way to easily control who is able to access its facilities. If the method returns true then the user will be allowed to continue, but if it returns false, they will be refused access. In this example, there is always a check that the user is permitted to manage objects of the type aSimpleText and if a specific one is identified by its ID, then there is a further check that the user is permitted to edit that particular text item.

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:

Next are methods used to construct the toolbar, the set of named buttons that determine what task will be performed by our component. The code is:

// The code that creates the toolbar
public function toolbar ()
{
switch ( $this->task )
{
case 'new':
case 'edit':
$this->toolbarEDIT();
break;
case 'cancel':
case 'save':
default:
$this->toolbarDEFAULT();
break;
}
}
// When editing a text item, the toolbar options are save or cancel
private function toolbarEDIT()
{
// Set up the toolbar for editing
$toolbar = aliroAdminToolbar::getInstance();
$toolbar->save();
$toolbar->cancel();
}
// The default admin page is a list of items
// Toolbar options are (un)publish, add new and delete
private function toolbarDEFAULT()
{
$toolbar = aliroAdminToolbar::getInstance();
$toolbar->publish();
$toolbar->unpublish();
$toolbar->addNew();
$toolbar->deleteList();
}

The value of $this->task reflects which toolbar button has been selected, and it has been set in the standard manager processing. If no button was pressed, the default is list. In this case, there are two quite simple possible toolbars, one for the default list of text items, and one for use when a single item is edited. The aliroAdminToolbar class will construct toolbar entries easily. The toolbar method is called by the framework before the main processing, and now at last we are ready to get down to details! Assuming the component has been built by sub classing the standard framework classes, the methods are derived from the identity of the toolbar button selected (or the default list) simply by taking the name and adding Task. Thus the default is:

// This is the default action, and lists items from the DB, with
page control
public function listTask ()
{
$database = aliroDatabase::getInstance();
// get the total number of records
$database->setQuery("SELECT COUNT(*) FROM #__simple_text");
$total = $database->loadResult();
$this->makePageNav($total);
// get the subset (based on page control limits)
of required records
$query = "SELECT * FROM #__simple_text "
. "n LIMIT {$this->pageNav->limitstart}, {$this->
pageNav->limit}";
$rows = $database->doSQLget($query, 'textItem');
$view = new listTextHTML ($this);
$view->view($rows, $this->fulloptionurl);
}

The list method finds a database object, counts the rows in the database table, and uses the answer to construct a page navigation object. Other parameters for the page navigation object will have already been collected from the user's request by the standard classes.

Then the page navigation object provides assistance in creating a database query to obtain exactly one page of data and the doSQLget database method returns an array of objects of the textItem class. A viewer object is created from the listTextHTML class, passing the controller object (which is the current object). The rest of the class provides the processing for other possible toolbar buttons:

public function cancelTask ()
{
$this->listTask();
}
// Edit an existing text item
public function editTask ()
{
$text = new textItem();
$text->load($this->idparm);
$view = new listTextHTML ($this);
$view->edit($text);
}
// Add a new text item
public function newTask ()
{
$text = new textItem();
$view = new listTextHTML ($this);
$view->edit($text);
}
// Save a new/edited text item
public function saveTask ()
{
$text = new textItem();
if ($this->idparm) $text->load($id);
$text->published = 0;
$text->bind($_POST);
$text->store();
$this->listTask();
}
// A selection of text items can be deleted here
public function removeTask ()
{
$database = aliroDatabase::getInstance();
if (count($this->cid))
{
$cids = implode( ',', $this->cid );
$database->doSQL( "DELETE FROM #__simple_text
WHERE id IN ($cids)" );
}
$this->redirect('index.php?option=com_text',
T_('Deletion completed'));
}
public function publishTask ()
{
$this->changePublished(1);
}
public function unpublishTask()
{
$this->changePublished(0);
}
// This method does the real work for publish and unpublish
private function changePublished ($state=0)
{
$database = aliroDatabase::getInstance();
if (count($this->cid))
{
$new_publish = intval($state);
$idlist = implode (',', $this->cid);
$sql = "UPDATE #__simple_text SET published =
$new_publish WHERE id IN ($idlist)";
$database->doSQL ($sql);
}
$this->redirect('index.php?option=com_text');
}
}

Most are very simple, although a few comments may be helpful. Where an individual text item is involved, either newly created or existing, an object of the textItem class is created. The property $this->idparm will have been set, if possible, by the standard classes. A load method is available for textItem objects, inherited from the aliroDatabaseRow class.

When saving information after an edit, it is important to set any checkbox items, such as $text->published to zero before using the bind method to place user data taken from the $_POST super-global into the text object. This is because when an XHTML checkbox that was previously ticked is cleared, nothing is returned in the request. Checkboxes only return a value when set, and always return a value regardless of whether they were set by the user or as a default in the XHTML.

Another point to bear in mind concerning saving data is that XHTML Purifier might have been called to purify the XHTML submitted by the administrator as the text. This guarantees that it will not contain threats and will conform to standards. A further benefit is that XHTML Purifier irons out inconsistencies in the behavior of editors in areas such as the handling of XHTML entities. The result may still pose some issues, but on the whole the stored text will be consistent, which is extremely helpful for any further processing.

>> Continue Reading Real Content in PHP5 CMS: Part 3

 

[ 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