Anatomy of a typical page

21 December 2017 | 29 views | Tags: Documentation

Before diving into an explanation of Tuskfish subsystems it may be useful to step through the execution of a simple page. This will show the sequence of events and help place the subsystems in context.

So, let's take index.php as our example and step through it. This page presents a mixed stream of teasers for the most recent content on your site. It has pagination controls that can step back through the entire content of the site, and it can be filtered by tag.

// Enable strict type declaration.
  • Enable strict type checks on method parameters. This declaration must be the first line on every PHP script. Although type checks are present in all Tuskfish methods, they are not honoured unless the script calling them has made this declaration (the declaration applies to the calling script).
// Boot!
require_once "mainfile.php";
  • Defines the root path, trust path and site URL constants.
  • Defines the path to the Tuskfish library and configuration file.
  • Includes the configuration file, which in turn:
  • Defines all other path constants.
  • Sets the default language file.
  • Sets the custom class auto loader.
  • Defines the site salt.
  • Defines the site security key.
  • Defines the database name.
require_once TFISH_PATH . "tfish_header.php";
  • Initialise output buffering (with compression).
  • Include the HTMLPurifier library.
  • Set error reporting and custom error handler.
  • Establish connection to database.
  • Include the core language files.
  • Make site preferences available by instantiating $tfish_preference.
  • Initialise a PHP session.
  • Set default page-level metadata variables.
  • Instantiate the template object, so that it will be available globally.
  • Check if site is closed, if so redirect to the login page and exit.
// Get the relevant handler for retrieving content objects.
$content_handler = 'TfishContentHandler';
  • The handler will be used to retrieve and manipulate content objects. TfishContentHandler is the base handler type and can handle any kind of content. Use it when you need to manipulate mixed content types. Each content type also has a content-specific handler (eg. TfishArticle has TfishArticleHandler). If you only want to work with a single content type then use its specific handler.
// Specify theme, otherwise 'default' will be used.
$tfish_template->setTheme = 'default';
$target_file_name = 'index';
$index_template = 'single_stream';
  • If you want to assign a different theme to this page, then change 'default' to the name of the relevant theme, as defined by its directory name.
  • $target_file_name is the page that the pagination control links should point to, without the file extension.
  • $index_template is the name of the template file in the active theme directory that should be used to display this page. 'single_stream' displays a mixed feed of the latest content on your site in index view.
// Validate input parameters.
$clean_id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$clean_start = isset($_GET['start']) ? (int) $_GET['start'] : 0;
$clean_tag = isset($_GET['tag_id']) ? (int) $_GET['tag_id'] : 0;
  • You should always validate input parameters to ensure they are of the type your script is expecting to handle. Tuskfish components validate their input independently of one another, so if you forget to validate input, or make a mistake, the parameters should - in theory - be validated again when they reach the next layer of the application. But don't rely on it - Tuskfish policy is that you validate input at every level.
  • id is the ID of a particular content object. If it is set, then that object will be retrieved and its full description displayed (single object view). If it is not set then a list of the most recent content will be displayed as teasers (index view).
  • start is used for the pagination control. If you are paging through the index page it determines which content object the list should start from (ie. it is the offset). The number of content objects displayed is set by the User side pagination preference.
  • tag_id is an optional parameter that specifies that the index should only display content labelled with a particular tag.
// Set cache parameters.
$basename = basename(__FILE__);
$cache_parameters = array('id' => $clean_id, 'start' => $clean_start, 'tag_id' => $clean_tag);
  • These parameters are used to set the file name of the cached copy of this page view, so that it can be retrieved next time it is requested.
// Make tag-specific RSS feeds available.
$rss_url = !empty($clean_tag) ? TFISH_RSS_URL . '?tag_id=' . $clean_tag : TFISH_RSS_URL;
  • Generate an RSS link, either for the whole site (if tag_id is not set) or for a particular tag.
// Business logic starts here!

