A working example

Overview

This short tutorial will guide step by step in building a dynamic website from scratch using TiP.

It is supposed you unpacked TiP in the tip directory of your web server root, as stated in the Getting started tutorial. If not, do it.

Expected web site

We want to develop a basic site to manage articles: article means a document with a subject and a text content. On this site anyone can:

This is a quite usual request... a front-end for a database table is maybe the most common dynamic site.

The data

First of all, you must create a table in your database where to store the articles so, obviously, you must create a database before (if it doesn't exist). It is strongly suggested you set (if possible) your database connection encoding to UTF-8, as all the text managements inside TiP are done using UTF-8.

Let's create this article table.

CREATE TABLE `article` (
  `id` int(10) unsigned NOT NULL auto_increment,

  `subject` varchar(50) binary NOT NULL
    COMMENT 'category=required',

  `content` text NOT NULL
    COMMENT 'category=required|widget_args=Blockquote,Strong,Emphasis',

  `_creation` datetime NOT NULL,

  PRIMARY KEY  (`id`),
  KEY `_creation` (`_creation`)
);

Maybe this SQL statement will suggest you the idea the field comments are used for customization purpose. And effectively this is what the comments are used for.

The logic

The logic (that is, the PHP stuff) is basically the same shown in the Getting started tutorial, with some more line to configure the database connection and the article module:

<?php
// Define the TiP environment
require_once './tip/TIP.php';

// Modules configuration
// Change the 'database', 'user' and 'password' items
// to reflect your MySQL configuration
$cfg = array(
  'main'                  => array(
    'type'                => array('module', 'application'),
    'title'               => 'Article demo',
    'description'         => 'A more useful example',
    'keywords'            => 'tip,article,demo',
    'engine'              => array(
      'type'              => array('template_engine', 'rcbtng')
    ),
    'data_engine'         => array(
      'type'              => array('data_engine', 'mysql'),
      'server'            => 'localhost',
      'database'          => '???',
      'user'              => '???',
      'password'          => '???'
    )
  ),

  'article'               => array(
    'type'                => array('module', 'content'),
    'anonymous_privilege' => TIP_PRIVILEGE_ADMIN,
    'default_privilege'   => TIP_PRIVILEGE_ADMIN,
    'form_options'        => array(
      'add'               => array('captcha' => true),
      'edit'              => array('captcha' => true)
    )
  )
);

// "main" module instantiation
TIP_Type::getInstance('main');

// Application execution
$GLOBALS[TIP_MAIN]->go();
?>

As you can guess, I had to activate a CAPTCHA both on the add and on the edit form to avoid spamming.

The style

Now you need to define how you present the data retrieved by the logic. In a web application this is done using HTML, so you need to define some HTML templates.

The generation process is done by using a template engine. You can virtually use any template engine by implementing the TIP_Template_Engine abstract class, althought there is a basic engine implementation: the TIP_RcbtNG engine. This demo will use this engine.

Head and body

The <head> and <body> tags are generated separately. This because <head> must be the first element in a valid HTML file, but the TiP will generate it after <body>. This approach allows to set some important tag present in the head section (such as the <title> or the robots metatag) while generating the body using the other templates.

The templates reside in a subdirectory named as the module they refer under the style directory. For instance, the head and body templates (head.rcbt and body.rcbt, as the default values of head_template and body_template properties of the TIP_Application module) are respectively head and body (the file extension, as for TiP 0.3.0, is appended by the template engine).

So, first of all here it is the style/main/head.rcbt template:

<head>

<title>{getProperty(title)}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<meta name="description" content="{getProperty(description)}" />
<meta name="author" content="eNTiDi" />
<meta name="keywords" content="{getProperty(keywords)}" />
<meta name="robots" content="{getProperty(robots)}" />
<meta name="generator" content="tip" />

</head>

The head template is quite easy to understand and almost self explenatory. Also you can notice it is almost the same of the one in the getting started tutorial (the head section has a quite rigid structure).

Now the style/main/body.rcbt template:

<body>

<h1><a href="{HOME}">Article demo</a></h1>

<div id="menu">
<h2>The menu</h2>
<p><a href="{Article.actionUri(add)}">Add new article</a></p>

<h3>Article list</h3>
<ul>
{Article.forSelect(ORDER BY `_creation` DESC)}
  <li>
    <a href="{actionUri(view,{id})}">{subject}</a>,
    <span class="date">{date({_creation},datetime)}</span>
  </li>
{}
</ul>

</div>

<div id="page">
<h2>The page</h2>
{page()}
</div>

<div id="footer">
<h2>The footer</h2>
<address>Powered by <a href="http://tip.entidi.com/">TiP</a></address>
</div>

</body>

This body contains a few tags: take a look to the RCBT tutorial to have a quick introduction on what some of these tags do.

Also, this sample code shows you all the power and weakness of a TiP based site: it does not force you a style nor a mode. You can use it in the way you like, because TiP is only a tool, not a complete CMS solution.

The page content concept

When requested, the TiP system generate the content by running the body template followed by the head template. Consequentially, the body template calls other subtemplates, which ones strictly depend on the requested action.

So, the most important dynamic part of this process is the {page()} tag in style/main/body.rcbt, that does what described. In other terms, every action stores its result (the content of an article, a form, a notification, whatever) in a single place that will be output by using the {page()} tag. If no actions are specified (the common case in the homepage), a default template is used as page content.

This is style/main/default.rcbt, that is the page to generate on the homepage:

<p>This is the default content shown by the page() tag
when no other content is generated.</p>

{Article.forSelect(ORDER BY `_creation` DESC LIMIT 1)}
<p>Usually, you'll put some dynamic stuff you want to
highlight, such as showing the latest article.</p>
<h3>{subject}</h3>
<div class="wiki">{wiki(content)}</div>
{}

The TIP_Content

The data is managed throught TIP_Content modules. They can be viewed as "tables": one TIP_Content manages one MySQL table.

The most important thing you must define (in the style section) is how to view the table rows. This is done by defining the predefined view.rcby template, that is the template to use as specified by the value of the view_template property. Being part of the article module, this template file must be defined under the article directory.

So this is the content of our style/article/view.rcbt template:

<p>The following one is the article you selected:</p>

<h3>{subject}</h3>

{main.addToProperty(title, - {raw(subject)})}

<div class="wiki">{wiki(content)}</div>

<address>{date({raw(_creation)},datetime)}</address>

<ul class="commands">
  <li><a href="{actionUri(edit,{id})}">Edit</a></li>
  <li><a href="{actionUri(delete,{id})}">Delete</a></li>
</ul>

The last touch: the forms

Now the hard part. The forms are used on a TIP_Content module for everything else but viewing actions, so the form handling is maybe the most important section of a dynamic site.

TiP calls the same template (usually form.rcbt) to generate every form. The form template has a lot of tags to embed the items (generated directly by TiP) on the form, so a deep knowledge of the form generation is required to change this template.

Briefly, store this as style/main/form.rcbt and consider it as your favorite form template:

{main.addToProperty(title, - {raw(HEADER)})}
{main.setProperty(robots,noindex,nofollow)}

<h3>{HEADER}</h3>

<form{raw(ATTRIBUTES)}>
{forEach(SECTION)}

{if({hidden})}

<div style="display: none">
{forEach(ELEMENT)}{element(toHtml)}{}
</div>

{else()}

<fieldset>
<ul>
  {forEach(ELEMENT)}
  <li>

    {if({isValue({element(getLabel)})})}
      <label{if(!{group})} for="{element(getName)}"{}>
      {if({required})}
        <strong>{element(getLabel)}</strong>
      {else()}
        {element(getLabel)}
      {}
      </label>
    {}

    <div class="element">{element(toHtml)}</div>
    
    {if({isValue({raw(error)})})}
      <div class="error"><em>{error}</em></div>
    {}
    
    {if({isValue({element(getComment)})})}
      <div class="comment">{element(getComment)}</div>
    {}

  </li>
  {}
</ul>

{if({isValue({form(getRequiredNote)})})}
  <div class="note"><strong>{form(getRequiredNote)}</strong></div>
{}

</fieldset>
{}

{}
</form>
Although the form rendering is quite difficult to modify, there are a lot of customizations possibles by simply modifying the field comments. This has the great advantage to allow dynamic form generation.
With TiP, you can modify a form by simply adding or deleting fields from a table. It is also possible to change some behavior by editing the field comments.

To get an idea on the possible customizations, take a look to the form module tutorial.

Final result

You have a basic but fully working wiki site. Here you can see the resulting demo.

The only needed stuff I left out in this example is the TIP_Locale module, used to translate the label ids (such as form.label.content) to something more usable. This is because of my lazyness and because you must build a database table with [id, en_US, it_IT, ...] rows, depending on the languages you want to implement (too text for this tutorial), but I think including TIP_Locale could be a good exercise.

You can add some more stuff, such as a CSS style sheet (to improve visual appealing), a TIP_User module (if you need authentication), a TIP_Chronology block (to catalog content by date) or also another TIP_Content module (just call it with a different name).

Have fun with TiP!

A working example was last modified by Nicola on Tue 15 Oct 2013 12:52:28 PM CEST
Hosted by BerliOS Developer Logo