Gallery 3 Developer Handbook

Table of Contents

Gallery 3 Developer Handbook

Table of Contents

Overview

About This Document

Albums, Photos and Movies

File Storage

Thumbnails and Resizes

Users and Groups

Permissions

Design Concepts

Framework

Models, Views and Controllers

Helpers

Libraries

Themes

Events

Database Abstraction Layer

Object Relational Mapping (ORM)

Modified Preorder Tree Traversal (MPTT)

Kohana Cascading Filesystem

Controllers

*

Models

Item Types

Views

URLs and URL Routing

APIs / REST

Authentication

Internationalization

Simple internationalization

Internationalization with variables

Internationalization with plural forms

Graphics Toolkits and Rules

Embedding

Security

Forms

Life of a Gallery 3 Request

Contributing Code

Themes

Gallery3 Modules

Basic Module Layout

Module Lifecycle

Module Hooks

Event

Theme

Block

RSS

Task

Installer

REST

Overview

Gallery 3 is a simple and powerful web based photo management system that lets you host your photos on your own website.  Out of the box it is designed to support the common needs for the common users in a simple, elegant and powerful fashion.  In order to be successful this necessarily requires Gallery 3 to be limited in scope and to choose simpler options wherever possible. This approach reduces the amount of code that the small developer team needs to support and increases the rate at which they can product new versions of the product.

To cope with varying individual needs, Gallery 3 is broadly extensible.  Internally it uses a system of modules and themes that can be replaced, extended and added.  The goal of Gallery 3 is to make it as easy as possible to add a new feature to the system by creating new modules.  At the time of this writing, there are over 100 modules available for Gallery 3.

The goal of this documentation is to explain the fundamentals so that a new developer can create their own features and themes for Gallery 3 and share them with the rest of the community.

About This Document

This document contains detailed information and code samples demonstrating how to create modules and themes.  In the code samples, the module id will always be example in bold/italics, ie:

“...create your module.info file in gallery3/modules/example/module.info...”

“...the event helper will be called example_event...”

When you create your own module or theme, replace this with the actual id of your module.

Application Concepts

Albums, Photos and Movies

At the highest level, Gallery 3 is a single album that can contain other albums, photos and movies.  Each contained album has its own separate title, description and other metadata and is controlled by a set of permissions.  There is one single album at the top of the entire hierarchy, which is called the root album.  Photos, movies and albums can be moved around inside the hierarchy.

File Storage

Gallery 3 stores its data files in the gallery3/var/albums directory.  Photos and movies have an associated data file while albums are associated with a directory.  The gallery3/var/albums directory is maintained so that it always represents the same structure that is visible via the web interface.  This makes it easy to understand the data structure and allows other applications to inter-operate with the data you store in Gallery 3.

Thumbnails and Resizes

Every album, photo and movie has a thumbnail which is a small, automatically generated graphical representation of that item.  In the case of photos and movies, this is usually a scaled down version of the photo or the first frame of the movie.  However album thumbnails are derived from the album cover which is a photo, movie or album chosen to represent the album.

Since photos are typically very large, they have a resize which is an intermediary version of the image that is more detailed than the thumbnail but still viewable in the web browser.  There is a single resize version for each photo.  Movies and albums do not have a resize.

For more information on the metadata that’s associated with an individual album, photo or movie see the Models section below.

Users and Groups

In Gallery 3 a user is a representation of a real world person.  Individual users can log in to Gallery 3 with a password and be granted special permissions.  Visitors who do not log in as a specific user are automatically assigned as the guest user.  Users have an associated username, email address and an optional full name and URL.

There are three types of users in the system:

admin - These are users with privileges to access the admin interface.  The first user created by the Gallery 3 installer is an admin (and has the username "admin").   Admin users can perform administrative tasks in Gallery 3 including turning other users into admin users.    There must be at least one admin user, but there is no upper limit on the number of admin users.

guest - This is the special user assigned to visitors to Gallery 3 who do not have a regular account.  There can only be one guest account.

regular - These are users who are not admins and not guests.  There can be any number of regular accounts.

Groups are collections of users which allow Gallery 3 administrators to treat many users in a similar fashion easily.  Each group has an associated name, and Gallery 3 administrators can create, delete and rename groups to their liking.  By default there are two special groups in Gallery 3 that cannot be deleted (though they can be renamed):

