Embedding and Integration of Gallery 2

Introduction

This document contains instructions for writing the integration code to embed G2 in
another PHP application. Embedding G2 in another application using existing integrations is described in the specific integration packages. Please also see the List of Available Integrations before writing your own. At least they show what can be done and how it can be done (it's all open source).

Gallery2 is designed to be easily embedded in other applications. The GalleryEmbed class
provides an API to assist in processing G2 requests and keeping sessions,
user logins and user/group data in sync between G2 and the embedding application.
In this document embedding application is shortened to emApp and Gallery2 is called G2.

Requirements

There are some requirements at the emApp such that G2 can be embedded into it.

It needs to be a PHP application. Until the upcoming XML-RPC module for G2 is available, you can talk to G2 only with PHP

emApp needs to be on the same server as G2.

emApp needs to use UTF-8 for character encoding for Input (request data) / Output (generated HTML) and in communication with G2 through the API. If that's not possible, one needs to convert everything to and from UTF-8 when communicating with G2.

Notes:

Support of modules is a plus, but not required: Modular integration ensures that you can independentely upgrade G2 and emApp without maintaining too much code yourself

Support of a hook/event system is a plus, but not required: Event-based integration ensures that the two applications can work closely together without changing files / sourcecode, just by adding functionality through event listeners/handlers

emApp does not have to be database driven and if it is database driven, G2 and emApp can but do not have to run in the same database

You can work around the above limitations (PHP, same server) by writing a PHP wrapper / interface between GalleryEmbed and your application, e.g. to use remote procdure calls that talk to your wrapper which then talks to GalleryEmbed

Overview

G2 API

G2 is written in object-oriented PHP (only PHP 4.x compatible object-oriented features of PHP). GalleryCoreApi is your main interface to the G2 API (Application Programming Interface). The methods used for the integration will be described in this document as we need them. The first things you will get to know is the GalleryCoreApi class (gallery2/modules/core/classes/GalleryCoreApi.class) and how to deal with G2 status messages (G2's error management).

GalleryEmbed API

The GalleryEmbed API is mostly a subset of the G2 API plus some methods that are specific for embedding G2. Most G2 methods used for integrations are defined in the GalleryEmbed class. Use methods from GalleryEmbed strictly statically (don't instantiate a GalleryEmbed object, always use it like $ret = GalleryEmbed::functionName(); at never like $embed->functionName();)!

Sample Directory Structure

This is an example for a possible directory structure if G2 is integrated as a module in emApp (typical for CMS which support modules), but there is no need to conform with it:

Relation between emApp and G2

The relation between the emApp and G2 is a master-slave relation and the communication is only simplex. That means that only the emApp (master) initializes communications with G2 (slave), i.e. the emApp requests something from G2, and waits for the result. G2 never requests something from emApp. Instead, G2 relies on the emApp to notify it of all important events (user creation, update, delete, ...).

Reading this Document

In the following, we describe in each section step by step what is needed to first have a very basic integration and finally get a fully functionally integrated solution.

Section An entry point is mandatory and it's the most important section. After finishing this step, you will have a working embedded G2 in your emApp

Section Login And Session Management is also mandatory, else you cannot login to your G2 / browse as logged in user

Sections Initial User Synchronization, User Management and Group Management are required if you want to integrate the user (and group) management system of your emApp with G2

Section Visual Integration is recommended to make sure that G2 nicely matches the look and feel of your site and seamlessly integrates in your emApp

All other sections are optional and are highly targeted to a specific purpose

And remember that the ultimate goal should be an integration that doesn't require any manual changes to files by the user. Try to follow our recommendations concerning the event-based loose coupling technique and it should work out that way.

An entry point

The first task is to create an entry point from emApp to G2 (or wrapper file). This entry point is a PHP file which is just a small wrapper for the whole G2 application. All requests will go through this new entry point instead of main.php (or index.php) of G2. This entry point file can be located anywhere in your website, it doesn't have to be in the G2 directory.

In the above directory / file listing, g2embed.php is the entry point which does all the important work (call GalleryEmbed::init(); and GalleryEmbed::handleRequest();).

In your GalleryEmbed::init(...); call, use 'activeUserId' => . Once you have done the initial user synchronization you can use your embedded G2 as normal G2 users, but before that, you can only browse as guest user.

A small code snippet to examplify the basics of GalleryEmbed is attached to this post (sample_embedding_wrapper.zip).

Warning: When using GalleryEmbed, you need to set the content-type of your pages yourself. Although, note that G2 currently supports only UTF8 as character encoding! Preferably before calling GalleryEmbed::handleRequest() (or init), you should call

Login And Session Management

Use the login/authentication of the emApp for G2 and keep the sessions in sync.

If your emApp does not have a login / user management, you can copy the G2 login form to anywhere on your website. But you need to configure the cookie path in G2 Site Admin -> General properly (it's described there).

If your emApp has a login / user management (all CMS', forums, blogs, ... have something like that), you don't have to do anything special for the login. Only in the GalleryEmbed::init() call you'll have to specify what user is currently logged-in in your emApp. G2 relies on the emApp to do the authentication and just loads the corresponding user from its own backend.

G2 by default doesn't show the login link when embedded. You can force it to show the login link with:

$gallery->setConfig('login', true);

right after the GalleryEmbed::init(); but before the ::handleRequest(); call.

If your emApp has login system itself, you should at least set the 'loginRedirect' parameter in the GalleryEmbed::init(); call such that when a user clicks on login in G2, it redirects to the emApp's login page.

If your emApp doesn't have its own login system, it makes sense to show the login link and to not define any loginRedirect (just leave it away in your ::init(); call).

Initial User Synchronization

You need to map the user IDs of your emApp to the user IDs of G2. You might know other integrations that use a single database table for both applications. Integrations with G2 work a little different, we call it loose coupling or loose integration. Your emApp has its own database tables, G2 has its own tables and there's a single database table managed by G2 that maps the users from your emApp with the corresponding users in G2. You'll soon see that it has a lot of advantages.

Before you can use G2 in embedded mode as another user but guest, you need to map the users that already exist in your emApp with those that already exist in G2.

If both, emApp and G2 are freshly installed, there will be only maybe 1-3 default users that need to be manually mapped. Usually an admin user from emApp with the admin user of G2. Maybe also a few others. You don't need to map the guest user since you should specify activeUserId => (empty string) in your GalleryEmbed::init() call if the current request is for the guest user / non-logged-in visitor of your site. But of course you can map the guest users too, but still use activeUserId in your ::init() call for guests, else you'll get a small performance penalty.

If G2 and/or emApp are not freshly installed and already in use, maybe already with some registered users, you need to map all these users, hopefully in an automated way, else you have to do a lot of manual work. You should map users that exist in emApp and G2 and you should create a new user in emApp for all users in G2 that don't exist in emApp yet and the other way around.

This is a rough sketch of the algorithm that can be used to this initial mapping / import / export of users:

Get cached lists of EmAppUsers, G2Users and Map by entityId denoted as EmAppUsers_cache, G2Users_cache and Map_by_entityId_cache respectively;
For each g2User in G2Users_cache do
if g2User NOT in Map_by_entityId_cache then
Create User in EmApp with user data from g2User;
Insert new mapping into Map;
end if;
end for;
Get cached list of Map by externalId Map_by_externalId_cache;
For each emAppUser in EmAppUsers_cache do
if emAppUser NOT in Map_by_externalId_cache then
Create User in G2 with user data from emAppUser;
Insert new mapping into Map;
else
Update User in G2 with user data from emAppUser;
end if;
end for;

In future versions of G2, we will offer a framework which will minimize the work to do the initial integration. Mike Classic wrote a G2 module which does all the logic, handles timeouts and a lot of different thing. You will just have to provide a function to get all users of your emApp, a function that creates a new user in your emApp and a function to update a user in your emApp.
If you're curious how this will look like, see: http://cvs.sourceforge.net/viewcvs.py/gallery-contrib/embed/

User Management

When using embedded G2 with an emApp that has its own user management, you should deactivate G2's user registration module since all new users should register with emApp.

There are two methods to make sure that new users that are created in your emApp also get created in G2.

Event-based synchronization is the preferred and recommended method. But not all emApps might support it.

On-the-fly user creation is the low-tech fallback solution for less powerful emApps.

Event-Based Synchronization

Modern CMS frameworks have an event system and allow their modules to hook your own function calls into core functionality. Usually there is an event in these CMS when a user is created, an event when a user is deleted and another when the properties of a user get updated.

Your integration would then listen on such events in your emApp and call GalleryEmbed::createUser(), GalleryEmbed::deleteUser(), and GalleryEmbed::updateUser() respectively.

Since you call GalleryEmbed::createUser() right when the user gets created in emApp, G2 and your emApp will always be in sync'. This is what we call event-based loose coupling since even if the two applications are completely self-contained, manage their own data and no files need to be edited, they are kept 100% in sync.

On-the-fly User Creation

If your emApp doesn't have a hook/event system, you need something else that works to make sure that all users in your emApp that access your embedded G2 also exist in G2.

If you're writing an integration for your own custom web script / application and you don't have an event system, you can of course just edit your user creation code / functions and also call GalleryEmbed::create() there (same for ::delete() and ::update()).

If you need to write an integration for a CMS / portal / ... and this integration should be easily installable and maintainable by other users for their own website, you probably can't ask them to edit / replace their emApp files just to ensure that the GalleryEmbed::create(), ... methods get called.

For such cases we recommend the on-the-fly user creation. That means that you create a user in G2 right then when you need it and not before. You don't create the user right when the user is created in your emApp, you create it in the background when the user does his first visit to the embedded G2 in your emApp.

Also, G2 and emApp are generally out-of-sync, you're just keeping users very coarsely in sync.

If you need to use on-the-fly user creation, we recommend that you spend more time on the initial user synchronization such that you can run it from time to time or even on a regular basis / periodically and make it smarter such that it detects which users need to be updated and deleted.

What happens if a user is deleted in G2 (e.g. with GalleryEmbed::deleteUser())?

All items of the user get reassigned to a new owner, usually to the first admin in the admin list. The user itself is completely deleted.

Site-wide Configuration Parameters

You may want to keep site-wide configuration settings in sync with G2. E.g. such that when you change the default language in your emApp it is also changed in G2. The same applies to short URL support or the cookie path.

But you need to convert the language code of your emApp to the G2 format before calling setPluginParameter.

The G2 format is xx or xx_XX where xx is the ISO 2-letter language code and XX is the 2-letter country code. Use xx only if no country-specific locale is available. G2 will fall back from xx_XX to xx if no matching locale is available in your G2. if xx is not available, it will fall back to 'en_US'.

Short URL Support

You can enable short URLs for embedded G2 either as a user by browsing to your embedded G2 Site Admin section or you can do it in the code. See:

Cookie Settings

When embedded, G2 appends to all image URLs the G2 sessionId which leads to ugly URLs well, unless you look at the HTML source code, you don't see these URLs anyway). You need to set the cookie path, only then G2 will stop appending the sessionId to DownloadItem URLs.

Please read the explanations in the site admin page on what cookie path value is correct in what case. '/' is always correct, but is not secure if you're sharing your domain with other websites in subfolders (session hijacking).

Other Settings

You can set other settings too of course. Most G2 settings can be set with GalleryCoreApi::setPluginParameter().

Language Selection per User / Session

In G2, each user can have a preferred language. Please read Language Settings to get more information about how language preferences and settings are handled in G2.

In your GalleryEmbed::init(array('g2Uri' => $g2Uri, 'embedUri' => $embedUri', 'activeUserId' => $emAppUserId, 'activeLanguage' => $langCode)); you can set the language code for the active user for the current request. See the above section to inform yourself about the xx_XX format of language codes in G2.

You don't need to set the language code on each ::init() call. If you synchronize the user preferences separately, G2 loads the correct user preferences automatically.

ImageBlocks

You can use G2's image blocks to show random images somewhere on your website. You can use it also to show the most recent images, a specific image, the most popular image, etc.

You can either use the external imageblock URL as described in G2 Site Admin -> ImageBlock or you use the GalleryEmbed::getImageBlock() method. The latter has a few advantages. E.g. the links of the imageblock point to your embedded G2 and not to your standalone G2. Also, it's faster and you can better customize it.

G2 Images in Your emApp Articles

With the GalleryEmbed::getImageBlock(); you can fetch a random image, or a random image from a specific album. But you can also fetch a specific image either by the ID number of the image or by its path. With a little logic, you can then easily e.g. use [g2:55] or similar tags in your emApp's articles to show a specific image in your articles. Joomla, Mediawiki and WordPress already have this feature in their G2 integration, other integrations follow.

Visual Integration

The unmodified default G2 theme (matrix) might not be a good match for the look and feel of your emApp / website. You can start by customizing a G2 theme or by selecting a G2 theme that is better suited for embedding, like the Siriux theme, the WordPress theme, etc.

More and more integrations ship with a special theme that replaces the default G2 theme and matches the emApp much better.

Sidebar

Not all G2 themes make use of the sidebar. But for those that do (e.g. the default (matrix) theme), you can tell G2 whether to show the sidebar as or not. And if you're not showing it, you can fetch the sidebar HTML in a variable and add it to the sidebar of your emApp.

Call:

GalleryCapabilities::set('showSidebarBlocks', false);

between your GalleryEmbed::init(); and your GalleryEmbed::handleRequest(); call to disable showing the sidebar.

When disabled, you can get the sidebar HTML after the handleRequest call with:

You can then use $g2sidebarHtml when generating your sidebar of the emApp.

Advanced Technique - Generating your own Menus based on Template Data from G2

G2 not only returns the generated HTML based on the request and the G2 templates, it also returns the template data. With the template data, which contains everything to generate your own HTML pages from live G2 data, you can e.g. generate a Menu or other things with the template engine of your emApp.

$g2moddata = GalleryEmbed::handleRequest();
/* Now you *could* do something with $g2moddata['themeData'] */

Embedded Theme vs Standalone Theme

You can specify a different theme to be used for Gallery shown embedded vs. in standalone.

Note: This method / feature has been added in GalleryEmbed API version 1.3.

Group Management

Integrating the two group management systems

a) can be a non-trivial task and

b) is IMO not that important.

Synchronizing groups between your emApp and g2 might be non-trivial because group management is usually handled in a lot of different ways. User management is often very similar, but for group management different applications often choose different approaches.

Why do I think that group synchronization is not that important? It doesn't buy you that much. While the advantage of synchronized users is obvious, you have to search for arguments for doing a lot of work to get group synchronization working.

However, if you decide to also do group synchronization, you can use GalleryEmbed::createGroup(); and GalleryEmbed::addUserToGroup(); etc. to manage groups and memberships. It maps groups of your emApp to groups in G2 with the same mapping table that is used for users and the same logic applies here.

Group Management in G2

There are three default groups in G2:

Everybody group: All registered (logged in) users including all admins and the guest (anonymous) user is in this group

Registered Users group: All registered (logged in) users including all admins are in this group

Site Admins group: All admins are in this group

And there are a few rules:

Don't delete these three default groups

Don't try to delete the guest user and don't try to delete the last admin user. There must always be at least a single user in the admin group

Beyond that, you can create / delete as many groups / users as you want.

As long as you make sure that these rules still apply when synchronizing your groups and memberships, you can do what you want.

Search Syndication

You can syndicate search results from G2's search engine with the search function of your emApp.

G2 as Image/Multimedia Repository for your Website

You can use G2 as the multimedia backend for your emApp. This very codex (MediaWiki) is an example for it. Images are managed by G2 and you can use G2 images in codex articles. In the article editor, there's even an integrated image / album browser to pick images from G2 with your mouse.

Embedded Image Browser

Having an embedded G2 is sure nice. But using it to store images for your emApp and articles, blog entries etc is even better! Some integrations already have an embedded image / album browser to pick images from your emApp article editor and use them in your new / edited articles.

Joomla!, WordPress, and Drupal all have TinyMCE and/or FCKEditor editor instances which can take advantage of the Gallery2 Image Chooser.

There is a live demo of how to integrate the Gallery2 Image Chooser into any browser form.

MediaWiki's G2 integration includes an image / album browser and advanced features for embedded images.

Automating the Configuration

Tools / Guides to help finding out the embedUri, g2Uri, embed.php path based on user input

Integrations with an Installer

While this document applies to both, integrations for a single website and integrations for other products that will be used on hundreds or thousands of other websites, there are a few special steps required to offer an easy installation for the latter group.

Let's say you are using a content management system X and since you are integrating G2 into X, you thought of offering this integration code to all users of X such that others can integrate their G2 with X just with a few configuration steps.

Maybe you offer the integration as a download on your website. The users / administrators of other websites would then put this code on their server and then they need to configure G2 to work with X.

[optional] cookie path: if the cookie path is set in G2, there won't be a sessionId appended to core.DownloadItem URLs

[optional] rewrite paths. If the rewrite module is configured, there will be much nicer and shorter URLs

And not to forget the initial user/group synchronization. But that is already explained above.

In existing integrations like for xaraya, Wordpress, Joomla (mambo), ... the administrator that installs G2 in the CMS / emApp is only asked to enter one thing and the integration code figures out the rest.

The administrator is asked to enter the g2Uri or with other words "Please copy and paste the URL / address to your Gallery 2 installation", e.g. http://example.com/gallery2/ . Pretty user-friendly :)

How does it work?

Include G2EmbedDiscoveryUtilities.class in your integration. You can use its

$g2Uri = G2EmbedDiscoveryUtilities::normalizeG2Uri($g2Uri);

to sanitize and normalize the user (administrator) input, you can then use

or via cvs from cvs.sourceforge.net:/cvsroot/gallery checkout integrations (it's in the tools folder)

An alternative is of course to restrict your integration to only work if emApp is installed in the webroot and Gallery2 in a gallery2/ folder. Then you can hardcode everything. Of course, users prefer flexibility, but they also like something that just works and a lot of them are smart enough to adjust the code to their needs. Just be sure to communicate the restrictions that you choose to make.

What do you still need to do manually / with the API?

the rewrite API helps you to configure the rewrite module from your integration code

the cookie path: compare the path of the emApp with the G2 path and the longest common part should be used for the cookie path.

Integrations with G2 as the Master

The above described integration is based on the master-slave relationship with emApp as a master and G2 as the slave. emApp is supposed to to call G2 to show an embedded G2 and it's also supposed to inform G2 about all important events concerning users (create, update, delete).

An alternative scenario is if G2 is the master and should inform another application of all user registrations, logins, etc.

Since G2 has an event system itself, you can easily achieve this by creating your own G2 module which registers an event listener for GalleryEntity::save and GalleryEntity::delete events. GalleryEntity::save is called for newly created entities (users, items, ... a lot of things are entities).
Your event handler needs to check the entityType for which the event was called. We are only interested in events for GalleryUser entities.

Note: It's not an official module, neither cleaned up or finished, it's a 3rd party (beta) contribution. (Feel free to post bugs etc on forums)
It was made to work with phpbb (as slave), so it needs to be adapted to any other slave environment.

Integrations which are Duplex (No Master-Slave Relation)

An extension of the above mentioned approach (emApp as the master or G2 as the master in a master-slave simplex integration) is an integration where emApp sets in and requests things from G2 as well as the other way. That's no longer a master-slave relationship.

You simply combine the above two mentioned approaches and make sure both integrations let the other know of user registrations etc.

Warning: You will have to prevent notification loops.

Example: If a user is created in G2, G2 will call a createUser function to create a user in emApp. emApp will then want to call a createUser function to create a user in G2 since it wants to make sure everything gets synchronized. Etc. You just have to make sure that you fall into this trap and stop notifying the other application (e.g. set a global variable when creating and before calling the createUser method of the interface to the other application, check the global variable).