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 »
The Class Loader
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:
- page_add_header() – Adds an HTTP header to send with the page.
- page_add_link() – Adds an HTML link tag to the page header. Links are used to include related data such as stylesheets or RSS feeds.
- page_add_meta() – Adds an HTML meta tag to the page.
- page_add_script() – Adds a Javascript script to the page.
- page_add_style() – Adds a CSS stylesheet to the page.
- page_below() – Sets the below_page property of the page. This positions the box as a child of a particular page in the site hierarchy.
- page_description() – Sets the description metadata field.
- page_id() – Sets the page ID. Handy for boxes with "fake" pages that alias them.
- page_keywords() – Sets the keywords metadata field.
- page_onblur() – Adds an onblur handler to the page.
- page_onclick() – Adds an onclick handler to the page.
- page_onfocus() – Adds an onfocus handler to the page.
- page_onload() – Adds an onload handler to the page.
- page_onunload() – Adds an onunload handler to the page.
- page_template() – Sets the global template used to render the page.
- page_template_set() – Changes the global template set used to render the page.
- page_title() – Sets the title of the page.
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:
- App name (e.g., Myapp if your app folder is named 'myapp') with the first letter capitalized.
- Folder name (e.g., Contact if your form is in 'forms/contact') also with the first letter capitalized. Please note that if your form is in a sub-folder such as 'forms/contact/techsupport' you would name it MyappContactTechsupportForm, specifying each sub-folder in turn.
- The word 'Form'.
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:
- is 'some value' – The value must be equal to 'some value'
- contains 'some value' – The value entered must contain 'some value'
- regex 'pattern' – The value must match the regular expression defined in the 'pattern'
- equals 'fieldname' – The value must match the value from another form field, useful for verifying email addresses and passwords.
- empty – The field must be empty. This is more often used with the prefix 'not' so you can verify that the field is 'not empty'.
- length 'value' – The value must be the specified length. Some valid length examples are: 6, 6+, 6-12, 12- meaning 6 exactly, 6 or more, between 6 and 12, and 12 or less.
- gt 'value' – The value must be greater than 'value'
- ge 'value' – The value must be greater than or equal to 'value'
- lt 'value' – The value must be less than 'value'
- le 'value' – The value must be less than or equal to 'value'
- func 'function_name' – The value must return true when passed to the specified function. This enables you to easily define custom validation rules.
- unique 'table/column' – The value must not already exist in the specified database table in the specified column. Useful for ensuring usernames are unique, for example, or that an email address isn't used to register for multiple accounts.
- exists 'path/to/directory' – The value must not exist as a file name in the specified folder. This is useful for file uploads to ensure they don't overwrite an existing file.
- numeric – The value must be numeric, no letters.
- email – The value must be a valid email address.
- header – The value must validate for use in an HTTP header. Useful for securing against header injection attacks.
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.