Everybody - This group refers to every user including guests.

Registered Users - This group refers to logged in users excluding guests

Permissions

Each album in Gallery 3 has an associated set of permissions that govern what groups of users can do with the album and its contents.  Permissions operate at the intersection of albums and groups - this means that administrators can allow a group to take a specific action with an album.  You can not assign a permission specifically to a photo or a movie, and you can not assign a permission to a single user.  In both cases, you must wrap the photo or movie in an album or wrap the single user in a group before assigning permissions.  Imposing this limitation has had significant positive impact on performance, code comprehension and code size.  However note that there are several proposals to broaden the permission model in the upcoming 3.1 release.

Design Concepts

Framework

Gallery 3 is built on top of Kohana 2.3, a lean and fast application framework.  Some of the design concepts below are similar or identical to the way that Kohana works.  As a result, almost all of the well documented Kohana 2.x APIs are available to the Gallery 3 module and theme developer.   New developers are encouraged to familiarize themselves with the APIs that Kohana makes available.

Gallery 3 has the following top level structure:

application - This is a minimal Kohana application that is a wrapper around Gallery.  It’s required by Kohana, but serves little purpose except to bootstrap Kohana.

system - This is the Kohana framework code and core libraries.

modules - These are individual Gallery 3 modules.  The core Gallery 3 application is located in modules/gallery and it loads up any other Gallery 3 modules that are installed and activated.

themes - These are individual Gallery 3 themes.  The gallery module loads the appropriate theme based on the user request.

lib - Gallery 3 specific common Javascript and CSS libraries used by modules and themes

installer - Gallery 3 installer.  This is a mostly separate code base designed to bootstrap the initial install of Gallery 3.

Kohana is an opinionated framework.  There is a right way to write the code and if you do it that way, then it's very easy and you don't have to write much code.  One way that you'll see this is that Kohana expects certain files to be in certain places.  A fully fleshed out example module would have this directory structure:

example

├── config

├── controllers

├── css

├── helpers

├── hooks

├── images

├── js

├── libraries

│   └── drivers

│       └── ExampleDriver

├── models

├── tests

└── views

Very few modules need this many directories!  If you don't have any controllers, you wouldn't bother to create a controller directory.  But if you do have a controller and you put it in the controllers directory then Kohana knows exactly where to look for it.  This means that you don't have to do anything other than write a few lines of controller code then name and place the file the right way and Kohana will immediately start using it.  In Kohana, this feature is known as the Cascading File System, and it allows most modules to remain relatively small because they contain little to no boilerplate code.

Models, Views and Controllers

Gallery 3 uses a common architecture called Model / View / Controller (MVC).  This is a simple division of labor between different parts of the code that makes it easier to separate out concepts.  There is extensive documentation about this concept on Wikipedia.

Models contain data and make sure that it’s sane.  An Item_Model contains all data about an individual album, photo or movie, while the User_Model contains all data about an individual user.  Each model typically maps to a row in the database and the model code is responsible for ensuring data integrity.  For more information including detailed examples, refer to Kohana’s documentation on Models.

Controllers are the essential application.  They interpret requests from the user, then use all the other building blocks available in the framework to take the appropriate action and return the correct response.  Controllers typically process requests from a web browser, load up one or more models and then delegate to a View to render an HTML response back to the web browser.  A controller’s responsibility can be as simple as printing out an HTML response, or as complex as supporting uploading a photo or creating a new album.  Because Controllers are typically associated with a type of output, they shouldn’t contain much complexity.  Instead, they should delegate the hard work to helpers and libraries.  For more information including detailed examples, refer to Kohana’s documentation on Controllers.

Views contain the browser specific display information for your application.  Typically this is HTML output.  In Gallery 3, a view is a PHP file which is predominantly data, but has embedded PHP code to render relevant data.  Controllers load up data and make it available to the view which then renders the output.  For more information including detailed examples, refer to Kohana’s documentation on Views.

Helpers

A helper is a concept employed in many application frameworks, including Kohana.  It is a class that contains helpful static methods grouped together around a single concept.  Kohana comes with a wide range of useful helpers, and each Gallery 3 module can provide its own.  For more information including detailed examples, refer to Kohana’s documentation on Helpers.  One caveat on Kohana’s docs is that in Gallery 3, all helpers should be associated with a module and live in the module’s helpers directory.

