How to Customize G2 or Create a module?

jmullan
jmullan's picture

Joined: 2002-07-28
Posts: 974
Posted: Sat, 2004-08-07 18:58
valiant wrote:
jmullan wrote:
Quote:
Consequence: We'd have two sets ids but they are not interfering.
What you think?

I'm confused - what's to stop us from extending the gallery user to have a xaraya user id in addition to the element id? The Xaraya module could clean up the G2 users based on the Xaraya table each time it is loaded - create any new ones and delete any missing ones...

What you propose is an asynchronous method, violating data integrity.

Data integrity schmata integrity. If you have a Xaraya user only in Xaraya until the next request that hits G2, how will it make any difference? That "orphan" Xaraya user can't own or modify anything in G2 without hitting G2 to assign the ownership or do the modification. Whether gallery waits for an event or makes it own events based on discrepancies doesn't make that big of a difference in the grand scheme of things.

SELECT
  xarayaid
FROM
  xarayausers
  left join galleryusers
    on xarayausers.xarayaid = galleryusers.xarayaid 
WHERE
  galleryusers.xarayaid IS NULL
SELECT
  xarayaid
FROM
  xarayausers
  left join galleryusers
    on xarayausers.xarayaid = galleryusers.xarayaid 
WHERE
  xarayausers.xarayaid IS NULL
valiant wrote:
I don't like syncing. What if someone uses GalleryRemote in between etc. And sync G2/xaraya for every request isn't very effective.

When Galleryremote hits G2, the Xaraya module would fire up and do its cleanup. Unless you are creating and deleting hundreds of Xaraya users every second it wouldn't be that huge of a hit.

valiant wrote:
Goal: Users / Groups are managed by the CMS (xaraya). You don't create/update/delete users in G2 which is embedded in the CMS.

So the Xaraya module would disable the user and group creation and deletion in G2.

valiant wrote:
And the CMS doesn't create a GalleryEntity when creating a user/group. So there is no entry in the GalleryEntity table for a CMS user and GalleryCoreAPI::loadEntitiesById($ids) won't work.

Except that the first thing that happens when you load any page is that the Xaraya module fires up and creates the missing Gallery user. Everything in G2 would continue to use the entity id, except that it would refer to a G2 instance of a Xaraya user.

valiant wrote:
But if you ultimately don't want to change GalleryCoreAPI::loadEntitiesById($ids), we could add another column to the GalleryEntity table called 'external_id'. Those CMS that have events would create a G2 Entity with 'external_id' = CMS user_id whenever a user has been created. Those CMS that don't have events, would depend on G2 to sync on each request and create entries in GalleryEntity for all missing users. But as I said, I don't like this hack.

It wouldn't be in the GalleryEntity table, though, I think. This is a user property - it should either go into the user table or a mapping table.

I don't think that it is hacky at all. If you ever switch from Xaraya to something else, you won't lose your whole set of users and permissions - if you disable the Xaraya module your G2 would still work independantly.

It's your module, though, I'm just offering discussion.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Sat, 2004-08-07 19:35

Ok, it's only 1 single db query for each request of "overhead" for the syncronization.
Actually you convinced me, it's a far better approach.
Pro:
+ Less modifications in G2
+ All GalleryUsers are GalleryEntities, perfect

kudos get jmullan :)

I'll add 1 column to GalleryUser and GalleryUserGrouMap Table, 'g_extId'.
All user/group/map data will be stored in the CMS tables, the G2 tables user/group will only contain g_id and g_extId for the CMS <-> G2 user/group mapping.

edit: I'll add the column 'g_id' to the xaraya roles table instead adding a column to the G2 user/group tables, this way I need 1 table join less and the translating the queries gets as easy as it was before without the G2 user table as a 1:1 mapping table.
when migrating from a integrated G2 to a standalone G2, we'll have to extract the user<->group mapping anyway from the CMS, so we'll create a script for this anyway. it shouldn't be an argument to have all G2 user ids in a G2 user table just for the sake of it.

