Gallery2:Module Development Tutorial - Gallery Codex
Personal tools

Gallery2:Module Development Tutorial

From Gallery Codex

Revision as of 05:00, 12 July 2006 by Zimzat (Talk | contribs) (Category)

General Notes

Parts taken with permission from: http://txtdump.com/g2dev/

The code here has been updated for Gallery 2.1.

This tutorial will guide you to create all module files from scratch.

Module Creator Script

You can also create the files for a new module by running php lib/tools/creator/create-module.php from your gallery2 directory and then refer to those files as your read this tutorial.

Note: You must have a G2 developer package or install from CVS/nightly snapshot to get lib/tools/creator.

Developing for G2: an Introduction

Preface

When first trying to start developing for G2 you can spend a lot of time trying to figure out the basics, in this tutorial we will start with the basics, a module that somebody with no PHP experience whatsoever can code, and hope to move on to bigger and better things.

The G2 Philosophy and Implementation

The price for stability and security is redundancy and complexity. G2 has been developed to achieve almost complete separation of code and design as well as a pluggable module system. What does this mean to you? By using G2's API (Application Programming Interface) you can add new functionality to G2 without modifying any of the core code.

The first impulse I felt when I wanted to modify G2 was to find the code in question and edit it to fit my needs. While this is a very fast short term method, this sort of thinking would create major headaches in the future especially once you have to upgrade the core codebase. A much more efficient way to implement a modification or a new feature is to code a module for it. It may take you slightly more time to get started, but you will pat yourself on the back later when you realise that 'maintaining' your ten custom modules is as easy as changing a few lines to match with the new API versions.

How does G2 work?

Before we get started, you need at least some understanding of how G2 works. For example, what happens when the user clicks the "Member List" link on the sidebar?

  1. The request goes to main.php, the main G2 wrapper script. It looks at the part that says "?g2_view=members.MembersList" and realizes that you want to load the "members" module and display the view (a view is analogous to a dynamic G2 page) "MembersList".
  2. The "members" module is loaded, from modules/members and is told to display the "MembersList" view. The module loads up a file called MembersList.inc, which in turn uses the G2 API to contact the database and prepare all of the information required (membername, id, email, etc.).
  3. The corresponding template, modules/members/templates/MembersList.tpl, is loaded and the data is passed to it. The template generates a table with the data and the transaction is complete.

As you can see, there are several distinct parts in the process, each of which serve their own purpose. With very few exceptions, you do not include complex code into the templates or templating data into the PHP code. Once you understand this key concept, developing for G2 becomes a whole lot easier.

You've made it this far. Great! Let's continue on to creating a module for G2. It will be easy, I promise.

Module Directory Structure

modules/$modulename/module.inc

This is the entry point to every module. It defines what the module is and will do.

modules/$modulename/$viewname.inc

Pages are called "views". Each view is given its own .inc file. The name of the file is the same as the view.

modules/$modulename/templates/$viewname.tpl

The actual HTML for each view is in a template file with the same name as the view.

modules/$modulename/templates/blocks/blocks.inc

A PHP file that tells each theme what blocks are available and what template file to load for each one.

modules/$modulename/templates/blocks/*.tpl

Contains the template code for blocks specified from blocks.inc.

modules/$modulename/classes/*Helper.class

*Helper.class files are optional. They are used to organize the code for things that are used in more than one view or simply to break up large code into managable chunks.

modules/$modulename/classes/*Interface.class

Interfaces are a way for multiple modules to share a common method for doing similar tasks. For example, the Search module implements a search interface that other modules build off of to add search capabilities to their module.

modules/$modulename/classes/Maps.xml

Database table schemes are stored in an XML file for portability. Gallery does the work to make it compatible with various engines. They are optional if the module doesn't store any data in the database.

Module Callbacks

  • registerEventListeners: Add advanced functionality
  • getSiteAdminViews: Insert links in site administration
  • getItemAdminViews: Insert links for items administration
  • getUserAdminViews: Insert links for user administration
  • getSystemLinks: ???
  • getItemLinks: Insert public links for items
  • getItemSummariesm: Insert summary content about the item

Coding a Dummy Module

Preface

Now that you have a basic grasp of the G2 system, let's dive in and code a basic module.

Module Structure

All modules in G2 must have their own directory (folder) under modules/ as well as a module.inc file within that directory. Go ahead and create both using your preferred method. Name the directory 'tutorial1'.

Tip: in *nix systems, the touch command is useful for creating empty files.

Great! You now have the following:

modules/tutorial1/           (directory)
modules/tutorial1/module.inc (empty file)

module.inc

module.inc is the very 'core' of your module. It tells G2 things like the module's name, description, version as well as a whole myriad of other nifty stuff - so let's fill it in.

First, we tell the world that this is a PHP script. Easy enough, just start with:

<?php

Now, let's paste in the standard G2 boilerplate.

/*
 * $RCSfile: module.inc,v $
 *
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2006 Bharat Mediratta
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
 */