Note that library class names must start with an lowercase letter for the Kohana Cascading Filesystem to find them properly.

Libraries

A library is a concept in the Kohana framework.  It’s similar to a helper in that it contains helpful methods grouped around a single concept.  The difference between libraries and helpers is that libraries are not static.  You must create an instance of a library in order to use it.  This allows you to initialize a library with relevant data and then call a series of functions on it.  Fore more information including detailed examples, refer to Kohana’s documentation on Libraries.

Note that library class names must start with an uppercase letter for the Kohana Cascading Filesystem to find them properly.

A typical pattern in Kohana is that libraries can use drivers to support multiple implementations for a common interface.  For example, the authentication system is implemented in the IdentityProvider class in modules/gallery/libraries but the authentication system is designed to be modular so that you can pick your own preferred way to authenticate.  There is a driver interface in modules/gallery/libraries/drivers/IdentityProvider.php and then the user module has its own implementation of this interface in modules/user/libraries/drivers/IdentityProvider/Gallery.php

Modules

A Gallery 3 module is a collection of models, views, controllers, helpers and libraries.  Modules are installed and activated via the Admin > Modules web interface.  Once activated, all components of the module are available for use.  The module’s controllers become routeable URLs in the web interface and  its helpers, libraries, models and views become available to any other module or the active theme.

For more details on how to design and build Gallery 3 modules, see the "Creating Gallery 3 Modules" section.

Notes about modules:

  1. There can be an unlimited number of modules active at any given time.
  2. If multiple modules provide a helper, library, controller, view or model with the same name, the module that comes first in the module load order takes precedence.
  3. Modules are responsible for providing functionality and features to the application.  They are not responsible for how the feature looks, that’s left to the theme.
  4. Each module must have its own unique name and that must be the name of its directory

Themes

A Gallery 3 theme is the skin for the application.  Where modules provide functionality, it’s up to the theme to figure out where the feature should be placed on the page and how it should be decorated with colors, fonts, borders, etc.  The theme takes precedence over all modules and can override any controller, view, helper, library or model in the system.

There are two types of themes:

site - This is the main theme for the site.  When a user browses the Gallery, this is the theme that is used.

admin - This is the theme for the admin interface only.

Any Gallery 3 install can have a site theme and an admin theme.  They are designed separately because typically the admin interface has very different needs from the site interface, and it will have less users who are more savvy.  As a result, there will probably be far less admin themes, and many more site themes.  This relieves the average themer of the burden of writing a nice admin theme; they can focus on just creating a beautiful site theme.

Events

Because Gallery 3 is modular in its nature, it’s important to let modules be loosely coupled.  Tight bindings between modules (where one module directly calls into another one) reduces modularity and limits the possible ways that Gallery 3 can be deployed.

In Gallery 3, this loose coupling is accomplished by using an event system.  Modules can publish arbitrary events whenever they choose, and any other module can listen for that event and take action on it.  The gallery module has a set of events that it publishes, and many modules listen for and take action on these events.  [link to complete event reference] 

To implement an event listener for your module, you must create a special kind of helper called example_event.  Any event that’s triggered will result in a static method call to this helper.  For example, when an Item_Model is saved it will generate a “item_updated” event.  This will attempt to call example_event::item_updated().  The item_updated() event takes two arguments, the original Item_Model and the newly updated Item_Model so the event handler can figure out what changed and take an appropriate action.

Notes about events:

Database Abstraction Layer

Gallery 3 uses the Kohana Database Abstraction Layer.  This is well documented on the Kohana website:

Notes about the Gallery 3 database abstraction layer:

Object Relational Mapping (ORM)

Gallery 3 takes advantage of Kohana’s ORM library.  This is a powerful abstraction that lets you treat a row in the database as an object in PHP.  The object is defined by the SQL table definition with a strict correspondance between the ORM name and the table in the database.  Once this mapping has been created, you can do most create, read, update and delete operations on an ORM instance without touching any SQL.

The bulk of data operations in Gallery 3 are done using ORM for simplicity.  However when operating across many rows in the database at once, ORM can be very inefficient.  In those cases, you may wish to use the Database Builder and create custom SQL queries.  Almost all models in Gallery 3 are instances of the ORM class.