edit: Differentiating between two sets of ids for each user/group, a G2 internal (aka EntityId) and a CMS specific (xar_uid) introduces a major difficulty. The group<->user map is done with the xar_uid and the rest (SELECT, ORDER BY, WHERE = ?, ...) is g_id.
I've extended my Query translator to handle this, hope other CMS can use it too...

here's an example what the translater does:

G2:

SELECT
          [GalleryUserGroupMap::userId],
          [GalleryUser::userName]
        FROM
          [GalleryUserGroupMap], [GalleryUser]
        WHERE
          [GalleryUserGroupMap::groupId] = ?
          AND
          [GalleryUserGroupMap::userId] = [GalleryUser::id]
        ORDER BY
          [GalleryUser::userName]

target: 

SELECT u0.g_id, u0.xar_uname
FROM xar_rolemembers, xar_roles as u0, xar_roles as g0
WHERE xar_rolemembers.xar_parentId = g0.xar_uid
AND g0.g_id = ?
AND xar_rolemembers.xar_uid = u0.xar_uid
ORDER BY u0.xar_uname

what do you think?

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Sat, 2004-08-07 21:04

@bharat:

how is this "make a XarayaUser" meant to work?

I create a GalleryXarayaGroup_core class extending GalleryGroup.
I create an interface GalleryXarayaGroup extending GalleryXarayaGroup_core (later it's created automatically, i know).
Know i want G2 to return an instance of GalleryXarayaGroup, everytime someone calls:
GalleryCoreApi::newFactoryInstance('GalleryEntity', 'GalleryGroup');

But the factory will return a GalleryGroup.
registerImplementation() and newInstance() don't allow registering a "GalleryXarayaGroup" implementation of GalleryGroup implementation of classtype GalleryEntity.

I mean, we won't change all the GalleryCoreApi::newFactoryInstance('GalleryEntity', 'GalleryGroup'); calls.
But I can't register a GalleryXarayaGroup implementation, as the class name doesn't match 'GalleryGroup'.
What I could is write another "GalleryGroup" class in my module path, unregister the GalleryGroup implementation of the core module and register mine. Is that what you want? A class with exactly the same name that doesn't derive from GalleryGroup but from GalleryEntity???

Or and that's what i guess, i don't fully understand the concept. Please elaborate :)

 
bharat
bharat's picture

Joined: 2002-05-21
Posts: 7994
Posted: Sun, 2004-08-08 20:37

