Elefant PHP Content Management System

Meet Sitellite's successor: Elefant CMS

A modern PHP framework and content management system based on the improved features of PHP 5.3+. Elefant is an extremely fast and easy to use CMS that inherits all the best of Sitellite, without the fat. Learn more »


SimpleTemplate in Depth

What is Sitellite?

Sitellite is a website content management system (CMS), which provides features for creating, publishing and managing content on a website.  A CMS provides the system for the various roles involved in the website lifecycle to come together, including the programmers, designers, administrators and content creators. 

Main Components

Sitellite Application Framework

At the core of Sitellite is a standard library of over 100 PHP classes that provide generic, reusable components for building any type of web application.  This includes a database abstraction layer, template libraries, a class loader, form handling, and more.

The Content Server

The content server is the publishing component of Sitellite, which controls access to content such as web pages or news stories, as well as to applications (apps) written on top of the Sitellite framework, and their components (boxes, forms, libraries, collections).  The content server is in charge of parsing a visitor request, determining what they are requesting, and how to display it (design or output templates).

The Content Manager

The content manager is the GUI used b editors and administrators to update the web site content, as well as to access additional apps written on top of the framework.

Model-View-Controller, Sitellite style

Sitellite was designed as a platform for deploying multiple web-based applications with a high degree of compatibility and integration between each of them. We realized that you can look at any website as a collection of applications, from its news section, to user comments, to a discussion forum, search, blog, contact forms, or anything else. But each of these has to come together to make up a consistent visitor experience.

This is where many pure development frameworks fall short, and provides the central distinction of the Sitellite development model, or what we call extended MVC (MVC2). At its core, a CMS is an implementation of the MVC usage pattern. The content server is the controller, the output templates are the view, and the apps and content are the model.

Where we extend this is that each application also implements the MVC pattern on top of this, allowing the same models, views, and controller to be shared among multiple apps, while still allowing for maintainable, organized code within each app.

MVC inside a Sitellite application

There are several different components that make up a complete application. In terms of the actual source code, Sitellite splits this into 3 different areas:

  • Libraries – These go in the 'lib' folder. They make up the abstraction layer between the database and the controller code. In MVC terms, these are the Model.
  • Boxes – These go in the 'boxes' folder. A box is essentially just a PHP script that takes the visitor request parameters, calls the appropriate methods from the Model, and compiles the results using a template. While Sitellite's content server acts as the central Controller, each box acts as a mini Controller itself.
  • Forms – Forms go in the 'forms' folder. They act very much like boxes, but are limited to form handling (display, validation, results processing). Sitellite automatically includes the necessary form classes from SAF to make coding them a bit faster. Forms can be considered nother form of a Controller, like the boxes.

After the source code itself, an app also consists of a few additional components:

  • Templates – These go in the 'html' folder. In Sitellite there are global templates for the site design, and application-specific templates for rendering the generic output of the application (independent of display style/CSS). The app-specific templates use the .spt file extension and use a template language called SimpleTemplate which is better suited to app development. Global templates use an XML-based template format called XT, which are better suited to design output.
  • Configurations – These go in the 'conf' folder. Configuration info includes instructions for how Sitellite should handle the app, as well as settings that customize the app for a particular site.
  • Data – These go in the 'data' folder. Data would include file uploads associated with usage of the app. Each app may maintain its own storage folder, or can integrate with Sitellite's built-in Web Files document repository or the Image Manager feature in Sitellite. This depends on the needs of each particular app.
  • Translations – These go in the 'lang' folder. Sitellite stores translations of content separately, but individual text strings in an app also need translating and Sitellite provides a means of doing that as well, and these are stored in the 'lang' folder. It's easy to make applications multilingual in Sitellite.
  • Install data – These go in the 'install' folder. This would include any custom content types defined in the app, the database schema for the app, scheduled tasks to be installed in the scheduler, etc.

As you can see, there's a lot more than just a model, view and controller to a real-world application. But Sitellite takes care of each of these in a standardized way across each app, which makes it easier for everything to work together nicely.

But enough with the theory, let's get into some practical examples. 

Exercise: Install the Sitellite CMS on your workstation so you can work through the examples in this lesson. Describe any challenges you ran into during the installation process.

Sitellite Installation Guides

Here are a few guides to help you get Sitellite installed on your desktop.

Sitellite on Windows Setup Guide