To create and use your own ORM, you’d start by creating a database table in your module installer.  The tag module installer creates a table in the database with code like this (found in modules/tag/helpers/tag_installer.php):

$db = Database::instance();

$db->query("CREATE TABLE IF NOT EXISTS {tags} (

             `id` int(9) NOT NULL auto_increment,

             `name` varchar(128) NOT NULL,

             `count` int(10) unsigned NOT NULL DEFAULT 0,

            PRIMARY KEY (`id`),

            UNIQUE KEY(`name`))

            DEFAULT CHARSET=utf8;");

Note that the table name is plural and prefixed in this database query.  The tag module has a model in modules/tag/models/tag.php like this:

class Tag_Model_Core extends ORM {

}

Now any code in the tag module can create a new tag like this:

$tag = new Tag_Model();  // or ORM::factory(“tag”)

$tag->name = “My Tag”

$tag->save();

To load a tag with a given id, you can do a simple query for it:

$tag = ORM::factory(“tag”)->where(“id”, “=”, 123)->find();

if ($tag->loaded()) {

  // found it!

}

Note that when you refer to an ORM, it’s in the singular (“tag”) whereas the table name in the database is always plural (“tags”).

Modified Preorder Tree Traversal (MPTT)

For storing hierarchical data in the database, Gallery 3 uses the ORM_MPTT subclass to ORM which implements Modified Preorder Tree Traversal.  This is a technique for keeping track of parent/child relationships for a tree of data which allows for extremely efficient read access.  There’s a great description of the concept and how it works on Sitepoint.com that you should read if you plan on working with MPTT.  The ORM_MPTT class does the bulk of the work for you.  Using it is very simple, simply add left_ptr, right_ptr, parent_id, and level integer columns to your database table and then have your model extend the ORM_MPTT class instead of ORM.

Kohana Cascading Filesystem

Gallery 3 makes heavy use of the Kohana Cascading Filesystem, which is designed to allow you to not worry about finding and including code before you use it.  When your code refers to a helper, Kohana searches all the helpers directories in order until it finds the helper that you’re referring to, auto-loads this class for you and then calls the method you’re referring to.  In order for this to work, Kohana has to know exactly where to look.

Supposing you have the following modules in your Gallery 3 install and they are activated in the following order: alpha, beta, gamma.

modules

├── alpha

│   └── helpers

│       ├── chart

│       └── chart.php

├── beta

│   ├── helpers

│   └── libraries

│       └── Ball.php

└── gamma

    ├── config

    └── libraries

        └── Ball.php

    └── helpers

        └── chart.php

If you write the following code:

$ball = new Ball();

chart::draw();

The Kohana Cascading Filesystem will load modules/alpha/chart.php because that’s the first instance of the chart helper class it finds, and it will load modules/beta/Ball.php because that’s the first instance of the Ball class that it can find.  In your code you do not need to include these files yourself.

Note: Typically, it’s a bad practice to rely directly on a helper or library from any module other than the gallery module.  

Controllers

A typical controller is a class in modules/example/controllers.  The URL to the controller corresponds to the controller class name and a function name inside the controller.  For example if you’d like to create a controller for the following URL:

http://example.com/gallery3/index.php/foo/bar

You would create a file called modules/example/controllers/foo.php containing this code:

class foo_Controller extends Controller {

  public function bar() {

    print "Hello world";

  }

}

When your module is installed and activated, the bar() function will respond to both GET and POST requests made to the /gallery3/index.php/foo/bar URL.

A typical controller would do more than just print out some text.  It would probably load up one or more models and then delegate to a view to print out some HTML to render on the web page.  For example:

class foo_Controller extends Controller {

  public function bar() {

    $view = new View("foo_bar.html");

    $view->item = item::root();

    print $view;

  }

}

This controller loads the root album and then looks for a view called foo_bar.html.php and loads it.  When inside that view, the variable $item will be set to the root item.  If in this example you want to print out the title of the root album, you could create modules/example/views/foo.html.php containing the following code:

Gallery title: <?= $item->title ?>

If there are multiple views called foo.html.php the version chosen would be determined by the module load order.

Notes about controllers:

Models

Item Types

Views

A view is a PHP file that lives in the views directory of your module, typically used to display HTML content to the web browser..  Controllers and helpers can access views by using Kohana’s View  PHP class.  Views are typically predominantly HTML with some PHP sprinkled in to show data from models and other sources.

