Please take a look at the overview of Gallery 2 Embedding.
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.
There are some requirements at the emApp such that G2 can be embedded into it.
Notes:
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).
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();)!
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:
|-- g2data/ `-- htdocs/ (the document / web root of your website, often called www or public_html) |-- index.php (the entry point for your emApp) |-- modules/ | `-- gallery2/ (this is NOT the G2 application, just your integration files) | |-- user/ | | `-- g2embed.php (THE wrapper file. Gets called by emApp, calls GalleryEmbed) | |-- admin/ | | `-- setup.php (optional: administrate the integration) | |-- blocks/ | | `-- image.php (optional: wrapper for the G2 image block) | |-- hooks/ | | |-- createUser.php (synchronize user creation) | | |-- updateUser.php (, user data updates) | | `-- deleteUser.php (, and deletion) | `-- g2helper.php (a collection of common helper functions) `-- gallery2/ (the G2 application directory) |-- embed.php (include this file in your wrapper) |-- main.php (the entry point for standalone G2) |-- modules/ \-- ... (other G2 directories and files)
An explanation of all these files follows in the sections below.
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, ...).
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.
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.
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();).
For now, please see: Integration - How to write new integration code
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
if (!headers_sent()) { header('Content-Type: text/html; charset=UTF-8'); }
Use the login/authentication of the emApp for G2 and keep the sessions in sync.
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).
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.
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/
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.
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.
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.
Sample code for on-the-fly user creation (put it in your G2 wrapper file):
$ret = GalleryEmbed::init(array('embedUri' => $embedUri, 'g2Uri' => $g2Uri, 'activeUserId' => $emAppUserId)); if ($ret) { /* Error! */ /* Did we get an error because the user doesn't exist in g2 yet? */ $ret2 = GalleryEmbed::isExternalIdMapped($emAppUserId, 'GalleryUser'); if ($ret2 && $ret2->getErrorCode() & ERROR_MISSING_OBJECT) { /* The user does not exist in G2 yet. Create in now on-the-fly */ $ret = GalleryEmbed::createUser($emAppUserId, array('username' => $emAppUser['username'], 'language' => $emAppUser['language'], ...)); if ($ret) { /* An error during user creation. Not good, print an error or do whatever is appropriate * in your emApp when an error occurs */ print "An error occurred during the on-the-fly user creation <br>"; print $ret->getAsHtml(); exit; } } else { /* The error we got wasn't due to a missing user, it was a real error */ if ($ret2) { print "An error occurred while checking if a user already exists<br>"; print $ret2->getAsHtml(); } print "An error occurred while trying to initialize G2<br>"; print $ret->getAsHtml(); exit; } } /* At this point we know that either the user either existed already before or that it was just created * proceed with the normal request to G2 */ $data = GalleryEmbed::handleRequest(); /* print $data['bodyHtml'] etc.... */
But the on-the-fly user creation is problematic!
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.
Also see the somewhat outdated post at:
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.
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.
Before calling any G2 function, you must initialize G2 with:
$ret = GalleryEmbed::init(array('fullInit' => true)); // and other params if necessary if ($ret) { print $ret->getAsHtml(); exit; }
You can set the G2 default language with the following call:
$ret = GalleryCoreApi::setPluginParameter('module', 'core', 'default.language', $g2languageCode); if ($ret) { print $ret->getAsHtml(); exit; } }
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'.
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:
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.
$ret = GalleryCoreApi::setPluginParameter('module', 'core', 'cookie.path', '/'); if ($ret) { print $ret->getAsHtml(); exit; } }
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).
You can set other settings too of course. Most G2 settings can be set with GalleryCoreApi::setPluginParameter().
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.
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.
See:
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.
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.
See: Theme Guide | Template Reference | How to - Visual Integration
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:
/* the handlerequest call */ $g2moddata = GalleryEmbed::handleRequest(); /* check if there's sidebar content */ if (!empty($g2moddata['sidebarBlocksHtml'])) { global $g2sidebarHtml; $g2sidebarHtml = $g2moddata['sidebarBlocksHtml']; }
You can then use $g2sidebarHtml when generating your sidebar of the emApp.
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'] */
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.
A basic approach:
$ret = GalleryEmbed::init(...); handleStatus($ret); $ret = GalleryEmbed::setThemeForRequest('siriux'); handleStatus($ret); $data = GalleryEmbed::handleRequest();
For an advanced and flexible technique, see: Theme Override By Event.
Integrating the two group management systems
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.
There are three default groups in G2:
And there are a few rules:
As long as you make sure that these rules still apply when synchronizing your groups and memberships, you can do what you want.
You can syndicate search results from G2's search engine with the search function of your emApp.
See: GalleryEmbed::searchScan(); | GalleryEmbed::search();
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.
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.
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.
Parameters that need to be configured:
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 :)
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
list ($success, $embedPhpPath, $errorString) = G2EmbedDiscoveryUtilities::getG2EmbedPathByG2Uri($g2Uri);
to get the absolute filesystem path of embed.php and you need to find out the embedUri yourself, shouldn't be too hard, and then run it through
$embedUri = G2EmbedDiscoveryUtilities::normalizeEmbedUri($embedUri);
to ensure that it is in the format that GalleryEmbed::init() expects.
The G2EmbedDiscoveryUtilities.class can be downloaded from:
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.
You can then set it with a single API call:
$ret = GalleryCoreApi::setPluginParameter('module', 'core', 'cookie.path', $path);
If the emApp cannot be configured to work in UTF-8, character set conversion between emApp and G2 is necessary.
Convert to UTF-8 at:
Convert from UTF-8 at:
If you use other APIs (e.g. RewriteApi or GalleryCoreApi), you need to do the same conversion there as well.
For ISO-8859-1, PHP offers built-in functions:
Generally, you can use:
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.
Example:
You can grab here a 'module' which does creating/updating of external users using G2 as master: http://www.site.hu/me/g2/mod_UpdateExternalUser.tar.gz (OUTDATED!)
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.
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).