Sitellite on Mac OS X Setup Guide

Sitellite Installation Guide

Directory structure

boxes
conf
data
docs
forms
html
install
lang
lib
pix

Exercise: Create a blank application directory in the correct folder of your Sitellite installation, and describe the use of at least two of these folders. Name the app folder itself 'myapp'.

Creating a box

<?php

echo 'Hello ' . $_GET['name'];

?>
sitellite_access = public
sitellite_status = approved
sitellite_action = on

You should now be able to call your box from the browser like this:

http://www.example.com/myapp-helloworld-action?name=Me

For the sake of not repeating information in too many places, here's a tutorials that covers Sitellite's URL structure and what each component of it means.

Our first template

<p>Hello {name}</p>
<?php

echo template_simple ('helloworld.spt', $_GET);

?>

Taking stock

boxes
    helloworld
        access.php
        index.php
conf
data
docs
forms
html
    helloworld.spt
install
lang
lib
pix

SimpleTemplate basics

You can pass either an array or an object in the second parameter of the template_simple() function call.  The items or properties of this parameter are then made available to the template by name using curly braces to denote insertion points.  For example, {name} in the above example refers to $_GET['name'].

SimpleTemplate files have several more powerful features which we'll explore later, including looping and conditions (if statements). 

Global objects

Sitellite makes a number of global objects available to you to make certain programming tasks easier.  These can be called in your boxes, libraries, or even in your templates.  The main objects include:

$cgi

Contains the GET and POST variables sent by the current box or form.  Also allows you to do some input validation on user-submitted data and other routines related to request handling.

$db

A database connection object.  This allows you to send and receive data from the underlying MySQL database, and to connect to additional databases as needed as well.

$intl

A language translation object.  This allows you to add multilingual features to your apps.

$loader

A class and box loading object.  This allows you to import new class libraries from SAF or from any app, and also allows you to call boxes and forms from  one another.

$menu

Contains a tree structure of the pages of the website.  Can be used to build dynamic navigational menus.

$page

Contains the current web page data.  Allows you to view and set new page properties, including the global templates used for the current request.

$session

A session authentication object.  This contains the current user's profile and access rights.  You can also set temporary session data here, such as a list of shopping cart items.

$site

Contains the directory and URL information for the website and the current web page request.

Some of these objects also provide functions to access them, such as $db, $page, and $session.  Others have to be imported into the box's namespaces before they can be used, since each box is executed in its own namespace separate from the global one.  This helps eliminate conflicts and side-effects between boxes.

Accessing global objects

<?php

// get the names list from the session data
$names = session_get ('names');

// if there's no list yet, create a blank one
if (! is_array ($names)) {
    $names = array ();
}

// add the new name and save it to the session data
$names[] = $_GET['name'];
session_set ('names', $names);

// now we'll create a data array to pass to the template,
// since we're passing it the $names list as well as the
// current name
$data = array (
    'name' => $_GET['name'],
    'list' => $names,
);

// call the template and pass it $data this time
echo template_simple ('helloworld.spt', $data);

?>
<p>Hello {name}</p>

<p>I've said hello to:</p>

<ul>
{loop obj[list]}
    <li>{loop/_value}</li>
{end loop}
</ul>
If you refresh the browser window and change the "?name=Tom" parameter each time, you should see a history of all the names sent from this point on.

Setting page properties

<?php

// get the names list from the session data
$names = session_get ('names');

// if there's no list yet, create a blank one
if (! is_array ($names)) {
    $names = array ();
}

// add the new name and save it to the session data
$names[] = $_GET['name'];
session_set ('names', $names);

// now we'll create a data array to pass to the template,
// since we're passing it the $names list as well as the
// current name
$data = array (
    'name' => $_GET['name'],
    'list' => $names,
);

// set the page title
page_title ('Hello World Examples');

// call the template and pass it $data this time
echo template_simple ('helloworld.spt', $data);

?>

The other properties of the page that can be altered include:

The output of the box technically becomes the $page->body value used in the global design templates.

You can find the complete list of functions, properties and methods for each object above in Sitellite's API reference here: 

Sitellite API Reference

Exercise: Use one of the other global objects mentioned above to add a new feature to the 'helloworld' box. Upload the modified PHP code. Feel free to add the modified helloworld.spt file as a comment to the journal entry as well.

The MailForm library