To display the title and owner of an item, the controller and view combination might look like this:

modules/example/controllers/title_and_owner.php:

<?

class Title_And_Owner_Controller extends Controller {

  public function show($id) {

    $item = ORM::factory("item", $id);      // Load the item model

    access::required("view", $item);        // Check permissions

    $v = new View("title_and_owner.html");  // Load up our view

    $v->item = $item;                       // Set a view variable

    print $v;                               // Print results

  }

}

modules/example/views/title_and_owner.html.php:

<p>

  Hello <b><?= identity::active_user()->name ?></b>!

</p>

<p>

  You’re looking at <?= $item->title ?> which was

  uploaded by <?= $item->owner->name ?>

</p>

While the view has the power to make PHP calls directly, it's a good idea to keep them as simple as possible.  When using conditionals or looping in a view, it's common to use PHP's Alternative Control Syntax which is easier to read in templates.  For example, to use both looping and conditionals in a template, you might have this:

<? if (!empty($items)): ?>

<ul>

  <? foreach ($items as $item): ?>

  <li>

    <a href="<?= $item->url() ?>">

      <?= $item->title ?>

    </a>

  </li>

  <? endforeach ?>

</ul>

<? endif ?>

The alternate syntax meshes reasonably well with HTML markup.  Curly brace style syntax is much harder to read inside templates.  If you find yourself writing large chunks of PHP with curly braces in your view, consider moving that code to a controller or a helper.

URLs and URL Routing

Gallery 3 uses Kohana's routing system to connect a url to a controller.  There is a simple mapping between the URL that you see in the address bar of your browser and the function name in a controller that's executed.  

For this URL:

http://example.com/gallery3/index.php/tag/add/3/foo

       ^           ^        ^         ^   ^   ^

       │           │        │         │   │   ├── function arguments

       │           │        │         │   ├── function named "add"

       │           │        │         ├── controller "Tag_Controller"

       │           │        ├── main Gallery 3 PHP script, edit
      │           │            gallery3/.htaccess to remove this

       │           ├── directory where Gallery 3 is installed

       ├── hostname of your website

Gallery 3 expects that there will be a controller called Tag_Controller containing a function named add.  This controller can be in any module, if there are two controllers with the same name the module load order will break the tie.  Anything data after the function name will be passed to the function as arguments.  In the above example, you might have the following code:

modules/example/controllers/tag.php

<?

class Tag_Controller extends Controller {

  public function add($item_id, $tag_name) {

    // $item_id == 3

    // $tag_name == "foo"

  }

}

When generating Galley 3 URLs, use the url::site() function from the Kohana URL helper to refer to a controller and function.  For example to generate the url above, you would do:

<?= url::site("tag/add/3/foo") ?>

<?= url::site("tag/add/3/foo?q=1") // query params are ok ?>

<?= url::site("items/{$item->id}") // variables are fine ?>

Gallery 3 extends the URL helper in modules/gallery/helpers/MY_url.php to add the following additional methods:

abs_file - same as url::file() except it returns the absolute url

abs_site - same as url::site() except it returns the absolute url

current - return the server-relative current url

abs_current - same as url::current() except it returns the absolute url

merge - same as url::merge except that it escapes XSS

merge_querystring - merge an extra query string into the given url

APIs / REST

Gallery 3 supports a REST API which is documented here: http://codex.gallery2.org/Gallery3:API:REST

Authentication

By default, Gallery 3 has its own standalone authentication system.  The user module stores information about each user in the database and can authenticate users by username and password.  There are three types of users

However, Gallery 3 also comes with a pluggable authentication system that supports the concept of identity providers.  An identity provider is an authentication system that can provide the following basic functions:

User functions

Group functions

The user module provides this functionality in modules/user/libraries/drivers/IdentityProvider/Gallery.php and uses ORM to interact with User_Model and Group_Model for the data it needs.

Internationalization

All user visible strings in Gallery 3 are internationalized.  Internationalization is the process of preparing a string so that a volunteer can translate it into their native language.  Internationalized and localized strings are stored in the database, not on the filesystem.  

There are two global functions used to internationalize all strings, one for singular strings and one for strings containing plural forms.

Simple internationalization