Then, we want to fill in some administrative data. You don't have to change any of this. This data is not used by our module for anything, but G2 uses it for management purposes. If you want to you can edit out Bharat's name and replace it with your own. I'm sure he wouldn't mind.

/**
 * @package Tutorial1
 * @version $Revision: 1.1 $ $Date: 2006/01/02 07:58:13 $
 * @author Bharat Mediratta <bharat@menalto.com>
 */

/**
 * Tutorial1 module
 *
 * My first module!
 *
 * @package Tutorial1
 */

Next, we tell G2 that our tutorial1 module is providing extended functionality to the main G2 module.

class Tutorial1Module extends GalleryModule {

Now we need to fill in the required data for the module. We do this by editing the module's Tutorial1Module function.

    function Tutorial1Module() {
        global $gallery;

        $this->setId('tutorial1');
        $this->setName('Tutorial Module 1');
        $this->setDescription($gallery->i18n('My first module.'));
        $this->setVersion('0.9.0');
        $this->setGroup('data', $gallery->i18n('Extra Data'));
        $this->setCallbacks('');
        $this->setRequiredCoreApi(array(7, 0));
        $this->setRequiredModuleApi(array(3, 0));
    }

The first three fields are fairly self-descriptive. The setVersion parameter is simply a version you want to use for your module. We'll use 0.9.0 to start. setGroup refers to the group the module will be placed under the the Site Admin pages. We don't need to set any callbacks just yet, so we leave that blank.

Our RequiredCoreApi and RequiredModuleApi versions must match G2's provided versions, or things will break. Want to know your G2's latest API version? Check one of the G2 Team's modules in your install and use the numbers from there. Here, we're using the CoreAPI v 7.0 and ModuleAPI v 3.0.

We're done! Let's close it up with:

}
?>

You've now coded your first G2 module! Simple, wasn't it? You'll find that all of your future modules will follow the same structure.

The full code is available here (from an older G2 version).

Using your Module

It's hard to use something with no functionality, but provided everything went smoothly you'll be able to login to your Site Admin intefaces, click Modules, scroll down to the Extra Data section and install, activate, deactivate and uninstall your module. Fun, isn't it?

Wrap Up

Congratulations! You've just created a fully compatible, standards-compliant, wonderful, amazing, brilliant module for G2. Give yourself a pat on the back and continue to Lesson 3 where you'll learn to create a module that starts using the templating engine.

A module that displays static pages

A note of warning: The example below is outdated and will be updated soon. It is left here for reference until a replacement has been added.

Preface

Now that you know how to create a proper G2 module let's go one step further and create a module that uses the G2 templating engine to show static pages.

Preparation

To begin, create a modules/tutorial2 directory. There's no sense in going over pasting the G2 boilerplate and author/date/email data again, so we'll continue on where you left off with the last module. Copy the old module.inc into the new directory but make sure to change all instances of tutorial1 to tutorial2 and Tutorial1 to Tutorial2.

Also, let's create the following files and directories. You can leave the files empty for now.

modules/tutorial2/MyPage.inc
modules/tutorial2/templates/
modules/tutorial2/templates/MyPage.tpl

module.inc

Great news, we don't need to make any changes to module.inc! Let's continue and open the MyPage.inc file.

MyPage.inc

In G2, it is not possible to access templates directly. Rather, you must use a view that will prepare all the necessary data and then load the template itself. MyPage.inc is such a view.

Let's start by pasting the regular G2 boilerplate.

<?php
/*
 * $RCSfile: MyPage.inc,v $
 *
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2006 Bharat Mediratta
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
 */

Fill in the other standard data.

/**
 * @version $Revision: 1.1 $ $Date: 2006/01/05 07:00:00 $
 * @package Tutorial2
 * @subpackage UserInterface
 * @author Your Name <you@email.com>
 */

/**
 * This view shows a static page.
 *
 * @package Tutorial2
 * @subpackage UserInterface
 */

Phew! The physical structure of module.inc may seem familiar to you so you might be able to guess what's coming. We want to extend the GalleryView class with your own class.

class MyPageView extends GalleryView {

Now, our module doesn't have to do any data manipulation so we only need two functions. The main one is loadTemplate.

    /**
     * @see GalleryView::loadTemplate
     */
    function loadTemplate(&$template, &$form) {
        global $gallery;

In this function we tell G2 everything we want to do before loading our template. For our page, we'll want a nice visual G2 wrapper, including the sidebar, so we must load the Navigation view and load the template for the Navigation view.

        list ($ret, $navigationView) = GalleryView::loadView('core:Navigation');
        if ($ret) {
            return array($ret->wrap(__FILE__, __LINE__), null);
        }

        list ($ret, $ignored) = $navigationView->loadTemplate($template, $form);
        if ($ret) {
            return array($ret->wrap(__FILE__, __LINE__), null);
        }

Take note of the structure of these calls, as it is fairly important. It is repeated throughout a lot of the G2 code. Also note that we've loaded the navigation view, but have not done anything with it just yet. However, it is by loading the navigation view that we can include the G2 sidebar in the template file.

Let's continue by loading the template.

        return array(null,
                     array('body' => 'modules/tutorial2/templates/MyPage.tpl'));
    }

If we've gone this far, the operation has been a success. Therefore, we tell G2 that we've succeeded and load the appropriate template file.

Ever notice those nice "back to ____" nagivation links on the sidebar? Let's incorporate this feature into our module. We do this by creating another function.

