How to make your own Seagull module

note: this is for Seagull 0.4.x. FIXME: link to php files

Introduction

OK, you want to create your own module of Seagull? That's fine!

While you can get quite a bit done in the creation of web based applications using just the modules supplied with Seagull, the place you really get leverage is in building your own custom modules for specialized apps - that's where you get the reward (in code you don't have to write) for your investment of effort (in learning how the framework is structured, and how to shape your code to make best use of it).

note: This tutorial may help you, but it's still not 100% complete yet. You can use it for getting information on how modules work, for better understanding of Seagull... Please have also a look at the /FaqModule (this is pretty simple...) and see how this one is done.

You can choose a module that's similar to your needs and modify it, or begin with a new one. It's almost always easier to copy an old module. If you do that by hand, be sure to rename the directories and files appropriately; what, exactly, "appropriately" means will likely become clearer as you read along.

As they used to say in school: read everything before doing anything. :-)

You can also use the Module Skeleton Generator provided in the Maintainance Module.

In this tutorial, we'll show you how to create a module for managing bookmarks or favourite URLs; we'll base it on the FAQ module.

It should:

  • output an ordered list of URLs to other pages (a library of bookmarks), with logo, text, counter, bad link button, etc., where each link redirects to the specified URL, counting how often the link has been clicked.

In the future it also should:

  • let the user page through the results (see /Code/Examples for how this is done);
  • order the links in categories (nestedSet??);
  • let everyone mark a link as bad (eg. outdated);
  • permit registered users (with the proper rights) to add or modify links;

but we'll start with the simple stuff, first. ;)

To be able to follow this tutorial, you'll need some basic knowledge of PHP, PEAR, and Seagull. For better understanding, these links are also useful:

  • Code/Examples
  • DB_DataObject Manual: All DB queries are made via DB_DataObject
  • Flexy Manual: Templates are made with Flexy
  • Howto/Misc/GoodModuleToCopy
  • The Concepts/ModelViewController (MVC) diagram
  • Howto/Misc/Upgrading (quick changelog for new Seagull versions)
  • To get an overview of workflow in Seagull please see the Howto/General/BasicWorkflow diagram.

Files and Directories to create

Directories

PHP Classes

  • modules/bookmark/: the directory for modules classes, langfiles...
  • modules/bookmark/classes/: this directory is for the classes
  • modules/bookmark/lang/: this one is for the language files
  • modules/bookmark/data/: for SQL files

Themes

  • www/themes/default/bookmark/: and this for the templates
  • www/themes/fc/bookmark/: for templates if you use the front controller (FIXME: outdated in 0.5 branch)

Files

  • modules/bookmark/conf.ini: for some configuration
  • modules/bookmark/classes/BookmarkMgr.php: the module class
  • var/cache/entities/Bookmark.php: the extended DB_DataObject entity for this module note: this file will be autogenerated by Seagull. See SQL section below

URL

  • index.php/bookmark: the URL called by the browser to invoke the Bookmark manager

SQL

In this section, we'll add a new table to the database:

CREATE TABLE bookmark (
  bookmark_id int(11) NOT NULL default '0',
  date_created datetime default NULL,
  last_updated datetime default NULL,
  url varchar(255) default NULL,
  name varchar(255) default NULL,
  text text,
  count smallint(4) NOT NULL default '0',
  item_order int(11) default NULL,
  PRIMARY KEY  (bookmark_id)
) ;
  • All schema scripts (to create table/DB) goes in data/schema.xx.sql
  • All default data goes in data/data.default.xx.sql
  • All constraints go in data/constraints.xx.sql

where xx is the two-letter code for the database type: if you're writing portable modules for general consumption, you should try to create table scripts for the popular databases; at least my (!MySQL) and pg (!PostgreSQL).

Don't use backticks (`). Some DBs choke on this. Just avoid using spaces, accented chars or other exotic characters...

So the script shown above would go in modules/bookmark/data/schema.my.sql and it will automatically get picked up by the installer if you want to install your module with Seagull.

Adding new tables to the Database

If you want to add your module to an existing Seagull installation please add the tables manually to the database. DataObject entities will be created automatically, as shown in the next step.

DataObject? entity

To create the DataObject entity

  • log in as admin,
  • go to the Maintainance Mgr (Maintainance Module),
  • click on 'Rebuild !DataObjects Now',
  • click on 'Rebuild Sequences Now',

This will create the file var/cache/entities/Bookmark.php and insert a new record into the sequence table.

Code examples

The code which makes up a module (well, and the templates, CSS, and other things) lives in many different places around the Seagull tree. In this section, we'll go through the list of things you need to create, and point out where they live, and try to explain what they do.

The URL called by the browser

SGL_PATH/www/index.php/bookmark/

Invoking any Seagull module is done by passing the correct parameters in the URL, your request is routed by the Front Controller. The pattern expected is index.php/modulename/managername/paramkey/paramvalue/ ... etc. For a full explanation see Howto/General/UrlManagementAndHttpRedirects.

The configuration file

SGL_PATH/modules/bookmark/conf.ini