If you have a simple string that has no plural forms in it and no variables in it, then localization is very simple using the t() function.  Simply pass your string in as an argument, eg:

In a controller, helper or library:

$hello_world = t("Hello, world");

In a view:

<?= t("Hello, world") ?>

Internationalization with variables

If you need to craft a string with variable values in it, you can use the optional array argument to the t() function call and embed placeholders in your internationalized string.  Placeholders are words prefixed by a percent (%) sign.  The placeholder word matches a key in the optional array argument.  You can have as many placeholders as you want.  For example:

In a view:

<?= t("Hello, %name", array("name" => $user->name) ?>

In a controller, helper or library:

$info = t("%name created %title",

          array("name" => $user->name,

                "title" => $item->title));

Internationalization with plural forms

Each locale has its own rules for plural forms and the rules get quite complicated, but to properly internationalize a string with plural forms in it, you simply have to separate the singular form of the string from the plural form and use the t2() call with the special %count variable in your internationalized string.  In the singular form, you do not need to use the variable.

In a view:

<?= t2("I have one apple", "I have %count apples", $count) ?>

You're also free to use plural forms with variables, for example:

In a controller, helper or library:

$info = t2("Hey %name, you have %count apples",

           $count,

           array("name" => $user->name));

Any internationalized string you add to your module will be detected by Gallery 3 when the administrator browses to the Admin > Languages page and updates their translations.  Localizers who install your module can localize your strings into their native language and upload those localizations to the Gallery website which will in turn make those localizations available to all users of the module.

Notes about internationalization:

Graphics Toolkits and Rules

At its core, Gallery 3 is a graphical image processing application.  In order to work it needs to be able to process a full size image and generate smaller resized versions, and thumbnails so that they can be properly viewed.  The gallery module contains a framework for processing image rules and some basic rules.

There are only two types of intermediate images: thumb and resize.  Each one has an associated set of rules stored in the graphics_rule ORM.  By default there is a single rule for generating a thumbnail and it looks like this:

target: thumb

module_name: gallery

operation: gallery_graphics::resize

args: { width: 200, height: 200, master: Image::AUTO }

priority: 100

This rule is created by the gallery module installer and specifies that when it is necessary to build a thumbnail, it will call the resize function in the gallery_graphics helper with the arguments provided.  Since there is only one rule in the pipeline, the source of the operation will be the full sized image found in var/albums, and the result will be the thumbnail stored in the appropriate place in var/thumbs.

The priority field specifies the order in which operations are performed.  When the watermark module is active and the admin has specified a specific watermark to use, that module introduces a new rule:

target: thumb

module_name: watermark

operation: gallery_graphics::composite

args: { file: "/path/to/watermark.gif", width: 105, height: 40, ...}

priority: 1000

Note that this rule runs at priority 1000.  So when generating a thumbnail, Gallery 3 will first run the resize function to size the image down, then it will call the composite function to add the watermark image on top.

To add a new rule, you must use the graphics::add_rule() function.  It takes the following arguments:

$module_name - the name of your module

$target - the target image type ("thumb" or "resize")

$operation - the static helper function to call

$args - the arguments to the function (can be an array or a single value)

$priority - the priority in which this operation should be called

Each module can provide its own graphics functions.  A graphics function is merely a helper call which takes an input file, an output file and a set of optional values.  When it's time to rebuild an image, the Gallery 3 framework will call graphics::generate() on an Item_Model that has a thumb or a resize which needs to be rebuilt, and then this code will then run every rule in order as appropriate.  If you want to introduce your own rule that pre-processes the full size image to sharpen it before the thumbnail is generated, it would look like this:

modules/example/helpers/example_installer.php:

<?

class example_installer {

  static function install() {

    // Normal install code

    graphics::add_rule("example", "thumb",

                       "example_graphics::sharpen",

                       array("amount" => 10),

                       80);

  }

}

This introduces the new rule into the system.  Now you must write the code for the sharpen function:

modules/example/helpers/example_graphics.php

<?

class example_graphics {

  static function sharpen($input_file, $output_file, $options) {

    Image::factory($input_file)

      ->sharpen($options["amount"])

      ->save($output_file)

  }

}

Note that this uses Kohana's built in Image library which provides basic functionality for simple graphics operations using the active graphics toolkit.  Create a static helper as a wrapper around this code to logically group multiple operations together or introduce more complex logic if necessary.  For example, gallery_graphics::resize() also makes sure that the image does not get upscaled if the input file is going to be smaller than the output file.

The following basic graphics functions are available via the gallery module in the gallery_graphics helper:

gallery_graphics::rotate

Rotate the image to a new orientation while preserving the aspect ratio.

Options:

degrees - any integer value from 0 to 360

gallery_graphics::resize

Resize the image to a smaller size while preserving the aspect ratio.  This will not upscale (ie: generate an image of a larger size).  This function creates a box of the size that you desire, then figures out how to fit the image into it while preserving the aspect ratio.  The master option allows you to specify whether you want to consider the width or the height to be the master dimension (the dimension to try to fit inside the box).  If you choose Image::NONE as the master dimension, it will change the aspect ratio of the image to be the new dimensions you specify (damaging the image).

Options:

width - the new width of the resized image

height - the new height of the resized image

master - Image::AUTO, Image::WIDTH,  Image::HEIGHT or Image:NONE

gallery_graphics::composite

Overlay a second image on top of the first one (eg watermarking).  

Options:

file - the path on disk to the overlay file

position - a string representing a cardinal direction: northwest, norh, northeast, west, center, east, southwest, south, southeast

transparency - an integer value from 0 to 100

padding - the number of pixels to pad the overlay from the edge of the source file

Notes about graphics rules:

Embedding

Review http://codex.gallery2.org/Gallery2:Embedding before writing this.

Security

Forms

Life of a Gallery 3 Request

Contributing Code

In software projects where there are many contributors, it's important to have coding standards.

Themes

Gallery3 Modules

A Gallery 3 module is a collection of models, views, controllers, helpers, libraries and config files gathered in a single directory located under gallery3/modules.  All of those elements are optional and are used to add specific features and capabilities to Gallery 3.  There is only one mandatory file in a Gallery 3 module and that is the module.info file that contains the name, description and version of the module.  

Basic Module Layout

The simplest module looks like this:

hello_world

└── module.info

Here's what the module.info file could contain:

name = "Hello world!"

description = "This module does nothing!"

version = 1

This module can be activated and deactivated in the Admin > Modules interface, but won't actually do anything.  To add functionality to the module, you can add controllers, event hooks, rest hooks, search hooks, theme hooks, RSS hooks, task hooks, block hooks, an installer and more.  Each of these bits of functionality are easy to add and mostly independent of each other so it's simple to extend Gallery 3 in many directions without writing much code.

Gallery 3 module hooks are very simple.  Each hook system expects there to be a specially named helper with specially named functions in them.  Once you understand the pattern it's simple and easy to add a new hook with just a few lines of code.

For example, if you'd like to print out the words Hello world at the top of every album, you would need to add a theme hook to your module.  In the helpers directory, you'd create a file called hello_world_theme.php containing the following code:

<?

class hello_world_theme {

  static function album_top($theme) {

    return "Hi";

  }

}

A few things to note about this:

There are many other theme hooks, this is just a simple example.  But with these two files you can affect many useful changes in Gallery 3.

A more complex Gallery 3 module might look like this:

modules

└── search

    ├── controllers

    │   └── search.php

    ├── helpers

    │   ├── search_event.php

    │   ├── search_installer.php

    │   ├── search.php

    │   ├── search_task.php

    │   └── search_theme.php

    ├── models

    │   └── search_record.php

    ├── module.info

    └── views

        ├── search.html.php

        └── search_link.html.php

The search module has 1 controller, accessible at the /search url.  It has an event hook at search_event.php, and installer at search_installer.php, an admin task at search_task.php and a theme hook at search_theme.php.  It implements a single model in search_record.php and has two views.

Module Lifecycle

A module can be installed, activated and deactivated.  In Gallery 3.0, a module cannot be uninstalled, but there are plans to implement that in Gallery 3.1 and beyond.

At install time, the module is responsible for creating and populating any necessary database tables, registering graphics rules and setting module variables.  Activation and deactivation of a module is handled by the Gallery 3 framework.  In the future when uninstalling is supported, the module can remove any database tables that it created.

Module Hooks

The following hooks are available for modules to implement.  Any module can introduce its own hooks, but the ones listed below are the ones that are supported by the official Gallery 3 modules.

Event

These are general purpose hooks that are used in a variety of places throughout the system to indicate that an activity of some kind has happened, is about to happen, or has completed.  Any module can initiate an event, and any module can handle an event.  Multiple modules can handle a single event, it's up to the individual modules to make sure that they don't interfere with each other.  The following events are triggered by the official Gallery 3 modules:

add_photos_form

add_photos_form_completed

admin_menu

album_add_form

album_add_form_completed

album_menu

batch_complete

captcha_protect_form

comment_add_form

comment_created

comment_updated

context_menu

gallery_ready

gallery_shutdown

graphics_composite

graphics_composite_completed

graphics_resize

graphics_resize_completed

graphics_rotate

graphics_rotate_completed

group_before_delete

group_deleted

group_created

group_updated

identity_provider_changed

item_before_create

item_created

item_before_delete

item_deleted

item_edit_form

item_edit_form_completed

item_index_data

item_moved

item_related_update

item_updated

item_updated_data_file

module_change

movie_menu

photo_menu

pre_deactivate

show_user_profile

site_menu

tag_menu

theme_edit_form

theme_edit_form_completed

user_add_form_admin

user_add_form_admin_completed

user_auth

user_auth_failed

user_before_delete

user_deleted

user_change_email_form_completed

user_change_password_form

user_change_password_form_completed

user_created

user_edit_form_admin

user_edit_form_admin_completed

user_edit_form

user_edit_form_completed

user_login

user_login_failed

user_logout

user_menu

user_password_change

user_profile_contact_form

user_updated

Theme

Theme hooks are called directly by the theme itself.  Not every theme will call every hook, and not every theme page will call every hook.  The theme will call each hook as appropriate.  For example, when rendering an album page the theme may call the head, header_top, header_bottom, page_top, album_top, thumb_top, thumb_info, thumb_bottom, album_bottom, page_bottom, and credits hooks in that order.  Some of those hooks (for example thumb_top) might be called multiple times.

Each implementation of a theme hook returns a bit of HTML that will be rendered on the page at the spot where the theme calls the hook.  If multiple modules render HTML at that same spot, the module loading order will determine the order of the HTML and it's up to the modules to not interfere with each other.

admin_credits

admin_footer

admin_header_top

admin_header_bottom

admin_page_bottom

admin_page_top

admin_head

album_blocks

album_bottom

album_top

body_attributes

credits

dynamic_bottom

dynamic_top

footer

head

header_bottom

header_top

page_bottom

page_top

photo_blocks

photo_bottom

photo_top

resize_bottom

resize_top

sidebar_bottom

sidebar_top

thumb_bottom

thumb_info

thumb_top

Block

Blocks are visual elements that a module can provide for the theme.  In Gallery 3.0 there are two types: site blocks and admin blocks.  Site blocks are typically shown in the sidebar for the site theme, and admin blocks are typically shown in the sidebar for the admin site.

To register blocks for a module, create a <module_name>_block helper with the following methods.  Note that in 3.1, these hooks will likely get turned into module events.

get_admin_list

get_site_list

get

RSS

The RSS module allows other modules to provide their own feeds.  To add an RSS feed, create a <module_name>_rss helper with the following methods.  Note that in 3.1, these hooks will likely get turned into module events.

available_feeds

feed

Task

The Admin > Maintenance interface provides a way to run long lived tasks.  Each task is a tiny state machine that is triggered many times.  Each time it's triggered, a task should spend no more than 1-2 seconds doing a small amount of work, then return.  It will get called again when appropriate.  Usually the task is triggered by Javascript in the web browser so that it can display results to the end user and make sure that no single operation takes too much time and resources on the server

To add a task, create a <module_name>_task helper with the following methods.  Note that in 3.1, these hooks will likely get turned into module events.

available_tasks

Installer

Module installation and upgrading is handled by a special hook.  If a module does not provide its own code to handle installing and upgrading, the framework will perform the bare necessities required.  In many modules, this is perfectly sufficient, however if your module requires additional operations such as creating or altering database tables or module variables, then an installer is required.

To create an installer, create a <module_name>_installer helper with the following methods:

install

upgrade

REST

The rest module provides a RESTful API to Gallery 3 that provides much of the fundamental Gallery 3 functionality via a remote API.  This is reasonably well documented in http://codex.gallery2.org/Gallery3:API:REST