Hello World

'Hello World' Tutorial for Tiki Developers
Table of contents:

1. Introduction - A Historical Context

Luis Argerich (the founder of Tiki) wrote:
One of my goals behind Tiki was to make it very easy to any kind of programmer to be able to contribute, if you make a "genius" framework using object oriented code, MVC and other gadgets you need "genius" programmers. If a new PHP coder can't contribute without learning all sorts of things something is wrong with the framework specially for a community-driven project.

I always thought that adding one PHP file and one template file and putting your code in the PHP and your design in the template was the simplest way to add features to Tiki. Furthermore features can cooperate sharing the database but are completely independent, you remove the code and nothing happens to the rest of Tiki. It is true that the code inside each feature can be not very clear but it is code that is highly cohesive because it does just one thing.

I believe in functionality through simplicity, if you start simple you can always evolve if that is good for the project, and if you ever need it. If you start like the NASA you can realize too late that you made some ridiculous choices and there is no way back.

mose subsequently on the devel mailing list wrote:
is there any internal development documentation of Tiki project. I
think some architecture description, described database schema and it's
relations, internal rules, object relations.

I found only documentation for adding new feature, but I think it must
be something more, because of huge number of developers working on this

Is internal documentation something available on demand or it simply
doesn't exist?

It just doesn't exist. And here are the reasons:

The initial design of Tiki by Luis Argerich was especially focused on having code with very small abstractions. His purpose, as far as I can imagine, was to make the coding easy so to get a wider population of contributors, and an easy 'hackability'. Then, the source code was initially simple enough and didn't require more documentation that the db structure and the existing source code. There is very few and simple relations in the database, they usually are explicit enough. Many files were using code duplication rather than code abstraction.

That made a ground for a large number of contributions, then the code has been evolving organically from the existing basis. Quite fast on some aspects. Maintaining a code documentation was an effort that nobody felt necessary. Well, personally, I never needed it even in early times.

Technically speaking, the Tiki development is quite far from the corporate habits. Low-design, fast evolution cycles, organic selection over time of features, it's actually like if we were talking about the wiki-way, but in the coding field (validation of code is posterior to its commit and not prior, like in a wiki).

Of course such outfit brings drawbacks from the industry-oriented point of view that deals with time in much more a mechanical way than what our organic evolution proposes.

But I think that's what attracted many users and contributors of Tiki, and disgusted others.

Marc Laporte (project admin) wrote:
Tiki has an all-in-one approach to features. Think of it as a suite of applications like Open Office. Tiki has more built-in features than any other open source Web app (if you know of one, please let me know). Some people are concerned about all the features and that "it can't work" or "it's not the way to do it". Since Tiki has a different model than the "conventional wisdom", I feel it's important to explain how it works and why it works.

Please read about the Tiki model.

Nelson Ko (another project admin) wrote:
Keeping it simple is a well-enshrined principle of Tiki. However, being simple does not mean shunning abstraction and/or documentation. It is accepted that abstraction and documentation are good things, but too often too many have too much of a good thing simply because they are introduced for their own sakes.

It is important to recognize that the ultimate goal of abstraction and documentation is to make something simple even simpler, and the usefulness of such things increases with the volume of code. But in no way should these be used as crutches to excuse complicated design :-).

Increasing abstraction is a natural progression in the progress of all engineering. However, abstract too early and you might get locked in too early to a particular design. Abstract too late and things become less tractable. Ultimately, the demand and supply of this in an open community like ours should be organic. It will self-adjust over time, just as even PHP itself is evolving over time.

2. Glossary

Things are sometimes named a bit differently than in other Web Applications. Please see Glossary

3. Who does what?

Tiki Wiki CMS Groupware is a vast project. It's tricky for new people to know how the Tiki Community is organized, to find the right person to contact and to start to contribute. What may I expect from others within Tiki Community, and what do others expect from me? Who does what? Who should I talk to? How does it work? This page is intended to help.

Important: everyone is a volunteer. Things get done because someone, like you decides to take the time to make it better.

Please see:

4. Smarty/PHP

Tiki uses the Smarty template engine. Its purpose is to separate the presentation layer from the programming layer.
The PHP interfaces with the database, does the computation and assigns the Smarty variables. The Smarty templates use these variables to generate the actual HTML.

For instance:

<?php // Smarty use sample
$smarty->assign('user', $user);

This PHP code assigns the Smarty variable $user.


In a Smarty template, you can loop over an array of data and do some simple computation. To preserve separation and optimization, no PHP code must be inserted in a template.

5. The 'Hello World' page

How to display "hello world" in the central column of a Tiki page:

Create a PHP file in the Tiki root directory (where tiki-install.php is).

<?php // hello_world displayed in a Tiki center column
$smarty->assign('mid', 'hello_world.tpl');

