Writing Modules for phpWebSite
by Matthew McNaney
-----------------------------------------------------------------------
I. Introduction
II. Programming
A. Programming Style
1. Classes
2. Sessions
B. Debug
C. Database Interaction
1. Format
2. loadObject and saveObject
D. Text
1. Input
2. Output
3. Links
4. User text
E. Language
F. Templating
G. Error Handling
H. Security
III. Installation
A. Modules
B. Directories
C. Boost
1. Boost File
2. SQL import
3. Install file
4. Registering
D. Permissions
E. Control Panel
1. Links
2. Tabs
F. Dependencies
G. Installing
IV. Run Process
A. The index.php file
B. The Inc Files
1. init.php
2. runtime.php
3. close.php
V. Administrating
A. Permissions
B. Control Panel
C. Settings
D. Form Class
E. Files
1. Images
2. Files
F. DBPager
G. Mini Admin
VI. Users and Layouts
A. Layout
B. Javascript
C. Key Flag
D. Caching
VII. Uninstallation
VIII. Advanced and Miscellaneous
A. Approval
B. Versioning
C. Cookies
D. SmartTags
----------------------------------------------------------------------
I. Introduction
______________________________________________________________________
PhpWebSite has come a long way since it was a phpNuke fork. In 0.8.x
it became modular. With 0.x it was rewritten from the ground up and
to become more of a platform for web programs.
Now that we approach the release of 1.x, phpWebSite is going through
another metamorphosis. There are new tools and techniques to create a
module. This document will attempt to take you through this process.
Please note that the original version of this document was written
during the alpha stages of 1.0.0. We are much closer to release
now. Originally started two years ago, much has changed since. I have
rewritten this document to reflect these changes.
Quick note: whenever I refer to an "item", I am expressing shorthand
for any type of content in your module. A blog entry, calendar event,
house listing, comment, etc. are "items".
II. Programming
______________________________________________________________________
Every one codes differently. When I started writing phpws, PHP itself
was in version 4. As we get close to our release, PHP 5 installations
are more frequent. PHP's current class structure is much different.
More talented programmers are bound to scoff at my coding
technique. That's ok. It just happens to be the way I learned. I hope
to develop along with my code. Keeping that mind, feel free to deviate
from my coding samples. You may want to copy my code to get started
but develop your own technique.
A. Programming style
----------------------------------------------------------------------
1. Classes
......................................................................
phpWebSite 1.0 is almost entirely class based. If you are not familiar
with Object Oriented Programming, you may have problems from here
on. I would suggest reading some OOP documentation and code some
examples. Let me repeat the warning from above: my OOP coding may vary
wildly from "the standard" however it works for me. Use coding methods
you find comfortable.
You will find that many of the core classes use static calls. Others
depend on a constructed object. I suggest that if you have a simple
function that other modules could use to make it a static call. I like
static calls because they are easier to remember. A more complex class
may require a constructed object.
For example, PHPWS_Text has two functions (which I will cover in a
bit) that parse input and output text.
$text = PHPWS_Text::parseInput($input);
echo PHPWS_Text::parseOuput($text);
Simple. The database class has a static call as well:
PHPWS_DB::query('SELECT id from mytable');
You will tend, however, to contruct a database object most of the time
because of its complexity:
$db = & new PHPWS_DB('table_name');
$db->addWhere('column_name', 'foo');
$db->loadObject($object);
2. Sessions
......................................................................
Sessions can be very helpful. They prevent redundant object
construction, relay information without forms, links, or cookies, and
generally simplify coding. That said, avoid them when possible.
In phpWebSite 0.10.x and under, we couldn't get enough of sessions. We
also tended to overuse them. This caused memory bloat and required
constant vigilance of values. In phpWebSite 1.0, we try and use them
sparingly. You should as well.
I would also recommend avoiding sessioned objects. Although it isn't
hard to make sure their class is included before a session creation
(which is required), it can tend to be a pain.
B. Debug
----------------------------------------------------------------------
Those familiar with 0.10.x may remember the good ole testArray and
testObject function:
echo phpws_debug::testarray($some_array);
echo phpws_debug::testobject($some_object);
These still exist in phpWebSite 1.0, but they have been combined to
just "test":
test($some_array);
test($some_object);
test($some_string);
Notice you don't need to add echo.
The "test" function will recognize the type of variable passed to it
and behave appropiately. test() will change html tags to readable to
special characters for readability.
test() accepts two extra parameters. The second parameter tells test()
to exit after echoing the result, like so:
test($some_array, true);
Normally, test ignores redundant arrays and objects. You may bypass
this by entering 'true' in the third parameter:
test($some_array, true, true);
Beware. Allowing recursive printing could cause a memory overflow:
$foo = new Foo;
$bar = new Bar;
$bar->foo = &$foo;
$foo->bar = &$bar;
test($foo, false, false); // Expect a crash!
C. Database Interaction
----------------------------------------------------------------------
phpWebSite uses the PEAR Database class. This library allows multiple
database type (MySQL, Postgresql, Oracle, etc.)
interaction. phpWebSite also uses its own special database class named
PHPWS_DB. This class handles the connection and most basic queries. You
should familiarize yourself with it by reading the Database_Class.txt
file in the docs directory.
1. Format
......................................................................
Since you want your module to work independent from the database
platform, you should construct your queries carefully. Great effort
was put into the PHPWS_DB class to allow it to conform to separate
platforms. When possible, you should use it instead of constructing
your own queries. Also, security measures in PHPWS_DB class try to
prevent database hijacking.
2. loadObject and saveObject
......................................................................
Two very useful functions to note are loadObject and saveObject.
When you create an item class, your variable names should mirror the
column names.
For example, this class:
class foo {
var $id = 0;
var $title = null;
}
Should have a table like so:
CREATE TABLE foo {
id INT NOT NULL,
title VARCHAR(255),
primary key ('id')
};
If you sync the variables and columns, loadObject and saveObject will
simplify your life. Here is how you can easily load an object from the
database. First I create the constructor function for "foo".
class foo {
var $id = 0;
var $title = null;
function foo($id=0) {
if ($id) {
$db = & new PHPWS_DB('foo_table'); // the foo table
$this->id = (int)$id; // id exists, set the id
$db->loadObject($this);
}
}
}
Now I can create my object.
$foo = & new foo(5);
The constructor will set the id, pull that row from table, and set the
variables for that object (just title in this example).
Now I can change the object and save the results with saveObject.
$foo->title = 'Better title';
$db = & new PHPWS_DB('foo_table');
$db->saveObject($foo);
These two functions are the foundation of your item class.
D. Text
It should go without saying that a majority of the information we will
be working with is text inputted by the user and text outputted by the
software. The amount of text tends to create issues and problems. How
do we save the text? How is the text displayed? How can we be sure the
text is safe? Which html tags will we allow?
phpWebSite has a special text class to assist you with these issues.
1. Input
......................................................................
All input needs to be secured before going into the
database. Although the database class tries to prevent malacious
actions, it is best to parse the text before saving it.
Use parseInput before setting your object's text variables. I usually
use something like the following example:
$foo->setSummary($_POST['summary');
class foo {
....
function setSummary($summary) {
$this->summary = PHPWS_Text::parseInput($summary);
}
parseInput will encode all the html tags, remove problematic Windows
characters, and change local web address to relative links.
Now the information contained in $foo->summary is ready to be saved.
2. Output
......................................................................
Now we need to display our text. We can use parseOutput.
echo $foo->getSummary();
function getSummary()
{
return PHPWS_Text::parseOutput($this->summary);
}
parseOutput does more work than parseInput. It will:
1) Decode the html characters (encoded previously in parseInput)
2) change bbcode into html tags
3) remove profanity
4) strip tags we don't wish to allow
5) properly replace newline breaks with html breaks
6) translate various symbols into XHTML acceptable coding
3. Links
......................................................................
There are a handful of functions in the Text class that can assist you
with html links. Although permanent links are easier to type out, you
may find these functions help with the dynamic ones.
PHPWS_Text::moduleLink($subject, $module, $variables,
$target, $title);
moduleLink creates a phpWebSite relative link. For example:
$party_vars['city'] = 'Boone';
$party_vars['house_id'] = 5;
PHPWS_Text::moduleLink('Party at Jay\'s house',
'party_mod',
$party_vars,
'blank',
'Directions to Jay\'s');
The above will create:
Party at Jay's house
The target and title variables are optional.
Sometimes you may want to create a more secure link. secureLink does
this for you. It works exactly like moduleLink except it adds an
authorization key to the end of the link.
PHPWS_Text::secureLink($subject, $module, $variables, $target, $title);
The authorization key works with the permissioning system in
phpWebSite. It makes sure the person arriving at an administrative
option is actually coming from the site and is logged in.
Here is another example. Let's say that someone was able to create a
bogus link somewhere on your site to exploit a weakness in the
software:
Now if user number 6 went to that address, nothing would happen
because they don't have that priviledge. However, when you, an admin,
log on, that image tag could send a command to the user module to make
user 6 an admin. Since YOU had permissions, it would get through.
With the authorization key, the above would fail because the security
check would be expecting it. Since the hacker can't foresee what your
authorization code will be the moment you log on, they have little
chance of guessing the code.
If you want to use a link like the above but want the href information
only, you can use linkAddress:
PHPWS_Text::linkAddress($module, $variables, $secure,
$add_base, $convert_amp);
The first two parameters should be familiar. The last three are
optional, If "secure" is true, the authorization key will be added to
the link. If "add_base" is true, the full site address will be
prefixed to the address (i.e. http:://mysite.com/index.php... etc.).
Finally, if "convert_amp" is true, all the ampersands will be
converted into XHTML appropiate &. You may want to set this to
false if you are using a header.
Finally, if you want to use short links via mod_rewrite, you can use
the rewriteLink function:
PHPWS_Text::rewriteLink($subject, $module, $id, $page);
Example:
echo PHPWS_Text::rewriteLink('Read more', 'newspaper', 6, 2);
The above would print:
Read more
If not using Apache or mod_rewrite is disabled, you will get a normal
link.
Read more
Please familiarize yourself with mod_rewrite and .htaccess file
formatting before using the above.
4. User text
......................................................................
Some final notes before leaving the text discussion. Don't ever trust
user input ESPECIALLY anonymous input. Comments, for example, strips
all tags from user input. If they aren't using bb encoding, they don't
get to format the text. This is for your own safety.
As careful as I have been, I am SURE that somewhere I have left a
variable open that can be exploited. It is inevitable. Get in the
habit of testing all user data. Cast data types. Strip tags. Don't
allow file uploads. If you allow any of the above, parse the mess out
of it.
Testing text doesn't stop at form entry either. Parse cookies. Expect
someone to try and hack any input into the system.
E. Language
----------------------------------------------------------------------
phpWebSite 1.0 uses the gettext method of language translation. There
are several benefits using gettext over the old method. First, there
isn't an extra language session taking up memory. Second, it is easier
to develop code around it. Third, editing the files is simple and
there are many client programs that perform this function.
The downside is that gettext must be compiled on the machine. This
isn't that much of a problem on most Linux machines but may cause a
problem with custom builds or Windows servers.
Translating static text in your module is easy:
_('Translate this text');
Surround your text with underline, left then right parenthesis. The
underline is short for gettext();
When you have dynamic content in your translation, use printf or
sprintf like so:
printf(_('Good morning %s, how are you?'), 'Ted');
This way the translator can move the "%s" around.
You can learn more about gettext and translation in the
docs/Language.txt file.
F. Templating
----------------------------------------------------------------------
Whenever possible, you should avoid putting html mark up in your
code. Layout and style should be governed by template files.
Template files are typically html encoded files containing special
content tags. You can insert content into these content tags using the
PHPWS_Template class.
Example:
The template - greeting.tpl
----------------------------
{INFORMATION}
The code: ---------------------------- $template['INFORMATION'] = 'We hope you are well'; $template['GREETING'] = sprintf(_('Good morning %s, how are you?'), 'Ted'); echo PHPWS_Template::process($template, 'my_module', 'greeting.tpl'); The result: ----------------------------We hope you are well
This is just the basics of templating. Make sure to read docs/template.txt for more information. G. Error Handling ---------------------------------------------------------------------- Stuff happens (keeping this G rated y'all). Sometimes things go wrong in your module or in others. phpWebSite uses PEAR's error class for problem identification. PEAR's error class constructs an error object. These objects are easy to identify with "isError". $result = getError(); if (PEAR::isError($result)) { exit('An error occurred'); } Rarely will you just exit however. Instead you need to report the error to the user and the site administrator. The PHPWS_Error class extends the PEAR error class. You can use this class to create your own error object. For example, say you have a function that takes an id greater than zero. If the id is zero, then something went wrong. We would use the "get" function. function load($id, $name_of_req) { if ($id) { loadItem($id, $name_of_req); } else { return PHPWS_Error::get(ERROR_CODE, 'my_module', 'load', $name_of_req); } } In the above example, an error object is returned with these variables: ERROR_CODE - A predefined number indicating the problem my_module - The module the error occurred in load - The name of the function the error occurred in $name_of_req - This is extra information. In this case, the name of the user that requested the load. This parameter is optional. The ERROR_CODE is established earlier. I usually create a file named error_defines.php and include it when the module starts. inc/error_defines.php The ERROR_CODE corresponds to a message set in my conf/error.php file: conf/error.php The error class will look for the error.php file to get the proper message. The errors can be defined where ever you wish EXCEPT in the error.php file itself. The error.php may be called more than once and you can't have a repeating define function. Once you have an error object, you can print the message: echo $error->printError(); You can also use the PEAR functions getUserInfo and getMessage. If the error message doesn't need to be seen by the user, you can simply log it: PHPWS_Error::log(ERROR_CODE, 'my_module', 'load', $name_of_req); If you received the error object from another source, you can log it directly: PHPWS_Error::log($error_object); These errors are written to your logs/error.log file. What is great about error objects is the ability to pass them up. For example look at the below. function foo() { $bar = bar(); if (PEAR::isError($result)) { return $result; } else { process_something($result); return true; } } function bar() { $result = load_something(); if (PEAR::isError($result)) { return $result; } else { return true; } } $result = foo(); if (PEAR::isError($result)) { PHPWS_Error::log($result); echo 'Something is broken.'; } else { echo 'something loaded and processed successfully!'; } In this case, the error could start in bar(), pass up to foo(), then get passed to the result variable. We can then log the error and give the user a useful (or not so useful as in this example) message. If something goes REALLY bad, you can also use the default error page: PHPWS_Core::errorPage(); This just prints a simple html page that indicates something went quite wrong. The script then exits. You may see it if your database goes down or a file goes missing. H. Security ---------------------------------------------------------------------- Some final warnings before leaving this chapter. 1) Parse all text coming in from post and get arrays. 2) Parse all text coming from a cookie. 3) Check variable types. Cast integers. 4) For each php file you create, prevent any action from direct access. In other words, don't let a hacker browse directly to that file and run your script. 5) Use authorization keys on any command that alters the database. III. Installation ______________________________________________________________________ A. Modules ---------------------------------------------------------------------- When phpWebSite is accessed, the core loads all the currently installed modules. You can, however, access an uninstalled module. If we have a module named "foo" in the mod/foo directory and it has an index.php file, we can access it via phpwebsite like so: phpwebsite.com/index.php?module=foo Instructions contained in index.php will be executed within the phpWebSite shell. You should only try this for simple modules. A more complex module benefits from the Boost module's upgrade capabilities. B. Directories ---------------------------------------------------------------------- Decide on your module's function. Once you know what your module "does", think of a simple one or two word description for it. This will be your "module title". Now come up with a full name for it. This is the module's "proper name." For example, if I create a guestbook module, I may give it a module title of simply "guestbook" and a proper name of "Matt's Rockin' Guest Book." Now we need to create the module's directories. I will use the "Blog" module (module title : blog) for our examples. phpwebsite/mod/blog/ Our basic directory. It is important that the directory name is the same as the module title. The rest of the directories will be placed in the above directory. boost/ This directory contains information on how to install, update, and uninstall your module. class/ The location of your module's class files. conf/ This is where your configuration files are placed. These will be copied to the phpwebsite/config/blog/ directory upon installation. These are copied so branches can each have their own config file if they need it. Put files here that may be edited by a site administrator. docs/ This is where we would put documentation on our module. img/ Any images that are specific to our module need to go here. They will be copied to images/mod/blog/ when the module is installed. inc/ Another important directory. This directory contains your modules start up and close down files. It also can contain important files that 1) remain in the hub directory and 2) should not be altered by a site administrator. lang/ Contains your gettext file translations. More about this later. templates/ Contains your template files. These will be copied to templates/mod/blog/ on install. Once your directories have been created, you can begin adding your installation files. C. Boost ---------------------------------------------------------------------- The Boost module controls the installation, update, and uninstallation of all other modules in phpWebSite. Any information pertinent to the above are stored in the boost/ directory. 1. Boost File ...................................................................... The most important boost file is named, aptly, boost.php. It relates all install, update, and uninstall information to the Boost module with just a handful of variables. Here is a list of variables used in the boost.php file. proper_name ----------- This is the proper name of the module. The blog module's proper name is simply "Blog." version ------- This number tracks the development of our module. The format is 1.2.3. Minor fixes increment the last number (3). Large fixes increment the second number (2). Major upgrades of a program increment the first number (1). As of this writing, Blog is at version 0.2.6. register -------- If our module needs to perform an action when other modules are installed, this variable is set to TRUE. Blog doesn't require any external registration so it is set to FALSE. unregister ---------- If our module needs to perform an action when other modules are uninstalled, then this is set to TRUE. Usually, if a module registers other modules, it will need to unregister those modules as well. There are cases when a module does not register other modules but does need to unregister them. Layout, for example, does not register modules on installation but when a module is uninstalled, Layout drops all the display properties of that module. These properties were established during the runtime of that module, not during the installation. import_sql ---------- If this is set to TRUE, boost will look for a install.sql file. Blog uses this. image_dir --------- If this module uses user submitted images, you would set this to TRUE. Boost will create an images/module_title directory for storage of the images. Don't confuse this with images used by the module internally (i.e. admin icons, buttons, etc.). Blog sets this to true. file_dir -------- If your module needs a directory to save files to, change this to TRUE. Most of the time, this will be FALSE, as it is for Blog. about ----- If TRUE, then this module has an About file (named about.html) in the boost directory. The About file informs the admin who wrote the module, where the module is hosted on the web, and other errata. Blog has a brief about file so it is set to TRUE. version_http ------------ You may want to keep a xml file somewhere that lets the user know if they can get an updated copy. This variable stores the web address to that file. Blog uses this as well. priority -------- Most of the time, you can leave this alone. Priority determines the order that modules are loaded for execution. For some modules, like users and layout, it is very important they load first. The default value is 50 but you can leave it blank. We will fill it in just for instructional purposes. dependency ---------- If your module is dependent on other modules to function, you should set this to TRUE. We'll cover your dependency file in a little while. So here is our boost.php file from Blog : 2. SQL Import ...................................................................... If your module uses the database, you will need to import its tables during installation. Including a install.sql file is the simpliest way to do so. Here is Blog's install.sql file. CREATE TABLE blog_entries ( id INT NOT NULL, key_id INT NOT NULL, title VARCHAR( 60 ) NOT NULL , summary TEXT NULL, entry TEXT NOT NULL, author_id INT NOT NULL default '0', author varchar(50) NOT NULL default '', create_date INT NOT NULL , allow_comments SMALLINT NOT NULL default '0', approved INT NOT NULL default '0', allow_anon SMALLINT NOT NULL default '0', PRIMARY KEY ( id ) ); CREATE INDEX blogentries_idx on blog_entries(key_id); When forming or exporting your module's SQL file, keep compatibility in mind. Remove any database system-specific commands. Also, only use lengths on character fields. INT(11) is an acceptable MySQL entry but one that will cause problems in other database systems. Make sure to create indexes outside of the table declaration. Please note the index's name format: contracted table name + underline + idx. This is phpWebSite's database class's default format. 3. Install file ...................................................................... An install file is not necessary. Many times, you just need an install.sql file to import your module's tables. That said, there may be times when you need to perform other actions outside of the sql import. Perhaps you need to create some special directories. Maybe you need some information from the site administrator before the installation can complete. An install file can assist with such actions. We will be looking at Layout's install.php (look in: mod/layout/boost/) file as an example. First notice the function name "layout_install." You must have a function composed of your module's title, plus an underline, plus the word "install." Your function should take two parameters. The first parameter is a reference to Boost's content array variable. The second parameter is a boolean variable alerting you to the type of module installation; in a hub or in a branch. Let's have a quick run through this file. Layout uses the install.php file to determine your site's name and which theme you wish to use. There are two parts to the install file. One part prints a form to gather the above information. Notice that form content is added to the $content array: $content[] = PHPWS_Template::process($template, 'layout', 'setup.tpl'); Remember, Boost implodes this array and prints the information within. After the form is created, the install function returns FALSE. This tells Boost that we aren't done yet. The second part of Layout's install function error checks form data. If satisfied, the install function saves the data and returns TRUE. This tells Boost we are done. If the data is in error, the function displays an error message and runs the form again. Since we still are not finished, FALSE is returned again. Boost will continue to run this file until it receives a TRUE OR an error object. If it receives an error object, the installation will fail and you will be able to return to the Boost module. 4. Registering ...................................................................... If your module needs to interact with other modules when they are installed, you need to create a register.php file in the module's boost directory. When the module is installed, Boost registers the module in three ways: 1) The module is registered to other modules. 2) The other modules are registered to it. 3) The module is registered to itself. Let's use Control Panel as an example. 1) Control Panel is registered to other modules. The Users module registers its permissions for example. 2) Other modules are registered to Control Panel. Boost goes through runs every module currently installed against the Control Panel's register file. Control Panel looks at each module to see if it has a controlpanel.php file. If so, it creates the tabs and links requested by the module. 3) Control Panel registers itself. It looks in its own directory for a controlpanel.php file and creates the tabs and links specified. Take a look at Control Panel's register.php file. It contains a function similar in construction to the install.php function. The function should contain the module title, plus an underline, followed by the word "register.". The register function will contain two variables. The first variable is the title of the module getting registered. Like the install.php function, the second variable is the referenced content variable. Anything we want to print in Boost is piped into it. If you wish, you can follow the logic in PHPWS_ControlPanel::registerModule method. Basically it performs the actions we went over previously in the Control Panel setup. Like install.php, Boost expects one of three results, true, false, or an error. If true, the registration was successful. If false, the registration was not completed. This is an appropriate response because the module may not need registering. The final response would be an error object. If such is returned, it is logged and Boost gives a warning. The module will still install. D. Permissions ---------------------------------------------------------------------- I will touch on permissioning here. A more detailed account can (eventually not finished writing it yet) be found in docs/Permission.txt. Checking for permissions is handled in part V. There are five types of users: anonymous - this user isn't logged in to the system logged - this user is logged in, but doesn't have any administrative permissions restricted - this user is logged in and has restricted admin privileges. These users can edit items but their changes are subject to approval unrestricted - logged in user with full permissions in one module or more. Their changes are not subject to approval deity - an all-powerful logged in user. They have access to any module, can view any item, and often use some administrative functions or modules that mere mortals are not allowed to touch. Before you start adding permissions to your module, you need to create a permission configuration file. The permission.php file is saved in your module's boost directory. If you only want your module to have all or nothing permissions. Just add: $user_permissions = true; and save the permission file. This indicates you not be using sub-permissions (explained in a second) or item permissions. The only to administrative options would be no permissions and all permissions. You may also include sub-permissions for your modules. For example, say you wanted to specify the administrators who may delete items from your module. Add a subpermission like so: $permissions['delete'] = _('Delete item') Finally, you may narrow down permissions to specific items. Just set: $item_permissions = true; Let's look at what a completed permissions file would look like: Users would read this file and know, 1) the module wants to use permissioning 2) it will use subpermissions for editing and deleting and 3) it will allow the admin to set item permissions. Your permissioning file will be registered by Users upon installation. E. Control Panel ---------------------------------------------------------------------- If your module has administrative settings, you will need a way for the site operator to get started. This is where the Control Panel module comes in. To get your module's icon on one of the Control Panel tabs, you need to create a controlpanel.php file in your boost directory. I will show the User's controlpanel.php file as an example: $tabs[] = array('title' => _('My Page'), 'label' => 'my_page', 'link' => 'index.php?module=users&action=user', ); $link[] = array('label' => _('User Administration'), 'restricted' => TRUE, 'url' => 'index.php?module=users&action=admin', 'description' => _('Lets you create and edit users and groups.'), 'image' => 'users.png', 'tab' => 'admin' ); I'll come back to the tabs array in a moment. Take a look at the link array. 1. Links ..................................................................... Although there is only one administrator link for users, control panel allows extras. The is the reasoning for making the $link variable an array. Inside the link variable is another array - an associative array. Here are the keys of the link array: label ----- The proper name of the module. This is what the module will be titled in the control panel restricted ---------- Indicates if this module is restricted to administrators. If false, any one capable of logging in will see the link. In certain situations, this may be desired. url --- The address resulting from clicking on the link. This will most likely include "index.php?module=your_module_name" plus some administrative variables. description ----------- A description of your module. It will appear next to the module link. image ----- The file name of the image shown next to your link. tab --- The tab you want your link to appear on. There are three default choices: 1) content - if your module adds content to the web site, put the link here. 2) admin - if your module assists with the administration of the site or indirectly adds to the site content, put your link here. 3) developer - if your module helps with the development of a module, theme, style sheet, or anything outside the content or administration of the site; put your link here. 2. Tabs ...................................................................... Tabs control the different administrative pages. I just went over three of the default tabs (content, admin, developer). Your module's complexity may require a tab of its own. Going back to the User module example, you can see the $tabs[] array. It contains the information for the My Page tab. My Page is a special tab for controlling logged user options. Here are the variables needed in your tabs array: title ----- The clickable title of the tab. label ----- This is a simple word to identify the tab. The label should be alphanumeric with underlines only. link ---- The tab's destination url. To learn more about using the Control Panel in your module, please see part V. section B. F. Dependencies ---------------------------------------------------------------------- Sometimes modules depend on other modules to function properly. Or perhaps your module uses only the latest version of another module or the core. In order to prevent people from installing or updating your module, you need to create a dependency file. A dependency file is an xml file describing the various modules and versions required by your module. It lives in your module's boost directory. Here is an example from Blog: