Gallery Module

sing

Joined: 2005-09-07
Posts: 16
Posted: Tue, 2005-09-20 09:34

Hi All,

I am a newbie of Gallery2. I would like to develop some gallery modules to suit my needs, but I am not sure how to get start. I did read all the doc at codex pages and some tutorial modules. Anyone can give me some guidelins?

Thanks for any advice.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-09-20 10:14

Well, what's your goal? It's hard to guide you through without having an explicit example / goal.
And yes, we know that we need module howtos and more docs.

 
sing

Joined: 2005-09-07
Posts: 16
Posted: Tue, 2005-09-20 10:42

Hi,

Actually I want to develop many different feature to suit my needs on galery2. For example I would like to know how to work with other modules like quota, imageblock etc. Is there any other example modules? What is the callback for in the module?

Sing
Ec-photo.com http://www.ec-photo.com

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-09-20 13:16

A few G2 concepts:
- Views:
A view is a HTML page that displays something, e.g. the album and the photo pages are "ShowItem" views. There's also a view to add a comment, the AddComment view. Etc.
- Controllers:
A controller executes a command / handles a requested action. E.g. When you view the AddComment page which is a view and hit "submit", you tell a controller to add this comment to G2. The controller receives the comment title and comment text and for which item it should add a comment and finally creates this comment.
- Controller vs. Views:
Views should only display something. In a View, you load data from G2 with some API calls. You never change data / alter data in a view. You just load data and prepare it for presentation.
In a controller, you do the work. You add a comment to the database, you add an item to an album, etc.
Views and controllers are coupled very tightly. Hence there is a "MVC" (Model View Controller) programming pattern. A view shows a web form, you as a user send the form filled with data back to G2, G2 tells loads the requested controller, the controller receives the data and does something with it, finally the controller redirects to a view (the same or another) such that the user receives some feedback, e.g. "thank you, your request was successfully handled" page.
- Blocks, Callbacks, Preloads:
We learned that a view shows a page in G2. Blocks are smaller building blocks of a complete HTML page. E.g. in the G2 sidebar, you can add a search block, a item actions block, a random image block etc. Also, the exif data below photos on the ShowItem view is a block.
Basically, a block is a piece of HTML and you can show as many blocks on a single page as you want.
There is a GUI to add / remove / configure blocks in "Site admin" -> Themes -> Matrix (or any other theme that supports blocks).
You can add blocks to the sidebar, to album pages and to photo pages with this GUI.
Alternatively, you can place blocks anywhere on a page, but then you have to edit templates manually. More on templates later.
Some blocks need to load additional data. E.g. the random imge block needs to load a random image. This is done in the Callback. If a Callback is defined, G2 loads it. And the loaded data is added to the data that is loaded anyway and given to the block template.
Some blocks need to add HTML to the <head> section of the resulting HTML page. E.g. to add CSS or javascript. The Preloads are called before the page outputting begins and in the Preloads, you can add content to the <head> section.
- Modules:
G2 is modular. That means, that you can add views/controllers/blocks/themes/special handling for new file types/... in a modular way. You don't have to edit files, you just can write your own G2 module, place it into your gallery2/modules/ directory and click install & activate in the admin section of G2 and that's it.
The most important and the only mandatory module is the core module. It has all the core functionality, e.g. show album and photo pages, add items, albums, etc. And it also has the API to handle other modules. Finally, it offers a numberous means to include your own functionality whereever you need it.
- Entities:
G2 has a very elegant architecture. You need to have a basic understanding of object-oriented programming (OOP) to understand it. Of course you can learn the basics of OOP quickly. In G2, we deal with albums, photos, thumbnails, resized versions (resizes), movies, users, groups, etc. If you look up the definition of the word "entity", you'll understand that these are all entities, since "entity" is a very general term :)
So, we deal with the term "entity" quite often in G2.
In G2, we prefix our entities with "Gallery", such that we don't have a conflict when you use G2 integrated in other applications.
So it is GalleryEntitiy and not just entity.
A GalleryEntity has attributes like a unique ID, it has a creation timestamp (when was it created in G2), a modification timestamp etc. All entities have this in common.
See Gallery Class tree which is not quite up to date, but the most up to date schema we have.
So at the root, there is the the GalleryEntity. Ignore the ThumbnailImage entity on the right, that's just an entity from the thumbnail module (specify a default thumbnail for each file type). So, we see that GalleryUser, GalleryGroup are a GalleryEntity (they extend the GalleryEntity class). A GalleryUser has all the properties and functions of a GalleryEntity, plus the atributes of a user, e.g. a user name, a full name, a password etc. A GalleryGroup has a group name and that's it.
And then we see the GalleryChildEntity: G2 is basically a tree. Each album can contain subalbums and subitems, subalbums can again have subsubalbums etc. This is a tree structure. At the root of the tree, there is root album, which doesn't have a parent. All other items, be it photos, albums, or any other type, have a parent. This relationship, item <-> parent is relfected in this class, GalleryChildEntity. So each GalleryChildEntity has the properties of a GalleryEntity plus a parentId.
When we scroll down, we see that there are two GalleryChildEntities.
We differentiate GalleryFilesystemEntity from GalleryDerivative. The former are things that you explicitely create / add. E.g. an album is a folder in the filesystem. Thus it's a FileSystemEntity. When you add an item to G2, e.g. a photo, it is stored as a file in a folder, thus it is also a FileSystemEntity. GalleryDerivatives are the result of operations acting on a FilesystemEntity, e.g. to generate a thumbnail, we just have to call a function which generates it from an original photo.
In reality, we store GalleryDerivatives also on the filesystem, but in a different way. We store it in a g2data/cache/derivatives/ folder sorted by their ID. But we could leave the cache away and it G2 would still work. Also, we will see that GalleryFileSystemEntities have many different properties than derivatives. So it's important to differentiate the two.
There are two types of GalleryItems: GalleryAlbumItem and GalleryDataItem (photos, movies, ...). They both have a lot in common. E.g. they both need a title, summary, description etc. and thus, the GalleryItem class was introduced.
We can set the theme separately for each album, so this is a album property.
GalleryDataItem is the general term for all files that you upload to G2. A DataItem has a mimeType. You can implement special functionality for any mimeType, e.g. add a embedded audio player for audio items, or open a rich text editor for text files. At the moment, there are implementations of a GalleryPhotoItem, GalleryMovieItem (e.g. is responsible for showing movies in an embedded player in the browser) and GalleryAnimationItem (shows flash embedded in the browser). Everything else is GalleryUnknownItem. A module could register a GalleryAudioItem and implement a few functions and that's all that is needed to play sound files embedded in the browser.
Read about Derivatives in the next section.
Summary: In the end, you won't have to deal with this class hierarchy too much. There's an API function to load the children of an album and you'll receive album items and data items (mostly photos) as a result. You can then call $item->getTitle() or $item->getCreationTimeStamp(), etc. and it's just good to know where all the properties and functions come from. There's an auto-generated documentation on all classes and functions here (API docs): http://gallery.menalto.com/dev
You can extend this architecture / class hierarchy in any way. Just register your own classes and implementations.
- Derivatives:
Thumnails and resizes have something in common. They are both generated from the original photos. Using another term, they are both "derived" from an original. Thus, we call them both "derivative". In OOP, you often try to collect the attributes and features of similar things (classes) in a more general thing. And this is here the case. thumbnails and resizes are very similar, thus we introduced the derivatives.
There are several types of derivatives: Thumbnails, resizes and preferred resizes. Preferred resizes are generazed for non-webviewable data items, if possible. e.g. a same-size (jpeg) image of a PDF, of a TIFF, of a RAW file, etc. other resizes and thumbnails can then be generated from the preferred resize instead of from the non-webviewable format.
The mentioned derivatives are all derivatives of a photo. But in G2, you can have derivatives of any file type. Well, there isn't any other derivatives than the mentioned ones, but you could extend G2 with a module to create a smaller movie of a long movie, or a low bit rate version of a high fidelity song.
Thus we have the GalleryDerivative entity, which can be anything. And we have the GalleryDerivativeImage which is either a thumb, a resize or a preferred resize. A GalleryDerivativeImage extends the GalleryDerivative (OOP), so it has all the properties and functions of a GalleryDerivative too.
- Events:
Think of the following problem:
Whenever an item is added to my Gallery, I'd like to be informed with an email. The "add item" code is in the core module. But we decided to reduce the core functionality / module to a minimum. We don't want to add all features, e.g. user notications in the core module. And we don't want that you have to modify some files just to get this feature.
For things like this we have events. When ever an item (or an entity to be more general) is created in G2, we notify all other modules of this event. More specifically, we notify only those modules, that are interested in such an event.
To solve the above problem, you have to create a new G2 module and register an event listener. An event listener is a class that implements a specific function (handleEvent()). In the handleEvent function you just write a send email API call and that's it. The end user just has to download your notification module and install it with a mouseclick.
There are a other events in G2 too: e.g. when a user does a login, logout, etc. So you can add modules that do specific things when something specific happens.
- Themes:
Themes are responsible for the look and feel of G2. A theme can decide what to present and how to show it. A theme tells G2 what data to load and what template to show. This sounds very general and it actually is. As it is with very general things, they tend to be complex. To simplify the life of a theme creator, G2 already loads a lot of typical data by itself. E.g. the children of an album and related things. But a theme doesn't have to obey these very typical album / photo pages look, it could show anything. It's up to the creativity of the theme designer.
- Templates:
Templates are (X) HTML pages in G2 which include some smarty markup to include dynamic content. E.g. the album.tpl file of the matrix theme shows a by default 3 rows by 3 columns of thumbnails. In the matrix album.tpl template, this is done with HTML <table> <tr> rows and <td> cells.
Smarty is used to make the number of rows and columns dynamic. Smarty is also used to include another G2 template to show in each table cell an image.
G2 templates support the basic smarty markup and additionally, we have the tags that start with {g-> ...}, e.g. {g->image ...} includes an image, {g->url ...} includes a specific URL, {g->text ...} shows some text in the language of the active G2 user, {g->block ...} includes a G2 block, etc.
In any G2 templates/ folder, you can create a templates/local/ folder and place in their a customized / changed template.
Soon, themes will be able to override / replace any module template.
- Factory:
Remember that you can extend the G2 class hierarchy, add your own GalleryAudioItem, etc.? This is done with the Factory. A factory produces something. In G2, you order something from the factory with $entity = GalleryCoreApi::newFactoryInstance(i want an instance of an item that is specialized for audio files); Then the factory searches its store for something that is specialized for audio items. If it finds something, it returns it. If it doesn't find a specialized audio item, it returns the UnknownItem, since we cannot do anything about it.
If you want to write a module that adds audio item support to G2, you have to tell the factory about it. You have to call GalleryCoreApi::registerFactoryImplementation('GalleryItem', 'GalleryAudioItem', 'modules/audio/classes/GalleryAudioItem.class', ... ,'audio/wav'); Once registered, the factory will know about your implementation and when you add a new wav file to G2, it will call ::newFactoryInstance('GalleryItem', ..., 'audio/wav'); and it will find your GalleryAudioItem implementation.
But the factory is used for a lot of other things too:
* Register your own ItemAdd plugin / method: E.g. Add item from local server, add item with the upload applet etc. could all be done in modules. The module just has to register the new method
* Register your own ItemAddOption, sizelimit, quota, create thumbnail on upload etc. are all item add options. This code is executed on-the-fly when items are added
* Register a ItemEditOption: the customfields, multilang, thumbnail, album, photo, ... tabs in the edit album / edit photo / ... tab are all plugins
* Register your own URL generator: the Rewrite url generator creates short urls with the help of mod_rewrite by replacing the original G2 url generator. Another url generator uses a global content distribution network (coral) to safe bandwidth of your server.
* Register a maintenance task that will be listed in site admin -> maintenance: e.g. change all spaces to dashes in all items
* Register your own cart plugin / checkout option: do something with a collection of items. e.g. zip download, print with shutterfly, ...
* Register a onLoad handler for a specific entity type: e.g. the multilanguage module loads captions in different languages for all loaded items. The random thumbnail module loads a random thumbnail for album items, etc.
* Register a colorpack, a customfields implementation, ...
* Register your own validation plugin: e.g. the captcha test in the register view is such a validation plugin
* Register new toolkits and toolkit operations, e.g. in a module, that can create low bitrate versions of high fidelity music files
* Register a SearchInterface implementation, to make additional data searchable, e.g. the comment module registers a search interface implementation to add the search comments functionality
* If you want to expose an API in your module that should be used by other modules, register an interface. Other modules can then get the interface from the factory and call API methods that are exposed by your module's interface
* If you need plugins somewhere else, e.g. in an existing module or the core, please hack your G2 and if you can convince us, we will maybe add other get-plugin-calls too

- Permissions:
G2 has per item permissions. That is, you don't only set permissions for each album, you can set them for each item, if you want. There are user and group permissions. The active user has the sum of his user permissions and all permissions granted to groups he is a member of.
There are only "positive" permissions, that is you can't say "user X has all permission, but delete item". You have to enumarate all other permissions, you can't just say all minus permission Z.
Each module can register new permissions. E.g. the comments module registers add / view / delete / ... comment permissions. The core module registers the view item, resize, source (original) permissions, edit, delete permission etc. The view item permission is used for a several things. An often requested feature is more granular permissions, e.g. edit item name, but not edit album theme. For now, we only have "edit item", which is too general. But we'll soon hit a problem, since we can only support 32 different permissions at the moment. This will probably a small change in G2, but it is required, before adding more detailed permissions.
- User Groups:
G2 has flat user groups. That is, a group cannot contain another group, it's not a tree structure. A user can belong to multiple groups.
There are 3 default groups of G2:
* Site Admins: These users have [core] All Access to the Gallery and they can access the "Site admin" section
* Registered Users: All users with an account are in this group. You must be logged in such that G2 regocognizes you as a member of this group. Since all administrators also have an account, they are also in the registered users group.
* Everybody: Everyone is part of this group, it's the sum of the registered users and the guests / anonymous users / not logged in users
You can create your own groups too.
- Coding conventions:
G2 is relatively strict about variable / function / class / file / .. naming and about the coding style. This is not to piss you off, but it helps a lot when you have to read code of other people or your own code after some while.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2005-09-20 13:21
 
nivekiam
nivekiam's picture

Joined: 2002-12-10
Posts: 16504
Posted: Tue, 2005-09-20 13:41

um, what's up with that link? Why is there a smiley in the middle of it?

 
mindless
mindless's picture

Joined: 2004-01-04
Posts: 8601
Posted: Tue, 2005-09-20 14:36

:D isn't a smiley now.

 
sing

Joined: 2005-09-07
Posts: 16
Posted: Wed, 2005-09-21 04:18

Thanks a lot. This is really a very useful doc.
Here is another question, how gallery2 knows load which files for the task?
I use the "comment" module as an example, there is a module.inc, I know that gallery2 will load the module.inc during installation and register the callbacks functions. But I don't see that there is any call to other inc files (Like AddComment.inc, EditComment.inc), so how do gallery2 know how to deal with the add/delete comment? Or gallery2 will looks for all inc files during installation of new module?

Sing
Ec-photo.com http://www.ec-photo.com

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Wed, 2005-09-21 10:44

it's easier to understand G2 when the url rewrite module is disabled. so turn it off for now.

basically, there are 2 types of requests G2 handles:
1. View requests: e.g.
- g2_view=core.ShowItem in the url: G2 shows a album or a photo page
- g2_view=comment.AddComment: G2 is requested to show a AddComment page
the pattern is simple modulename.ViewName. g2 loads the module modulename and then it looks for modules/modulename/ViewName.inc

2. controller requests: e.g.
- g2_controller=core.Logout: logs the user out and redirects to the view/page where he came from
- g2_controller=comment.AddComment: receives the title and comment text and creates the comment in the database and redirects to the item view
here we have the same pattern, and it includes modules/modulename/ControllerName.inc and instantiates the controller class.

without our very strict naming convention, this wouldn't work. because of the naming convention and standards, we know where to look for what filename and what class name to expect.

controllers are only called by HTML <form> 's since what you see is always a view, a page. and controller always do their work and then redirect to a view, either showing the results or just redirecting you to another view.

 
cthree

Joined: 2005-11-19
Posts: 3
Posted: Mon, 2006-01-09 23:01

Soon, themes will be able to override / replace any module template.

Has this come along? I haven't looked yet to see if progress has been made or not but I thought I'd ask.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Tue, 2006-01-10 01:35

nope, no progress there.

 
alexisb
alexisb's picture

Joined: 2005-11-24
Posts: 56
Posted: Fri, 2006-01-20 03:37
valiant wrote:
For things like this we have events. When ever an item (or an entity to be more general) is created in G2, we notify all other modules of this event. More specifically, we notify only those modules, that are interested in such an event.
To solve the above problem, you have to create a new G2 module and register an event listener. An event listener is a class that implements a specific function (handleEvent()). In the handleEvent function you just write a send email API call and that's it. The end user just has to download your notification module and install it with a mouseclick.
There are a other events in G2 too: e.g. when a user does a login, logout, etc. So you can add modules that do specific things when something specific happens.

Hello, I reviewed the handleEvent() method in thumbnail/module.inc:

    /**
     * Event handler for GalleryEntity::delete event.
     * Remove custom ThumbnailImage if the thumbnail itself is deleted.
     *
     * @see GalleryEventListener::handleEvent
     */
    function handleEvent($event) {
	$entity = $event->getEntity();
	if (GalleryUtilities::isA($entity, 'GalleryDerivativeImage') &&
		$entity->getDerivativeType() == DERIVATIVE_TYPE_IMAGE_THUMBNAIL &&
		$entity->getDerivativeSourceId() != $entity->getParentId()) {
	    list ($ret, $source) =
		GalleryCoreApi::loadEntitiesById($entity->getDerivativeSourceId());
	    if ($ret->isError()) {
		if ($ret->getErrorCode() & ERROR_MISSING_OBJECT) {
		    /* Already gone.. ok! */
		    return array(GalleryStatus::success(), null);
		}
		return array($ret->wrap(__FILE__, __LINE__), null);
	    }
	    if (GalleryUtilities::isA($source, 'ThumbnailImage')) {
		$ret = GalleryCoreApi::deleteEntityById($source->getId());
		if ($ret->isError()) {
		    return array($ret->wrap(__FILE__, __LINE__), null);
		}
	    }
	}
	return array(GalleryStatus::success(), null);
    }

but I can't see in which part of the code the event listener knows what is happening, in this case the comments say this is supposed to remove custom ThumbnailImage if the thumbnail itself is deleted, where is the code that is verifiying that a thumbnail is being deleted?

I was expecting to find something like "if ($event=='delete')" or some switch block.

Thanks.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Fri, 2006-01-20 15:31

in thumbnail/module.inc
function registerEventListeners()
we're registering only an event handler for GalleryEntity::delete events. so we know that all events that are handled by us are GalleryEntity::delete events only.

there are other modules that use a single event handler to handle GalleryEntity:save and GalleryEntity::delete , .. events. if you do that, you have to check the event name of course.
modules/imageblock/module.inc does that for example. it's event handler must check the event type:
switch ($event->getEventName()) {
case 'Gallery::ViewableTreeChange'
....
break;
case 'GalleryEntity::save':
....
break;
}

 
alexisb
alexisb's picture

Joined: 2005-11-24
Posts: 56
Posted: Fri, 2006-01-20 16:22

Hi, thanks a lot Valiant, as usual, your reply is quite helpful.

Cheers.

Alexis Bellido - Ventanazul web solutions

 
alexisb
alexisb's picture

Joined: 2005-11-24
Posts: 56
Posted: Mon, 2006-01-23 03:06

Hi again, after studying most of the code related to events and listeners I have one quick question, where are the events and listeners stored? I think there is no table in the database for this, right?

Is all this information recreated and stored in memory each time a page is loaded?, could you please post a brief explanation of how it works?

I've been reading about the observer design pattern and the events and listener approach look similar to it, are we talking about the same thing? According to Wikipedia and JavaWorld it seems so.

Any comments about these issues?

Thanks.

Alexis Bellido - Ventanazul web solutions

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Mon, 2006-01-23 03:25

g2 loads all active modules on each request and all modules report if they have event listeners and this is then stored in memory.
there is no db table for this.

@observer pattern:
it's very similar, yes. we have a group of objects observing a specific event and there's another object that can create such an event to notify all observers.
in G2, it's "event handler / listener" instead of "observer" though.

but i'd call what we do in g2 "event driven programming" to describe that aspect of g2. we're not observing a specific object for a specific event. we're listening for a specfic event that can be created anywhere, not just by an object or by a specific object.

but the principle is absolutely the same.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Mon, 2006-01-23 18:57

also see:
How to extend GalleryEntity:
http://gallery.menalto.com/node/36885

What does GalleryModule::performFactoryRegistrations() do?
http://gallery.menalto.com/node/36569

Creating New Tables and GalleryStorage
http://gallery.menalto.com/node/43276

 
alexisb
alexisb's picture

Joined: 2005-11-24
Posts: 56
Posted: Wed, 2006-01-25 19:24

Hi, cool, thanks a lot!

Alexis Bellido - Ventanazul web solutions

 
Richard Aston

Joined: 2006-01-27
Posts: 18
Posted: Fri, 2006-01-27 18:50
valiant wrote:
not to forget: Gallery2:Development_Concepts

Thnx for the tip! Forgot that ooglaser one!