Create a template located in the sub directory templates/ (http://www.domain.com/templates/) or in templates/styles/your_style/

<p>Hello World</p>

To test it, type the URL in your browser : http://my.domain.com/hello_world.php (where http://my.domain.com links to your Tiki root).

Notice the use of include_once('tiki-setup.php') in the PHP code. This include initializes the context to be able to use smarty. Later we will see it is doing more.
tiki.tpl is the Tiki global template file. It is the (almost) only template that contains a complete html page. A simplified working tiki.tpl file would be:

{* top *}
{include file="header.tpl"}
{* middle *}
    {* left column *}
         {section name=ix loop=$left_modules}
    {* middle column *}
            {include file=$mid} 
    {* right column *}
        {section name=ix loop=$right_modules}
{* bottom*}
{include file="footer.tpl"}

You can see the top section header.tpl, the bottom section footer.tpl and the 3 columns. The left column displays all the left modules through a loop. Idem for the right column. The central part is an include of the $mid file. (Notice: in Tiki 1.10, it is simply {$mid} and not {include file=$mid})
In our hello_world example, the $mid part is the template hello_world.tpl and will display the string hello world.

The templates hello_world.tpl can be created in the sub-directory templates/ or the sub-directory templates/styles/your_style/ (where your_style is the name of the theme you are currently using (A theme name is the css file name without extension). If it exists, the template in your current theme directory overrides the one in the default directory. The advantage of putting your templates in your local theme directory is to better identify what you have changed from the original Tiki. (Notice: if you are using SVN and are handy with SVN conflict resolution, it may be better to directly modify the templates in the default directory to be able to update/merge the Tiki modification with yours)

The $left_modules and $right_modules smarty variables are set up in tiki-modules.php. You will see later in the module section how the modules are set.

6. About templates

More about: Templates Best Practices

7. tiki-setup.php

This include does everything you need to have a Tiki page. It does a lot more that initializing smarty. By including this file in your PHP, you:

  • establish the connection with the database.
  • read all the system preferences, user preferences, permissions from the database.
  • check if the user is logged via a cookie.
  • computes all the modules (in Tiki1.9). In Tiki 1.10 this is done in $smarty->display
  • check up for some security points
  • ...

A couple of interesting variables are also set there

  • user (user login)
  • group (default group name)
  • language
  • all preferences
  • ...

All these variables are set for PHP and for smarty.

To see your username in the hello world program, modify your template:

<p>Hello World, Dear {$user|escape}</p>

8. How to debug and to see the smarty variables

If you want to see all the smarty variables that are set, you can use the smarty function {debug} in your templates. Smarty will display a popup window.

<p>Hello World</p>

You can also use the PHP function echo or print_r to see some PHP variable for debugging.
And you can use the function debug_backtrace to see the php trace.

function test() {
   echo "<pre>";
   echo "</pre>";
echo "<pre>user:$user</pre>";
$smarty->assign('mid', 'hello_world.tpl');

9. To execute some queries in the PHP

Tiki uses the ADODB database abstraction library. A good description about how to code the queries can be found at DbAbstractionDev

A typical list routine without permission check.
In lib/contribution/contributionlib.php

// List a subset of contribution
// $offset = will return the objects from there to $offset + $maxRecords (if 0 = from the beginning)	
// $maxRecords = max number to return if = -1 returns all the objects
// $sort_mode = order criterium
// $find = string to search
function list_contributions($offset=0, $maxRecords=-1, $sort_mode='name_asc', $find='') {
	$bindvars = array();
	$mid = '';
	if ($find) {
		$mid .= " where (`name` like ?)";
		$bindvars[] = "%$find%";
	$query = "select * from `tiki_contributions` $mid order by ".$this->convert_sortmode($sort_mode);
	$result = $this->query($query, $bindvars, $maxRecords, $offset);

	$ret = array();
	while ($res = $result->fetchRow()) {
		$ret[] = $res;
	$retval = array();
	$retval['data'] = $ret;
	if ($maxRecords > 0) {
		$query_cant = "select count(*) from `tiki_contributions` $mid";
		$retval['cant'] = $this->getOne($query_cant, $bindvars);
	} else {
		$retval['cant'] = count($ret);
	return $retval;		

First, you have to notice that all table names and column names must be back quoted.
The $sort_mode is the concatenation of the column name, the underscore and asc or desc. It can be any column.

In the main php, tiki-contribution.php, you will have

$contributions = $contributionlib->list_contributions();
$smarty->assign('contributions', $contributions['data']);

and in the template templates/tiki-contribution.tpl, you will have

{cycle values="even,odd" print=false}
{section name=ix loop=$contributions}
	<td class="{cycle advance=false}">{$contributions["ix"].name|escape}</td>
	<td class="{cycle}">{$contributions["ix"].hits}</td>

Notice: There is not yet a good method to check the perms in this kind of routine. You have to get all the objects and checked them.

If there is a problem with the query, Tiki will output a message saying that a query failed. This message is usually not detailed enough to allow you to figure out what's wrong.

To debug the query:

  • Put a traces printing the content of the query and its arguments.
  • Try to execute the query manually in PHPMyAdmin.
    • Go to PHPMyAdmin > Databases
    • Click on the name of the DB you are using for testing and debugging.
    • Click on SQL
    • In the Run SQL query/queries on database text field, paste the query that was output by your trace.
    • This query probably ends with something like this: (?, ?, ?). Replace the question marks by the arguments that are passed to the $this->query() method (you should have output them too in the trace). Make sure you put the arguments between backquotes.
    • Click on Go button, and see what error message is displayed.
    • Note: In your SQL query, the table and field names must be between backquotes (`), but the values must be between single quotes (').

10. To introduce a new feature or a new preference

Each feature is optional. Thus, there is a preference to activate. See Create a new preference

11. To modify the database schema

If you need to add, delete or modify the structure of a table you should look at: Database Schema Upgrade.

12. To get/set a user preference

To get the value of a user preference, you can use in a function :

global $tikilib, $someuser;
$foo = $tikilib->get_user_preference($someuser, $pref_name, $default_pref_value);

$someuser is the user name.
$pref_name can be theme, allowMsgs
The default for $default_pref_value is ""
The effect of the above code is to populate the global array variable $user_preferences[someuser], which is also returned in the variable $foo.
Notice: global $tikilib, $someuser; is not necessary if you add these lines in the same file that the include tiki-setup.php (the hello_world.php) but are needed in you add these lines in a function.
Notice: To access the prefs of the current user (user), it is not necessary to use the above. These are always available as variables. In version 1.10, they are available in the $prefs array.

To set a user preference

global $tikilib, $someuser;
$tikilib->set_user_preference($someuser, $pref_name, $pref_value);

Notice: we don't include lib/tikilib.php because it is always included in tiki-setup.php. Meanwhile, the other libraries must be included like this:

global $categlib; include_once('lib/categories/categlib.php');

Notice the use of global. Global is necessary because your library can have been included in another function that is out of scope of your new function.
Notice that these two commands actually work together as a single operation. This is why putting both on the same line actually helps code review, and is an accepted exception to the usual coding standards.

You can have a list of user preferences in the SQL table tiki_user_preferences. Same remark than for tiki_preferences, the list can be incomplete.

13. To create a menu or to introduce a menu option

Tiki has a couple of ways to introduce menus
- one by creating the menu and the menu options through sql (1)
- one by using the tiki API (2)
- one by using the templating / module feature (3)
- one based on phplayers (4)

There is not really a best method to create a menu. It depends on if your menu can be dynamically updated, you will not use the template. If your menu is fixed, perhaps a template is the best.
The phplayers method is not described here as there is no current API. (phplayers menu are interesting because they can have more than 2 levels (Means an option in an option in a section)

INSERT INTO tiki_menus (menuId,name,description,type) VALUES (100,'My menu','My very own menu','d'
INSERT INTO tiki_menu_options (menuId,type,name,url,position,section,perm,groupname)
VALUES (100,'o','My option','tiki-my_option.php',10,'','','');
INSERT INTO tiki_menu_options (menuId,type,name,url,position,section,perm,groupname)
VALUES (100,'o','My other option','tiki-my_other_option.php',15,'','','');

A menu affects 2 tables. One for the menu by itself, and the other one for the menu options. In the previous example, we created a menu ˜My menu with 2 options.
A menu type can be
- d (dynamic collapsed)
- e (dynamic extended)
- f (fixed)
an option type can be:
- s (section)
- r (sorted section)
- o (option)
- - (separator)

The section parameter corresponds the feature. If the feature is not activated, the option will not show up.
The groupname parameter is a filter on the group.

Notice: the menuId 42 is reserved for the application menu (the default Tiki menu).
Trick: we usually don't give a consecutive number to menu options to be able later to add additional options easier.
If an option can be see with 2 perms, you need to add 2 options with the same position and different perm. Idem if it can be see in 2 sections or 2 groups.
Some documentation can be found at Custom Menus
Notice: there are some bugs about the expand/collapse javascript in tw1.9.2.

To insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (adminTo insert this menu in a column, you need to create a user menu that uses it (admin->modules->create a new user menu) and then assign this user menu (admin->modules->assign new module)

global $menulib; include_once(˜'lib/menubuilder/menulib.php');
$menulib->replace_menu($menuId, $name, $description, $type);
foreach ('optionmenu') {
  $menulib  $menulib->replace_menu_option($menuId, $optionId, $name, $url, $type, $position, $section, $perm, $groupname);

The parameter values are the same than described previously.
Notice: you can notice the use of global $menulib. This is very useful when you use this part of code in a function. The include can have been done somewhere else, and the global gives you the opportunity to catch the variable $menulib.

If you don't know the $menuId you want to use, you need to

$menulib->replace_menu(0, $name, $description, $type);
$query = 'select max(`menuId`) from `tiki_menus`';
$menuId = $tikilib->getOne($query);
foreach (optionmenu) {
  $menulib->replace_menu_option($menuId, $optionId, $name, $url, $type, $position, $section, $perm, $groupname);

Next, you have to create a user menu and assign it

global $modlib; include_once ('lib/modules/modlib.php');
$modlib->replace_user_module($name, $title, "{menu id=$menuId}");
$modlib->assign_module($name, $title, $position, $order, 0, $rows , serialize($groups), $params, $type);

$position is l (left column) or r (right column)
If you assign 2 modules with the same order in the same column, they will appear both in a random order.
$groups is an array of groups (ex: array("Registered", "Anonymous"))
$params is the value you put in the param field in admin->modules->assign module. (ex: 'max=10')
$type is 'D' to assign to everybody.

You can also create a menu with a template module. A good example is templates/modules/mod-application_menu.tpl. This menu is the old menu that has been replaced by a database menu to be able to use the mods.

You only need to create a new template in templates/modules/ with a filename beginning with mod-. This new module will automatically appear in the admin->modules scrolling list.

16. To introduce a new permission

16.1. Before permission revamp (sometime between Tiki 6 and Tiki 9)


16.2. Nowadays

You know you are after the permission revamp when yous MySQL database users_permissions table is empty.

You need to edit function get_raw_permissions() in file lib/userslib.php and add your permission at the correct place like this:

Sample permission definition
                                'name' => 'tiki_p_blog_post',
                                'description' => tra('Can post to a blog'),
                                'level' => 'registered',
                                'type' => 'blogs',
                                'admin' => false,
                                'prefs' => array('feature_blogs'),
                                'scope' => 'object',
level can be editors, registered, admin (seems mostly deprecated)
type is the group of perms (wiki, cms, blog..)
admin is true for the perm that admins the whole type (note naming convention tiki_p_type_admin)
prefs array of features which need to be active for the permission to be active
scope ??? TODO: explain
apply_to ??? TODO: explain

Then you need to Clear Tiki Caches and the new permission will show up in tiki-objectpermissions.php admin interface.
You can create a new group of perms there. There is no other requirement for a group of perms.
NOTE: This is the order they appear in tiki-objectpermissions.php admin interface and it's important to keep them grouped by 'type'

16.3. Usage

In a php, the perm is used like this:

if ($tiki_p_whatever == 'y')

Don't forget to compare to a value; a common error is to write only

if ($tiki_p_whatever)//wrong code

We usually name a perm with a name beginning by tiki_p_

17. To check a permission

New for version 4: Permission Revamp

All permissions can be overwritten at a category or object level. The rule to check if you have the tiki_p_perm on an object is:
- check if the object has special perm. If yes, test if one of the group user has the perm tiki_p_perm otherwise returns false.
- check if the object has a category with special perm. If yes, test if one of the group user has the perm tiki_p_perm otherwise returns false.
- if none above, use the global tiki_p_perm value

A function is dedicated to do the job

$perm = $tikilib->user_has_perm_on_object($user, $object_id, $object_type, $tiki_p_perm);

where user is the login name, $object_id is the object id (can be the page name or the article_id or ...), $object_type is the object type (can be 'wiki page', 'article'). You can use an array of perms instead of tiki_p_perm if needed.


if (!$tikilib->user_has_perm_on_object($user, $pageName, 'wiki_page', 'tiki_p_view') {
	$smarty->assign('msg', tra('Permission denied you cannot read this page'));


if (!$tikilib->user_has_perm_on_object($user, $quiz_id, 'quiz', array('tiki_p_take_quiz','tiki_p_view_quiz_stats')){
	$smarty->assign('msg', tra('Permission denied you cannot access this quiz'));

Notice: This function is very convenient but it is quite a bit slow even if there are a couple of caches to optimize it. There is only one example in the Tiki code that integrates the perm checking in the sql query.

To reassign all the permissions a user has on an object
There is actually no integrated function to do the job. This snippet reassigns the perm variables.

global $userlib; include_once('lib/userslib.php');

global $feature_categories;
if ($userlib->object_has_one_permission($objectId, $objectType)) {
	$perms = $userlib->get_permissions(0, -1, 'permName_desc', '', $objectType);
	foreach ($perms['data'] as $perm) {
		$permName = $perm["permName"];
		if ($userlib->object_has_permission($user, $objectId, $objectType, $permName)) {
			$permName = 'y';
		} else {
			$permName = 'n';
} elseif ($feature_categories == 'y') {
	global $categlib; include_once('lib/categories/categlib.php');
$perms_array = $categlib->get_object_categories_perms($user, $objectType, $objectId); 
   	if ($perms_array) {
    		foreach ($perms_array as $permName => $value) {
    			$permName = $value;

$objectId is the object identifier: Ex: the page name for a wiki page, the article_id for an article.
$ObjectType is one of 'wiki page', 'article', 'forum', 'post', 'blog', 'faq', 'image gallery', 'file gallery', 'comment', 'tracker', 'survey', 'quiz', ...

18. To wiki parse a textarea

Imagine you have a textarea and you want this textarea to accept wiki format.
To parse the textarea, you will have to call


Usually if you have a choice like “it is html”, you need to add an additional parameter

$tikilib->parse($test, $is_html)

PS: some tikwiki1.9 have bugs around the is_html parameter. There are patched by replacing $test with htmlspecialchars($test);

If you want to add some quicktags to the textarea

include_once ('lib/quicktags/quicktagslib.php');
$quicktags = $quicktagslib->list_quicktags(0,-1,'taglabel_desc','','wiki');
$smarty->assign_by_ref('quicktags', $quicktags["data"]);
$smarty->assign('quicktagscant', $quicktags["cant"]);

In the templates in order to use the quicktags, you have to add in the template where you want the quicktags to appear:

{include file="tiki-edit_help_tool.tpl"}

PS: only one quicktag zone per page is allowed

If you want to add the resize buttoms, you have to add in the php:


And in your tpl, you have to add something like this:

<form id='editpageform'>
  {include file="textareasize.tpl" area_name='editwiki' formId='editpageform'}
  <textarea id='editwiki' rows="{$rows}" cols="{$cols}">{$pagedata|escape}</textarea>
  <input type="hidden" name="rows" value="{$rows}"/>
  <input type="hidden" name="cols" value="{$cols}"/>

19. Categories

How to make your feature use categories.
docs need to be written

20. Theme control center

How to make your feature be able to take on a different theme (depending on context)
docs need to be written

Tiki has 2 search features. One that is based on the mysql fulltext search feature and the other one that is Tiki feature. (see http://doc.Tiki.org/Search+Admin for more details)

First, you have to add a fulltext index in the database.
For instance, you have a table

CREATE TABLE contribution (
	title varchar(80) default NULL,
	body text

If you want to be able to search on the title and the body field, you will need to add a fulltext index

CREATE TABLE contribution (
	title varchar(80) default NULL,
	body text,
	hits int(8) default NULL,
	lastModif int(14) default NULL,
	FULLTEXT KEY ft (title ,body)

Mysql will automatically takes care of the indexation in real time.
You can put as many fields as you want

Notice: if a field is in a wiki format and if you want to index what you see, you will need to have another field that contains the parsed field. Even in this case, you can have some difference (for instance if your field contains a last module format, only the result at indexation time will be indexed). Usually, in Tiki we index the field not parsed as.. in IMHO no choice is perfect.

Second, you have to add the search function in lib/searchlib.php

function find_contributions($words = '', $offset = 0, $maxRecords = -1, $fulltext = false) {
	static $search_contributions = array(
			'from' => '`tiki_contributions` c,
			'name' => 'c. `title`',
			'data' => 'c. `body`',
			'hits' => 'c. `hits`',
			'lastModif' => 'c. `lastModif`',
			'href' => 'tiki-view_contribution.php?contributionId=%d',
			'id' => array('c. `contributionId`'),
			'pageName' => 'c. `title`',
			'search' => array('c.`title`', 'c.`body`'),

			'permName' => 'tiki_p_view_contribution',
			'objectType' => 'contribution',
			'objectKey' => 'c.`title`',

	return $this->_find($contributions, $words, $offset, $maxRecords, $fulltext);

'name' is the column that contains the name of the object
'search' must be the list of fields of the mysql fulltext index.

Third, you need to add a call to the function in templates/modules/mod-search_box.tpl

{if $feature_contribution eq 'y'}
	<option value="contributions">{tr}Contributions{/tr}</option>

Also in templates/tiki-searchresults.tpl

{if $feature_contribution eq 'y'}
	<a class="linkbut" href="tiki-searchresults.php?highlight={$words}&amp;where=contributions">{tr}Contributions{/tr}</a>
<form class="forms" method="get" action="tiki-searchresults.php">
	{if $feature_articles eq 'y'}
		<option value="contributions">{tr}Contributions{/tr}</option>

And in tiki-searchresults.php

if ($where == 'contributions' &&  $feature_contribution != 'y') {
	$smarty->assign('msg', tra("This feature is disabled").": feature_contribution");

And you need to add the search on this object in the global search. In lib/searchlib.php, you need to update the function find_pages

function find_pages($words = '', $offset = 0, $maxRecords = -1, $fulltext = false) {
	if ($feature_contribution == 'y') {
		$rv = $this->find_contributions($words, $offset, $maxRecords, $fulltext);
		foreach ($rv['data'] as $a) {
			$a['type'] = tra('Contribution);
			array_push($data, $a);
		$cant += $rv['cant'];

If you data are a little more sophisticated, you can do some join in the search. This is the faqs example

function find_faqs($words = '', $offset = 0, $maxRecords = -1, $fulltext = false) {
	static $search_faqs = array(
		'from' => '`tiki_faqs` f , `tiki_faq_questions` q',
		'name' => 'f. `title`',
		'data' => 'f. `description`',
		'hits' => 'f. `hits`',
		'lastModif' => 'f. `created`',
		'href' => 'tiki-view_faq.php?faqId=%d',
		'id' => array('f. `faqId`'),
		'pageName' => 'CONCAT(f. `title`, ": ", q. `question`)',
		'search' => array('q. `question`', 'q. `answer`'),
		'filter' => 'q. `faqId` = f. `faqId`',
		'permName' => 'tiki_p_view_faqs',
		'objectType' => 'faq',
		'objectKey' => 'f.`title`',

	return $this->_find($search_faqs, $words, $offset, $maxRecords, $fulltext);

NO additional perm checking is needed. The function does the perm checking.

23. To add a confirmation step

Notice: We consider here only the "Protect against CSRF with a ticket:" feature in admin->login. The "Protect against CSRF with a confirmation step:" was supposed to disappear.

if ($_REQUEST["action"] == 'delete') {
	$area = 'delete_object';
	if ($feature_ticketlib2 != 'y' or (isset($_POST['daconfirm']) and isset($_SESSION["ticket_$area"]))) {
	} else {

This works only if the delete parameters are passed directly in the URL. Key_get lists the parameters passed through a form input. In this case, you have to copy the parameters with the limit of URL size (it is why usually the multiple delete has no confirmation step

Here is an example from the multiple remove in admin->users

if ($_REQUEST["submit_mult"] == "remove_users") {
		$area = 'batchdeluser';
		if ($feature_ticketlib2 == 'n' or (isset($_POST['daconfirm']) and isset($_SESSION["ticket_$area"]))) {
			foreach ($_REQUEST["checked"] as $deleteuser) {
				$tikifeedback[] = array('num'=>0,'mes'=>
                                sprintf(tra("%s <b>%s</b> successfully deleted."),tra("user"),$deleteuser));
		} elseif ( $feature_ticketlib2 == 'y') {
			$ch = "";
			foreach ($_REQUEST['checked'] as $c) {
				$ch .= "&amp;checked[]=".urlencode($c);
			key_get($area, "", "tiki-adminusers.php?submit_mult=remove_users".$ch);
		} else {
  • To protect your application against sea surf(CSRF - XSS)

Each Tiki page must include a protection against sea surfing.
For instance tiki-action_object.php

if ($_REQUEST['action_object']) {
	// execute your action
$smarty->assign('mid', 'tiki-action_object.tpl');

24. To create a new module

Modules are the boxes typically found on right & left sides of portal-style sites. They are great to re-use content. In Tiki, contrary to similar web applications, modules are typically built-in (bundled) in Tiki. You can pick from the list or you can create your own.

To create a new module, the minimum thing you need to do is to create a template file under the templates/modules/ (or templates/styles/your_style/modules/) with a filename beginning with mod- and a .tpl extension.
Example, a contribution module will be

{tikimodule title="{tr}Contribution{/tr}" name="contribution" flip=$module_params.flip decorations=$module_params.decorations}
  <div align="center">
    My contribution

Notice the use of the smarty function tikimodule that will set up the box, the title and the box preferences.

If you need to set up some variables, you need to create a php file

$smarty->assign('my_contribution_text', 'hello world');

The template that uses this variable will look like

{tikimodule title="{tr}Contribution{/tr}" name="contribution flip=$module_params.flip decorations=$module_params.decorations}
  <div style="text-align: center">

Notice: This is a simplified version. This simple program allows only to have one occurrence of the module (there is only one variable $my_contribution_text. And in 1.9, the modules are computed at the beginning in tiki-setup.php that don't allow to use variable set in the main program.

If you want to use the parameters that are set up in the admin->modules-> param module line or in the MODULE plugin, you can use

$smarty->assign('max', $module_params['max']);

where max is either a param of MODULE or a param inserted in the admin->modules->param line.

To create a new plugin

Plugins are an extension to basic wiki syntax. Despite the name, in Tiki, contrary to similar web applications, plugins are typically built-in (bundled) in Tiki. You can pick from the list or you can create your own.

To create a new plugin, you need to create a file in lib/wiki-plugins/ with a filename beginning with wikiplugin_ and an extension .php. (while the template is in templates/wiki-plugins/*.tpl)



//the info function is needed for tw >= 3.0
function wikiplugin_helloworld_info() {
	return array(
		'name' => tra('Helloworld'),
		'documentation' => 'Hello World',
		'description' => tra('Test.'),
		'format' => 'wiki', // html or wiki : must be lower case not any other value accepted
		'prefs' => array( 'wikiplugin_helloworld', 'feature_myfeature'),
//each plugin can have an admin setting(pref) to enable them
		'body' => tra('explain what the body of the plugin means'),
		'validate' =>'all', //add this line if each insertion of this plugin needs to be validated by an admin, 
				// (because new or modified plugin body or plugin arguments is found). Possible values are: 'all', body' or 'arguments'. 
		'params' => array(
			'title' => array(
				'required' => false,
				'name' => tra('Title'),
				'description' => tra('Describe what title is'),

function wikiplugin_helloworld($data, $params) {
     if( isset( $params['data'] ) ) {
           $title = $params['data'];
           return "Hello World __$title__ $data!   &quot;;

} else {
// Indicate that a parameter is missing
return WikiParser_PluginOutput::argumentError( array(&#039;title&#039;) );
} ?&gt;

~/np~ In a wiki page use
The display will be
Hello World Mr Tiki!
Note: The best is to try it first on an otherwise empty wikipage. That guarantees that your new wikiplugin will be recognized. When your plugin is displayed at first time, you might be asked if you wanted to enable the new plugin (if in admin mode).
A plugin consists of 2 functions. The name of the functions must be formatted. They begin with wikiplugin_, then the name of the plugin in lower case, then _help for the help function. The info function provides a descriptor for the plugin. The descriptor is used to generate the documentation, the edit UI for the plugins and provides processing instructions explaining how to handle the input, process the output and execute the plugin. The process function has 2 parameters, the text inside the tags data and the list of parameters param as a PHP array.
Note: Many plugins in tiki use extract() on the $params array. When done, EXTR_SKIP must be used. However, using extract is discouraged.
By default, the value returned by the function will be wiki parsed. Chunks of HTML provided by the plugin must be enclosed in ~np~ ... ~/np~ to avoid further parsing. If the entire output is HTML, 'format' => 'html', can be added in the description function to skip parsing of the output. Alternatively, the plugins may return WikiParser_PluginOutput objects generated through the various factories available in lib/core/lib/WikiParser/PluginOutput.php. These objects will provide standard output for common errors. If you have more than one parameter, the wikiplugin use will be
{HELLOWORLD(title=Mr, name=tiki)}Tiki{HELLOWORLD}
the extract function in the php code will set for you all the parameters and you will be able to use $params'name' in the same way you use $params'title' in the php code. If you want to be clean - of course - you have to test the param has been set
function wikiplugin_helloworld($data, $params) {
	if (isset($params['title'])) {
		return tra("Missing parameter title");
	return "Hello World {$params['title']} $data!";
Now if you want to do more pretty display with smarty, you will use such a code
.... the help stuff (with format => html)
function wikiplugin_helloworld($data, $params) {
	global $smarty;
	if (! isset($params['name']) ) {
		return tra('Missing paramter name');
	$smarty->assign('name', $params['name']);
	return $smarty->fetch('wiki-plugins/wikiplugin_helloworld.tpl');
and your tpl file templates/wiki-plugins/wikiplugin_helloworld.tpl
<b>Hello World</b> {$name}
The info function is required for all plugins.
function wikiplugin_helloworld_info() {
  return array(
        'name' => tra('HelloWorld'),
        'description' => tra('Greets person'),
        'prefs' => array( 'wikiplugin_helloworld' ),
        'params' => array(
                'name' => 'the name of the person to greet"
the prefs entry will require an appropriate addition in two locations: 1- in lib/setup/prefs.php
'wikiplugin_helloworld' => 'y',
And 2- according to instructions are Preferences. These two elements shall enable the control of the feature from the admin panel.
Note: If you try a more complex plugin code and get a blank page, then the best is to look if all the brackets and bracelets match. Check also that all your variables have their $ prefix.

1. Requiring validation of plugin calls

If you need that an admin validates each new or modified plugin call, you can use the plugin parameter validate (see example above for the helloworld plugin). Possible values are: 'arguments', body' or 'all' (to monitor changes in both arguments and body).

2. Plugin help UI

Please see: PluginUI

3. Webservice Plugin

Please see: Webservices

25. Plugin filters

In order to have more security on the input added by users on plugin parameters, you can add the filter on each plugin param code, so that its content added by a user is validated against some sanitation checks. The current filter parameters, as defined in ./lib/core/TikiFilter.php are:
Filter name
url it will allow also using wiki argument variables such as
rawhtml_unsafe passthrough filter - no security at all, meaning it must be handled otherwise. Use with care (read: avoid).

26. A select all checkbox

You have a table and each line is identified by an id Put if not the table in a form. You may adapt $objects, $ix to your need
{section name=ix loop=$objects}
	<input type="checkbox" name="checked[]" value="{$objects[ix].id|escape}" 
	{if $smarty.request.checked and in_array($objects[ix].id,$smarty.request.checked)}checked="checked"{/if} />
	<xxxxx type="text/javascript"> /* <![CDATA[ */
	document.write('<tr><td colspan="...">
	<input type="checkbox" id="clickall"
	document.write('<label for="clickall">{tr}select all{/tr}</label>
	/* ]]> */</xxxxx>
{tr}Perform action with checked:{/tr} 
<input type="image" name="delsel" src='img/icons/cross.png' alt={tr}delete{/tr}' title='{tr}delete{/tr}' />
replace xxxx with script and yyyy with onclick in Tiki<9.0, the icon was pics/icons/cross.png In php you test
if (isset($_REQUEST['delsel_x']) && isset($_REQUEST['checked'])) {
	foreach($_REQUEST['checked'] as $id) {

27. To do something specific in a template (ex.: tiki.tpl) conditional to the current item being in a category. Ex.: different header picture.

This is an example how to display in the top page something specific to the categories of the current object This function is not completly functionnal. It needs that object set a php variable $objectId or $objectid or $objId and $objecType or $section. But if you need you can easily add a $objectId = $forumId for instance in the .php Tiki>=3.0 this function has been introduced in tiki code and is called if the feature admin->category->'Categories used in tpl' has been set
{if !isset($objectCategoryIds)}
	global $smarty, $section, $objId, $objectId, $objectid, $objectType, $cat_objid, $cat_type, $objectCategoryIds;
	global $tikilib; include_once('lib/tikilib.php');
	global $categlib; include_once('lib/categories/categlib.php');
	if (empty($objectType)) {
		if (!empty($cat_type)) {
			$objectType = $cat_type;
		} elseif (!empty($section)) {
			$objectType = $section;
		if ($objectType == 'wiki') {
			$objectType = 'wiki page';
		} elseif ($objectType == 'cms') {
			$objectType = 'article';
		} elseif ($objectType == 'file_galleries') {
			$objectType = 'file gallery';
		} elseif ($objectType == 'trackers') {
			$objectType = 'tracker';
		} elseif ($objectType == 'forums') {
			$objectType = 'forum';
	if (!empty($objectType) && empty($objectId)) {
		if (!empty($objId)) {
			$objectId = $objId;
		} elseif (!empty($objectid)) {
			$objectId = $objectid;
		} elseif (!empty($cat_objid)) {
			$objectId = $cat_objid;
		} elseif (!empty($_REQUEST['galleryId']) && $objectType == 'file gallery') {
			$objectId = $_REQUEST['galleryId'];
		} elseif (!empty($_REQUEST['trackerId']) && $objectType == 'tracker') {
			$objectId = $_REQUEST['trackerd'];
		} elseif (!empty($_REQUEST['forumId']) && $objectType == 'forum') {
			$objectId = $_REQUEST['forumd'];
		} elseif (!empty($_REQUEST['page']) && $objectType == 'wiki page') {
			$objectId = $_REQUEST['page'];
	if (!empty($objectId) && !empty($objectType)) {
		$objectCategoryIds = $categlib->get_object_categories($objectType,$objectId);
		$smarty->assign('objectCategoryIds', $objectCategoryIds);

{*OTHER_TPL {if empty($objectCategoryIds)}{php}global $smarty, $objectCategoryIds;
if (!empty($objectCategoryIds)) $smarty->assign('objectCategoryIds', $objectCategoryIds);{/php}{/if} *}

{if (empty($objectCategoryIds) or empty($objectCategoryIds[0]))}
{elseif $objectCategoryIds[0] eq '13'}
If you are not using the last part in the same template, to uncomment the {*OTHER_TPL *}

28. Smarty variable you can use in a tpl

{$user} {$default_group} {$section} {$page} {$prefs} for tiki>=2.0

29. Escape in smarty

  • In an option we need to escape all textual values like this
<option value={$name|escape}>{$name|escape}</option>
  • In an url, we need to escape all the textual values like this
<a href="xx.php?name={$name|escape:'url'}>{$name|escape}</a>
tiki >=2.0 If you want to write in a template a link to the same page but with an additional parameter or replace the value of the parameter, you can use
<a href="{$smarty.server.PHP_SELF}?{query archive="y"}">LINK</a>

30. Useful functions to write a script

First begin your script with
Then some useful functions - only the minimum of params are given - see the libraries fiels for more option
// create a new group
global $userlib; include_once('lib/userslib.php');
$userlib->add_group($groupName, $description);
(in tw <= 1.9, you need a 3rd param $homePageName that can be '')
//and include them
$userslib->group_inclusion($groupName, $includeGroupName);
//(where $groupName='Registered' and $includeGroupName='Anonymous' for example)

// create a new user
global $userlib; include_once('lib/userslib.php');
$userlib->add_user($userName, $password, $email);

// create a new forum
global $commentslib; include_once('lib/commentslib.php');
$forumId = $commentslib->replace_forum(0, $forumName, $description);
(in tw <=2.0, the list of parameters is longer
 replace_forum(0, $forumName='', $description='', 'n', 120, 'admin', '',
 'n', 'n', 2592000, 'n', 259200, 10, 'lastPost_desc', '', '', 'y', 'y', 
'n', 'y', 'y', 'n', 'n', '', 110, '', '', '', 'n', 'n', '', 'n', 'n', 
'y', 'y', 'n', 'n', 'n', 'n', 'all_posted', '', '', 'n', 'att_no', 'db',
 '', 1000000, 0)

// create a new calendar
global $calendarlib; include_once ('lib/calendar/calendarlib.php');
$calendarId = $calendarlib->set_calendar(0, $creatorUser, $calendarName, $description);
(in tw <=1.9, one more parameter is needed
$calendarId = $calendarlib->set_calendar(0, $creatorUser, $calendarName, $description, array('custompriorities'=>'n'));

// assign a user to a group
global $userlib; include_once('lib/userslib.php');
$userlib->assign_user_to_group($userName, $groupName);

// assign perm to an object
global $userlib; include_once('lib/userslib.php');
$userlib->assign_object_permission($groupName, $objectId, $objectType, $permName);
with couple like ($forumId, 'forum'), ($calendarId, 'calendar') and with permName like 'tiki_p_view'

31. Naming convention

33. Credits

The documentation is licensed under a Creative Commons Attribution-ShareAlike License. Author: sylvie g Sponsors: Gerhard Brehm, Marc Laporte


The following is a list of keywords that should serve as hubs for navigation within the Tiki development and should correspond to documentation keywords.

Each feature in Tiki has a wiki page which regroups all the bugs, requests for enhancements, etc. It is somewhat a form of wiki-based project management. You can also express your interest in a feature by adding it to your profile. You can also try out the Dynamic filter.

Accessibility (WAI & 508)
Accounting 7.x
Ajax 2.x
Articles & Submissions
Batch 6.x
BigBlueButton audio/video/chat/screensharing (5.x)
Browser Compatibility
Communication Center
Contacts Address book
Contact us
Content template
Contribution 2.x
Credits 6.x
Custom Home (and Group Home Page)
Database MySQL - MyISAM
Database MySQL - InnoDB
Date and Time
Debugger Console
Directory (of hyperlinks)
Documentation link from Tiki to doc.tiki.org (Help System)
Docs 8.x
Draw 7.x
Dynamic Content
Dynamic Variable
External Authentication
Featured links
Feeds (RSS)
File Gallery
Friendship Network (Community)
i18n (Multilingual, l10n, Babelfish)
Image Gallery
Inter-User Messages
Kaltura video management
Live Support
Logs (system & action)
Lost edit protection
Meta Tag
Missing features
Visual Mapping 3.x
Mobile Tiki and Voice Tiki
OS independence (Non-Linux, Windows/IIS, Mac, BSD)
Organic Groups (Self-managed Teams)
Payment 5.x
Performance Speed / Load / Compression / Cache
Revision Approval
Search engine optimization (SEO)
Semantic links 3.x
Shopping Cart 5.x
Site Identity
Smarty Template
Social Networking
Spam protection (Anti-bot CATPCHA)
Staging and Approval
Syntax Highlighter (Codemirror)
Tags 2.x
Tell a Friend, alert + Social Bookmarking
Terms and Conditions
TikiTests 2.x
Token Access
Toolbar (Quicktags)
User Administration
User Files
User Menu
Webmail and Groupmail
WebServices 3.x
Wiki 3D
Wiki History, page rename, etc
Wiki plugins extends basic syntax
Wiki syntax text area, parser, etc
Wiki structure (book and table of content)
Workspace and perspectives 4.x

Useful Tools