Sitellite's form handling is provided by the MailForm libraries in SAF. MailForm handles generating forms based on simple INI-formatted files that define the form "widgets" (MailForm's name for fields) and a PHP script that does the handling.

MailForm has several built-in features for preventing spambot abuse, and also takes care of the input validation and translation issues automatically, including many built-in validation rules as well as the ability to create custom validation rules as PHP functions.

A simple contact form

[Form]

message = Please use this form to contact us.

[name]

type = text
alt = Your Name

[email]

type = text
alt = Email Address
rule 1 = "not empty, You must enter your email address."
rule 2 = "email, Your email address does not appear to be valid."

[message]

type = textarea
alt = Comments/Questions
labelPosition = left

[submit_button]

type = submit
setValues = Send

You'll also want to copy the 'access.php' file from the 'helloworld' box into your 'contact' folder as well. Forms need the same access settings as boxes.

Breaking down the above file, you'll see that it's divided into blocks using the square braces to denote block names.  It always starts with the [Form] block, which defines properties of the form itself, followed by subsequent blocks, each of which represents a single form field or widget.

Types 

Each widget must be given a type.  The types correspond to the list of available widgets in the Sitellite API references, and you can also make custom types and refer to them in individual apps.  For example, Sitellite's built-in WYSIWYG editor, called "xed" exposes a widget which can be called like this:

type = xed.Widget.Xeditor

This tells Sitellite to look in the 'xed' app's libraries for a Widget/Xeditor.php file where it should find a widget of the type 'xeditor'.  MailForm handles all the class loading behind-the-scenes, so you never have to worry about that in your form code.

Alt 

The alt setting defines the text to be displayed next to the field itself.  If not set, this will default to the widget name capitalized.  For example "widget_name" would become "Widget Name" if no alt setting is specified.

Rules

You can also see in the email field that two rules are defined for it.  The first tells MailForm that the field can't be empty, and the second puts it through some tests to try to make sure it's a valid email address.

You can have as many rules for each widget, and we'll look at some more of the rule types a bit later.  For now, it's enough to know that they take the form "rule # = rule, Error message."

<label for="name">Your Name</label>
<input type="text" name="name" />

The index.php file

<?php

class MyappContactForm extends MailForm {
    function MyappContactForm () {
        parent::MailForm (__FILE__);
    }

    function onSubmit ($vals) {
        // let's output the form data to the user
        echo 'You entered:<br /><br />';

        foreach ($vals as $key => $value) {
            echo $key . ' = ' . $value . '<br />';
        }
    }
}

?>

Sitellite knows to automatically import the MailForm library when a form is called, so you don't have to explicitly import it yourself.  In a box, you would need to first say:

loader_import ('saf.MailForm');

So the first thing to do is define a new class for our form.  Form classes are also automatically instantiated and called by Sitellite, but this means you need to name your form classes in a way that Sitellite expects.   The format of a class name is as follows:

You class extends the MailForm class so that it can inherit all the functionality it needs to draw your form.

Next, you need to create a constructor method, which is a function named the same thing as your class name.  In more complex forms, you might put some additional processing here, but for now all this needs to call is:

parent::MailForm (__FILE__);

This tells MailForm where your class is, so it can find and parse your settings.php file for you automatically.

The last thing your class needs is an onSubmit() method, which will be given an associative array of the form data and does something with the results.  In this case, it simply displays them back for the visitor. 

Viewing the results

You're now ready to view the form you just created.  To load the form from your browser, go to:

http://www.example.com/myapp-contact-form

You should see your form appear there (changing 'www.example.com' to your own site address).  Feel free to try it out and see the results. 

Exercise: Modify the onSubmit() form handler to send you an email using PHP's mail() function when the form is submitted. You can upload your finished changes here.

A closer look at validation rules

Let's take a closer look at the input validation capabilities of MailForm.  As we saw before, validation rules take the following format:

rule # = "rule, Error message."

The rule itself has its own format as well, and there are many different types of rules you can use out-of-the-box.  These are:

You can also specify 'not' in front of any rule to change the rule to its opposite.  For example:

rule 1 = not empty, This field cannot be empty.

rule 2 = not numeric, This field must contain letters AND numbers.

rule 3 = "not equals 'other_field', This field must not be the same as other_field."

Custom validation rules

<?php

function myapp_rule_username_lowercase ($vals) {
    if ($vals['username'] != strtolower ($vals['username'])) {
        // the username must be lower case
        return false;
    }
    // the username is correct
    return true;
}