See Howto/General/UsingConfigFiles for better understanding. But, in general, plan on putting items which users of your module might want to configure on a site by site basis in a config file like this; breaking such configuration details out of your code is always a good idea; with Seagull's decoupled architecture, it's easy to achieve.

The basic class

SGL_PATH/modules/bookmark/classes/BookmarkMgr.php

This is the module code that does the actual work. Understanding the context in which this code will be run is very important; it's called by the Controller, which expects it to have certain calling and return conventions.

All of the method functions which follow live in this one PHP file in the modules directory, however you can use as many classes/objects as your app architecture requires.

{{ include page="BookmarkMgrphp?" }}

That's the complete basic class; the code that does the work.

Now let's have a look at the templates, which define how the web forms look. We'll use the "list" (or "browse") template as an example:

The basic template

SGL_PATH/www/themes/$THEMENAME/bookmark/bookmarkList.html

{{ include page="bookmarkListhtml" }}

The other templates are the same, nothing special.

For translation

SGL_PATH/modules/bookmark/lang/english-iso-8859-15.php

This file contains a simple PHP array like:

<?php
    $words = array(
/*  LinkList */
        'Bookmark' => 'Bookmark',
        'Bookmark Manager' => 'Bookmark Manager',
        'Bookmark Manager :: Browse' => 'Bookmark Manager :: Browse',
        'Bookmark Manager :: Add' => 'Bookmark Manager :: Add',
        'Bookmark Manager :: Edit' => 'Bookmark Manager :: Edit',
        'Bookmark Manager :: Add' => 'Bookmark Manager :: Add',
        'Bookmark Manager :: Reorder' => 'Bookmark Manager :: Reorder',
        'Name'     => 'Name',
        'Url'      => 'Url',
        'Description' => 'Description',
        'Date created' => 'Date created'
    );
?>

You can manage the translations using the Maintainance Module. The files are named after the appropriate combination of language and character set name.

Add the module to the ModuleMgr?

Once you've put all the pieces in the right places, you need to register the modules using the provided form:

So, to add the new module into the module manager:

  • login as admin
  • hit the 'manage' button on the top left
  • hit 'Add a module' button
  • fill the form with:
    • Name: directory name of the module.
      • If you have created a bookmark module, as in the Wiki tutorial, you must write 'bookmark' here. The 'name' string will be used to identify the module uniquely so
        • use lowercase only
        • no spaces allowed
        • try and stick with single words and avoid bumpyCaps
    • Title: descriptive name of the module, this can be multiple words, etc.
    • Configurable: if the module can be configured inside the 'Module Manager' activate this checkbox, in other words, if there are features that only an admin can take advantage of, then make your module 'configurable' by enabling this field in the module table and linking to appropriate functionality. 'user' module is a good example of a module that can be configured, 'contactus' is an example of one that can't
    • Description: this text will be used in the 'Module Manager' to describe this module
    • Admin URI: the URL to call your module
    • Icon: filename (without path) of the icon used in 'Module Manager'
  • At the end hit 'Add' button. This will bring you back to the 'Module Manager' screen.

You should now see that your new module has been added at the end of the list of modules.

Assign the relevant Permissions

So we move into the 'Users & Security' module. At this point, you begin to be able to take real advantage of the extra effort you've had to invest in learning how to use Seagull: you didn't have to write the users and permissions management code; all you had to do was drop a couple of one-function calls in the right place, and you can take advantage of the code Seagull provides. Here's how you set up those users for the permissions you've provided for in your code:

Presuming you have already setup roles (groups of permissions) in the system, now you're ready to add permissions corresponding to each of the new action methods you've created in your manager class:

  • In the 'Users & Security' module select the 'manage permissions' button.
  • Add a permission for each action method you have created, ie.:
    • permission name is made of class name and method name eg. if we've named our class 'BookmarkMgr' and method is called '_edit', the permissions entry must be called bookmarkmgr_edit - note the caps;
    • please user lowercase for all permission names.
  • From 0.4.0 on you can use 'detect and add' function to let the system search for new possible perms as a result of modules you may have added - it will scan the module code you've installed, and create the appropriate entries in the permissions tables.
  • After you've created all the required perms, select the 'manage roles' button.
  • Select the role you wish to add the perms to, and in the Permissions column, hit the relevant 'change' button.
  • The right box represents all the perms assigned to the role, the left, all other perms.
  • Use the widget to select your new perms in the left box, move them to the right box, and hit 'submit'.
  • Any new user created in that role will inherit the new permissions you've created - role permissions are *only* looked at when you assign a role to a user, not when any user in a role does something (they're assigned, not looked up in realtime).
  • Currently, permissions assignments are not retroactive - which is yet another way of saying the same thing.
  • The public role, which is not user specific, will show the correct behaviour with any new perms added to it once you close the browser and open a new one, since perms are stored in a cookie which lasts the lifetime of the browser. This means the public role is the one exception to the caveat above.
  • ALTERNATIVELY, you can select an individual user from the 'manage users' section, and in the Permissions tab hit 'change', then activate the new perms one at a time. This manager allows you to filter the perms by module.

At this point, you can add the manager page for your module to your site's navigation, and people can use it.