// Retrieve a single object if an ID is set.
if ($clean_id) {

    // Retrieve target object.
    $content = $content_handler::getObject($clean_id);
  • If the id parameter was set, retrieve the matching content object (if it exists) and display its full description (single object view).
// Update view counter and assign object to template. Only increment counter for non-downloadable objects.
if ($content->type != 'TfishDownload' && !($content->type == 'TfishCollection' && $content->media)) {
    $content->counter += 1;
  • If a single object is being viewed, update its view counter, unless it is a download object or a collection with attached media (in which case the counter is only incremented when the actual media file is downloaded via enclosure.php).
// Check if cached page is available.
TfishCache::checkCache($tfish_preference, $basename, $cache_parameters);
  • If a cached copy of this page is found, execution of this script stops and the cached copy is returned. If there isn't a cached copy then execution continues.
// Assign content object to template.
$tfish_template->content = $content;

  • Assign the content object to $tfish_template in preparation for display.
// Prepare meta information for display.
$contentInfo = array();
if ($content->creator) $contentInfo[] = $content->escape('creator');
if ($content->date) $contentInfo[] = $content->escape('date');
if ($content->format)$contentInfo[] = '.' . $content->escape('format');        
if ($content->file_size) $contentInfo[] = $content->escape('file_size');

// For a content type-specific page use $content->tags, $content->template
if ($content->tags) {
    $tags = $content_handler::makeTagLinks($content->tags, false);
    $tags = TFISH_TAGS . ': ' . implode(', ', $tags);
    $contentInfo[] = $tags;
$tfish_template->contentInfo = implode(' | ', $contentInfo);

// Set page meta title and description using values from this content object.
if ($content->meta_title) $tfish_metadata->title = $content->meta_title;
if ($content->meta_description) $tfish_metadata->description = $content->meta_description;
  • Prepare various metadata fields for display.
// Check if has a parental object; if so display a thumbnail and teaser / link.
if (!empty($content->parent)) {
    $parent = $content_handler::getObject($content->parent);

    if (is_object($parent) && $parent->online) {
        $tfish_template->parent = $parent;

  • If this content object has a parent object designated, then the details of the parent will be displayed below its description as a teaser.
// Initialise criteria object.
$criteria = new TfishCriteria();
$criteria->order = 'date';
$criteria->ordertype = 'DESC';

// If object is a collection check if has child objects; if so display teasers / links.
if ($content->type == 'TfishCollection') {
    $criteria->add(new TfishCriteriaItem('parent', $content->id));
    $criteria->add(new TfishCriteriaItem('online', 1));
    if ($clean_start) $criteria->offset = $clean_start;
    $criteria->limit = $tfish_preference->user_pagination;

// If object is a tag, then a different method is required to call the related content.
if ($content->type == 'TfishTag') {
    if ($clean_start) $criteria->offset = $clean_start;
    $criteria->limit = $tfish_preference->user_pagination;
    $criteria->tag = array($content->id);
    $criteria->add(new TfishCriteriaItem('type', 'TfishBlock', '!='));
    $criteria->add(new TfishCriteriaItem('online', 1));
  • If this object is a collection or a tag, then its child/member content objects will be displayed as a list of teasers below its description.
// Prepare pagination control.
if ($content->type == 'TfishCollection' || $content->type == 'TfishTag') {
    $first_child_count = TfishContentHandler::getCount($criteria);
    $tfish_template->collection_pagination = $tfish_metadata->getPaginationControl(
        $first_child_count, $tfish_preference->user_pagination, $target_file_name,
        $clean_start, 0, array('id' => $clean_id));

    // Retrieve content objects and assign to template.
    $first_children = TfishContentHandler::getObjects($criteria);
    if (!empty($first_children)) {
        $tfish_template->first_children = $first_children;
  • A pagination control for child/member content objects will also be displayed, if necessary.
        // Render template.
        $tfish_template->tfish_main_content = $tfish_template->render($content->template);
    } else {
        $tfish_template->tfish_main_content = TFISH_ERROR_NO_SUCH_CONTENT;

// End single object view!
  • Assign the content object to $tfish_template and render the template for display.
// Otherwise retrieve an index page list of teasers. Begin index display!
else {
  • If an id was not set, then display a list of the latest content as teasers (index view). This can optionally be filtered by tag, if a tag_id was set.
// Check if cached page is available.
TfishCache::checkCache($tfish_preference, $basename, $cache_parameters);
  • Check if a cached version of the page is available. If so, the script stops executing here and the cached version is returned. Otherwise the script continues to run.
// Page title, customise it as you see fit.
$tfish_template->page_title = TFISH_LATEST_POSTS;
  • Set the page title to "Latest posts".
// Exclude static pages, tags and blocks from the index page.
$criteria = new TfishCriteria();
if ($clean_start) $criteria->offset = $clean_start;
$criteria->limit = $tfish_preference->user_pagination;
if ($clean_tag) $criteria->tag = array($clean_tag);
$criteria->add(new TfishCriteriaItem('type', 'TfishTag', '!='));
$criteria->add(new TfishCriteriaItem('type', 'TfishStatic', '!='));
$criteria->add(new TfishCriteriaItem('type', 'TfishBlock', '!='));
$criteria->add(new TfishCriteriaItem('online', 1));
  • Initialise a TfishCriteria object, which will hold conditions that we want to apply to the query to retrieve content objects for display.
  • Set the offset (which object to start from or $clean_start) and $limit (how many to retrieve) according to pagination requirements and the User-side pagination preference (user_pagination).
  • Filter the results by tag id ($clean_tag), if set.
  • Exclude tags, static pages and blocks from the result set.
  • Only include content that is marked as online.
// Prepare pagination control.
$count = $content_handler::getCount($criteria);
$tfish_template->pagination = $tfish_metadata->getPaginationControl($count,
$tfish_preference->user_pagination, TFISH_URL, $clean_start, $clean_tag);
  • Count the number of records that meet the query conditions ($criteria). This value is needed to set up the pagination control.
  • Prepare the pagination control for display. The number of objects to display on one page in index view is set by the User-side pagination preference. The object to start from is determined by $clean_start. Together these two values are used to calculate the agination control links.
  • By default the pagination links will point back at your home page (index.php) but by overriding TFISH_URL you can point them at another page (for example if this was a page with some other file name).
  • The pagination control can also filter results by tag, if you pass in $clean_tag, which happens if someone uses the tag select box to filter the page.
// Retrieve content objects and assign to template.
$criteria->order = 'date';
$criteria->ordertype = 'DESC';
$content_objects = $content_handler::getObjects($criteria);
  • Add a couple more query conditions to sort the result set by date, descending (most recent records first).
  • Retrieve the content objects matching the criteria.
// Render template.
$tfish_template->content_objects = $content_objects;
$tfish_template->tfish_main_content = $tfish_template->render($index_template);
  • Assign the retrieved content to $tfish_template in preparation for display. When a template is rendered, the properties of $tfish_template are extracted as variables, which means they can be accessed in the template.
  • Render the index template (single_stream.html, which was set earlier on) and assign it to the main content area of this page (the tfish_main_content zone in theme.html).
  • Take a look at the template files in the default theme for examples of how to output the extracted variables. Essentially you just echo them, although you must also escape them for ouput.
// Prepare tag select box.
$tfish_template->select_action = 'index.php';
$tfish_template->select_filters = TfishTagHandler::getTagSelectBox($clean_tag);
$tfish_template->select_filters_form = $tfish_template->render('select_filters');
  • Get a tag select box, set the destination page (the name of the current file, usually) and assign it to $tfish_template in preparation for display.
  • Render the select_filters form, which is the subtemplate that displays the tag select box.
// Override page template and metadata here (otherwise default site metadata will display).
require_once "TFISH_PATH" . "tfish_footer.php";
  • Include the main HTML file of the relevant template set.
  • Close the connection to database.
  • Write the contents of the output buffer to cache (if enabled).
  • Flush the output buffer to screen (render page) and clear it.

I hope this is enough to get you started on hacking or writing your own pages.

Copyright, all rights reserved.


Tuskfish CMS Developer Guide

This guide will give you an overview of the architecture of Tuskfish CMS, how to write code to perform common operations and how to extend the system to suit yourself. The guide accompanies the Tuskfish API documentation. Keep a copy handy as you read this guide. It is best to review links to the API where provided, as not every detail will be discussed in the text. This is the first version of the guide, so it is still a work in progress.