    /**
     * @see GalleryView::getViewDescription()
     */
    function getViewDescription() {
        list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'tutorial2');
        if ($ret) {
            return array($ret->wrap(__FILE__, __LINE__), null, null);
        }

        return array(null, $module->translate('My Page'));
    }

Feel free to change "My Page" to whatever you heart desires. Finally, we close the PHP script and the class.

}
?>

Phew! While it looks like a lot of code for such a simple task, most of the code can be reused over and over in your subsequent modules. You'll find that once you know how to code a basic module you can simply keep building on your existing knowledge to easily code bigger and bigger modules.


Let's continue with our last file, the template.

templates/MyPage.tpl

As you should remember, the last call in the loadTemplate() function is, well, the loading of the template. G2 uses the Smarty templating engine which provides excellent code and design separation. You'll find that you can use standard HTML in the template files, which makes them much easier to edit.

Let's get started.

{*
 * $Revision: 1.1 $
 * If you want to customize this file, do not edit it directly since future upgrades
 * may overwrite it.  Instead, copy it into a new directory called "local" and edit that
 * version.  Gallery will look for that file first and use it if it exists.
 *}
<div id="gsSystemLinks" class="gcBorder1">
  <ul class="gbBreadCrumb">
    <li class="firstChild">
      <a href="{g->url}">My Gallery </a>
    </li>
    <li>
      {g->text text="My Page"}
    </li>
  </ul>
</div>

This creates the nice breadcrumb navigation at the top. Note how G2 system calls are still used in the templates, but not extensively. All Smarty calls are enveloped in the curly brackets which sometimes results in funny results (horrible errors) if you try to use JavaScript, CSS, or any other HTML-related oddity that uses curly brackets. If you want to do so, just wrap your code with {literal}..{/literal} tags to tell Smarty that you don't want it to interpret it.

It is also good form to use {g->text text="your text here"} to output text, but this is obviously not required. Personally, I recommend you do it regardless as it makes it possible to easily localize your module.

Let's fill in the rest of the template.

<table width="100%" cellspacing="0" cellpadding="0">
  <colgroup width="1*,*"/>
  <tr valign="top"><td>
    {include file="gallery:`$Navigation.sidebar`" l10Domain=$Navigation.l10Domain}
  </td><td>

  <div id="gsContent" class="gcBorder1">
    <div class="gbBlock gcBackground1">
      <h2>{g->text text="Look, it's my first page! And it's got a title."} </h2>
    </div>

    <div class="gbBlock">   
    <p class="giDescription">
      {g->text text="Hey, cool! I can write stuff here."}
    </p>
    </div>

    <div class="gbBlock">   
    <p class="giDescription">
      {g->text text="Look, when I do this G2 makes me a nice little divisor."}
    </p>
    </div>

  </div>
  </td></tr>
</table>

I hope that the template is fairly self explanatory. As mentioned earlier, most of the things you can do with HTML you can also achieve with the templating engine. Play around with the template until you think you have it looking the way you want it.

Accessing your Page

We have not made any links in G2 to our page, so how do we access it? Luckily, G2 (the standalone version, at least) has a standard method of accessing modules and their views:

http://www.example.com/main.php?g2_view=tutorial2.MyPage

You should be able to see your new static page wrapped nicely with the G2 layout and CSS.

Working With the Database

In G2 most of the DB interaction is done through helper classes that are generated using a series of tools. This section will discuss the files that are needed, the tools that are needed and the procedures needed to get simple database operations working.

There are two types of tables. Map tables and Entity tables. Entity tables store objects like users, images or comments, and have a matching PHP file in the module's classes directory. You might guess that an image would have different fields (path, name, description, etc) than a comment (subject, commentorId, comments, etc), hence they are both entities but reside in different tables. Map tables contain any other data a module may need to store.

There are lots of files that are needed to make even the most simple DB operations work, however for the most part there is only one main file that actually needs edited intelligently.

You will need these files. Copy the files from another module (like comment) and make any directories that are needed.

dbExample/classes/GNUmakefile
dbExample/classes/GalleryStorage/GNUmakefile

The GNUmakefiles are not very important to understand, except that they are the method that all of the generated code is made. To run them you will need 'gmake' or 'make' and commandline PHP.

Entities are defined with a PHP class. See modules/comment/classes/GalleryComment.class as an example. Comments with @g2 tags define the database structure. The class must define get/set functions for all its data.

Maps are defined in classes/Maps.xml. See modules/customfield/classes/Maps.xml as an example. The structure of the table is defined in XML.

Once the entity and/or map definitions are ready, cd into the classes directory and do 'make' (or gmake). This will build the GalleryStorage/schema.tpl file and classes/Maps.inc (if there is a Maps.xml file).

advertisements