First of all, it looks like you're making tremendous progres. I like the idea of doing the synchronization the way that you're suggesting. This is pretty much what the PNphpBB2 modules does to integrate PhpBB2 into PostNuke (that's this forum system) and it seems to work well.

As for the GalleryGroup factories issue, I think you understand the concept just fine, it's just that I didn't think things through to this level of detail when I was setting up the factory implementations. The problem is that we're using the class name as the id, which is wrong. We should separate out the fact that you can register an implementation of GalleryEntity for id GalleryGroup with class name XarayaGroup. That'll require reworking the factory code a little bit, but it's the right thing to do.

For now (since we're just trying to hack it so that it works) I think that you should unregister the GalleryEntity->GalleryGroup implementation and register your own GalleryEntity->GalleryGroup but pass in your own class name, eg:

GalleryCoreApi::registerFactoryImplementation('GalleryEntity',
    'GalleryXarayaGroup',
    'GalleryGroup',
    $classDir . 'GalleryXarayaGroup.class');

Hm. As I typed the above, I realize that we actually do separate out the class name and the id and from reviewing the code it seems like this would get put into the factory correctly so that if you asked for a GalleryEntity->GalleryGroup you'd get a XarayaGroup. I'm late for the door (kids are banging on the office door yelling "DAD WE'RE HUNGRY") and Jen is giving me looks so I can't test this out now but this is should work. If it doesn't, we can always hack up the factory (just put in some special case if/then blocks in there) to make it work and let me know and I'll fix it so that it Does the Right Thing.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2004-08-08 21:00

we are here:

function registerFactoryImplementation($classType, $className, $id, $fileName, $hints=array()) { ... }
suggested call: GalleryCoreApi::registerFactoryImplementation('GalleryEntity',
    'GalleryXarayaGroup',
    'GalleryGroup',
    $classDir . 'GalleryXarayaGroup.class'); 
definition: function newFactoryInstance($classType, $className=null) { ...}
call: GalleryCoreApi::newFactoryInstance('GalleryEntity', 'GalleryGroup'); 

we are calling by $className and not by $id, that's the problem.

how to fix it?
just use the second argument of newFactoryInstance() as $id. perhaps you should check if that works before commiting to cvs, lol.

btw: I've changed the GalleryUser.class to make it work for now. More and more works fine, I'll have to change the UserGroupHelper classes too, as xaraya has a roles tree. So if user A is in group AllUsers and group AllUsers is in group Everybody, there's no direct map for user A beeing in group EveryBody and A won't have G2 permissions to see pictures an anonymous user would see.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Sun, 2004-08-08 23:44

update:
it works :)
- in init.php i check with 1 simple query if there are xaraya roles (users/groups) that have not a G2 Entity and if so create Entities for them.
- i get the active user from xaraya
- replaced 3 user group map functions in the UserGroupHelper classes using a mixture of xaraya API and G2 queries
- changed some functions in DatabaseStorage.class and extended the translateQuery
- changed User / Group class and interface. In future I won't have to change them when I can register my own implementations
- added g_id and g_language to xaraya roles table, gonna have to find a solution for g_language as it can't stay there, i think (redundancy etc.)

todo:
- unregister User/group views
- don't display login / your account link
- put css and javascript from G2 to xaraya <head> section
- get G2 page title somewhere
- G2 links[] -> somehow to xaraya menu
- put the whole thing into a module

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Mon, 2004-08-09 12:37

@adodb: xaraya search module (THE search function in xaraya) won't work with $hasInsertID set to true;
at the same time, gallery2 won't work with $hasInsertID set to false;
tempory fix: set it to true, don't use search function of xaraya.

update:
- i define('G2_MODE_EMBEDDED', 1) in main.php because I can't "unregister" certain (Login, View Account, Manage Users, Manage Groups) links in getSystemLinks() and getSiteAdminViews() in core module.inc.
So I use the defined constant as a switch to include these links or not.

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Fri, 2004-08-13 21:42

I wanted to summarize all changes needed to make G2 work within xaraya (not quite all changes, I've not yet started looking into a search xaraya plugin).
Most changes can't be made with a G2 module and are marked as such.

Changes needed:

G2: indicates that the changes cannot be made without changing G2 files
module: part of the xaraya module in G2
module in xaraya: part of the gallery2 module in xaraya

- module in xaraya or main.php: define('G2_MODE_EMBEDDED', 1);
- G2: GalleryUrlGenerator::getCurrentUrlDir(): add function preg_addslashes (non standard function) as xaraya base file contains question marks etc.
function preg_addslashes ($foo)
{
return preg_replace("/([^A-z0-9_-]|[\\\[\]])/", "\\\\\\1", $foo);
} /* credits to the one who posted this function as a comment to php.net */
- G2: GalleryUrlGenerator::buildQueryString() changed: ? -> & because new baseFile already has first argument
- G2: modules/core/classes/GalleryStorage/DatabaseStorage/ErrorHandler.inc
define('ADODB_ERROR_HANDLER') already defined -> if(!defined...
- G2: @adodb GenId problem:
G2 uses the adodb class definitions from xaraya and xaraya has hasGenId set to false.
As a temporary fix I set hasGenId = true in the xaradodb/drivers/adodb-mysql.inc.php in xaraya.
- G2: had to change GalleryUtilities::convertPathToUrl(), the function makes assumptions that don't apply to an embedded G2, i use $_SERVER['DOCUMENT_ROOT'] to fix it. (function neglects difference between gallery base path and xaraya base path)
- G2: getting the whole header in main.php, parsing it in xaraya for title, css and javascript
- G2: unregister core module::getSystemLinks() (login, your account), move language setting to xaraya
had to change getSystemLinks in core module.inc. not including the links based on define('G2_MODE_EMBEDDED', 1); which is set in xaraya
- G2: unregister core module::getSiteAdminViews() (User Group Management)
had to change getSiteAdminViews in core module.inc, btw. typo ("," too much) in original function in return value. not including the links based on define('G2_MODE_EMBEDDED', 1); which is set in xaraya
- G2: in init.php: put some xaraya code to get the current user and all xaraya users/groups that don't have a corresponding G2 entity yet.
- module: created a GalleryXarayaUser and a GalleryXarayaGroup in a G2 module
- G2: DatabaseStorage.class: switching between G2 standalone and embedded mode based on the constant G2_MODE_EMBEDDED. when in embedded mode: translate user/group queries differently and don't create/update/insert/delete data managed by xaraya (user, group, -map tables)
- G2: GalleryFactoryHelper_simple::newInstance() changed (temp fix): if className == Group or User, change it to XarayaGroup/User.
- G2: GalleryUserGroupHelper_medium::fetchUsersForGroup() changed (xaraya roles tree)
- G2: GalleryUserGroupHelper_medium::fetchGroupsForUser() changed (xaraya roles tree)
- G2: GalleryUserGroupHelper_simple::isUserInGroup() changed (xaraya roles tree)

ToDo:
- module: GalleryUser::language, not yet set-able
- G2: need system/item/... links from G2 with urls somewhere, so that i can pass them to xaraya
- module: when activating the xaraya module in G2, copy the entity ID of the default usergroups/anonymous/admin user to xaraya
- G2: create direct DownloadItem links and handle the renderShortCut (problem: the gallery cookie is sufficient to authenticate a user. there's only 1 possibility when it's insufficient: when a xaraya user has logged of (G2 cookie doesn't know that he logged of).
- G2: set global templates: before activating G2 xaraya module use global.tpl, after use global.tpl.local (remove html, head, body tags)
- module in xaraya: define('G2_MODE_EMBEDDED', 1);
- G2: GalleryUrlGenerator: baseFile different before and after activating G2 xaraya module
-> GalleryUrlGenerator should set the basefile according to the current
- G2: main.php different before and after activation of module (print HTML / copy HTML to variable)

 
valiant

Joined: 2003-01-04
Posts: 32509
Posted: Wed, 2004-08-18 16:51

This integration is not about xaraya exclusively, so i don't want to use mechanisms that possibly are unique for xaraya (i.e. events/hooks) to fasciliate upcoming integrations with typo3, pn, etc.
question:

right now, i check for each G2 request if all xaraya users/groups have their G2 entities and what the G2 entity ID of the active user is with a single query:

SELECT xar_uid, xar_uname, g_id
FROM xar_roles -- roles table is for users and groups :)
WHERE xar_uid = $xar_uid_from_xaraya_cache -- for current user
OR g_id IS NULL -- all G2-entity-less users/groups

I do this because:
a) g_id is not part of xaraya session data, i need to get it from the db anyway. perhaps i can add it to the user data cache, but that mechanism surely wouldn't work with all CMS'.
b) it could be the first G2 "visit" of the user and a G2 entity Id has to be created. plus, i can create G2 entity Ids for all "pending" xaraya groups this way.

but the db query is not really necessary...
- if we added the xaraya u(ser)id (a member of GalleryXarayaUser) to G2 session data (with session->put()), i could check if the xar_uid from the xaraya session is the same as the one from the G2 session. if not, is it the anonymous user? if not create a G2 entity for the user.
- xaraya groups don't need to be "imported/synced" to G2, unless they are used (assigned/revoked privileges). so we could syncronize xaraya groups/users that don't have G2 entities yet when loading a G2 permission admin view.

what do you think of that concept?
- only sync users if really needed (user has no G2 entity id/change permissions)
- add xaraya user id to G2 session data

where is the correct place to add the session->put() line?

@search integration:
i need to call the G2 search function from xaraya and get the search results as an array, i guess at least title/url per item. how...?
I could provide the gallery user id and the search phrase. what i need is either some special G2 call or direct access to a module/search API.