?>

To call this function as a rule, you would say:

rule 1 = "func 'myapp_rule_username_lowercase', Your chosen username must be lowercase."

Not necessarily the most useful example, but you could easily extend this for credit card format validation, ensuring dates are correct (ie. they didn't enter 1492/25/64), and so on.

Exercise: Add a custom validation rule to your form and call it via your settings.php file.

SimpleTemplate is one of two template engines in Sitellite. The other, XT, is an XML-based template language used to render the design templates for the web site. SimpleTemplate, on the other hand, is used for smaller output, such as the formatting of search results or news stories. The reason for the two, instead of the usual one-size-fits-all template language which is what most frameworks offer, is that we were able to better tailor each for its intended use.

SimpleTemplate allows you to create small templates fast. Its syntax is not overly complex, and it has none of the XML verbosity that XT does. This makes it ideal for programmers to use to format the output of database queries and other user interface needs.

The basics

As we've seen from the above examples, SimpleTemplate templates are saved to '.spt' files in the 'html' folder of each app.

The basic tags that can be used in the template correspond to the key names or property names of the associative array or object passed to the template_simple() function. So if the array contains the keys 'id', 'name', and 'description', then the main tags are {id}, {name}, and {description}.

Loops

<ul>
{loop obj[items]}
    <li>{loop/_value}</li>
{end loop}
</ul>

The obj[items] means "loop through the array stored in the 'items' key of the array passed to the template". 'obj' represents the array or object passed to the template. Similarly, if we passed an object to the template, obj[items] would become obj.items. These are both a shorthand way of saying $obj['items'] and $obj->items respectively, which makes it easier to read within a template.

We refer to the current loop item via {loop/property} where 'property' is some property of the current loop item. {loop/_value} is one of several properties available in any SimpleTemplate loop, because they are created by SimpleTemplate itself. These include:

Conditions

{if obj[some_value]}
<p>{some_value}</p>
{end if}
{if obj[some_value]}
<p>{some_value}</p>
{end if}
{if else}
<p>No value.</p>
{end if}
{if obj.property eq 'foo'}
<p>{property}</p>
{end if}

{if obj.property gt 5}
<p>{property}</p>
{end if}

{if not empty (obj.property)}
<p>{property}</p>
{end if}

Multiple loops

{loop obj[people]}
<h2>{loop/name}</h2>

<ul>
    {loop loop.cities}
        <li>{loop/_value}
            {if loop._value eq parent.city}- Current{end if}
        </li>
    {end loop}
</ul>
{end loop}

Output filters

Default - passed to htmlentities_compat():
{some_value}

Using an alternate function:
{filter strtoupper}{some_value}{end filter}

Using multiple filters - evaluated outward, so nl2br is first:
{filter strtoupper/nl2br}{some_value}{end filter}

Using an alternate filter on multiple tags:
{filter strtoupper}
<h1>{name}</h1>
<p>{description}</p>
{end filter}

Disabling all filters:
{filter none}{some_value}{end filter}

Filter shorthand for a single item:
{some_value|strtoupper}

Filter shorthand with multiple filters - evaluated outward again:
{some_value|nl2br|strtoupper}

Additional tags

Beyond looping, conditions and output filtering, SimpleTemplate has a number of additional features for template developers, listed with examples below.

{exec} and {php}

{php obj[some_value]}

{exec obj[some_value] += 1}

{intl}

{intl This text can be translated.}

This text cannot.

{alt}

{alt odd even}

<table>
{loop obj[items]}
    <tr class="{alt/next}">
        <td>{loop/_key}</td>
        <td>{loop/_value}</td>
    </td>
{end loop}
</table>

Boxes

Output a dynamic breadcrumb menu from the 'sitellite' app:
{box sitellite/nav/breadcrumb}

Output the helloworld box from our previous examples:
{box myapp/helloworld?name=Joe}

Output the standard Sitellite editing buttons for the web view:
{box cms/buttons?collection=myapp_listing&object=[obj]}

Registering multiple objects

<?php

loader_import ('myapp.Objects');

$product = new Product ($_GET['id']);

$category =& $product->getCategory ();

template_simple_register ('category', $category->makeObj ());

template_simple ('register_test.spt', $product->makeObj ());

?>
<p>Product name: {name}</p>
<p>Category name: {category/name}</p>