I'm working on a project where I'd like to create membership in an organic group based upon some external source (in my case, CiviCRM). It looks like it would be pretty easy to do this by either by having og_get_subscriptions check the external source and merge it into the list in $user->og_groups, or do this at hook_user load time by munging the output to og_get_subscriptions.
The advantage here is to use a source like CiviCRM or LDAP to get the notion of group membership. This also means that we'd no longer need to have group member ship be static, but could even have it change for the same user by various criteria.
Rather than get into a "race" with og.module, it seems to me that og.module could do this by calling hook_og with a new 'user load' event as $op, $nid being undefined, $uid for the user being loaded, and $args being the value of $user->og_groups as og has determined from its own tables.
From where I'm sitting, this looks clean and relatively safe, but I haven't thought this through as well as folks like Moshe or Gerhard have done. Is this the best way to do what I'm trying to do.
| Comment | File | Size | Author |
|---|---|---|---|
| #27 | abstract_membership_source.patch.txt | 8.49 KB | Torenware |
| #26 | og_membership_0.inc | 1.5 KB | Torenware |
| #25 | og_membership.inc | 1.5 KB | Torenware |
| #18 | og_ul_test.module | 1.59 KB | Torenware |
| #17 | og_user_load_revised.patch.txt | 2.56 KB | Torenware |
Comments
Comment #1
Torenware commentedHere's a patch to implement this. This uses module_invoke_all to merge new organic group nids into the list og produces for a user by default.
It's been tested under Drupal 4.7 and a modified og.module from HEAD (rev. 1.148)
Comment #2
moshe weitzman commentedthat should work ... i'm not inclined to accept a patch like this right now though. i want to do the na_arbitrator integration and see where we stand (see http://www.tejasa.com/node/112). i think that will make your life simpler. i think that once og is using na_arbitrator and views, folks will see that it isn't all that hard to roll your own og anymore with your own requirements.
Comment #3
Torenware commentedMosh,
Thanks for looking the patch over.
Cool. I'll keep tracking the na_arbitrator stuff. When do you think you'll have something useable in HEAD?
It's easy enough for me to keep using a private version of og until you're ready with your new approach.
Rob
Comment #4
venkat-rk commentedIs there any update on this? This patch seems preferable to og_civicrm (which has been languishing for months now) for the reasons outlined in http://www.torenware.com/node/64
Thanks.
Comment #5
moshe weitzman commentedno status change. see my last comment.
Comment #6
EmanueleQuinto commentedWaiting for update on na_arbitrator integration there is a little correction to Torenware patch.
If there is no other modules with hook_og $subscriptions[$uid] would be resetted, so I suggest
turn into
ema
Comment #7
Torenware commentedMoshe,
I understand you've gotten through most of the major upgrade of og.module you referred to some months ago. Webchick suggests that I revive this issue so that it can get some discussion, since in order to get Organic Groups to work well with CiviCRM smart groups (memberships defined by canned queries, basically), it needs to be possible for a Drupal module to reliably modify what og_get_subcriptions() returns. I've just studied the current 4.7 sources, and this is still not possible.
If you are still unconvinced that there is demand for this, it may make sense to take this discussion to the CiviCRM dev list, where the folks there can explain in more detail what various groups would like to do by integrating OG with CiviCRM.
Thanks,
Rob
Comment #8
moshe weitzman commentedyes, this is a good time to revive this discussion ... the last patch is nice and simple. but does it really suffice? for example, each group has a subscribers page which lists all members. doesn't that have to change as well? and other modules use the subscriber list for things like assigning issues.
Comment #9
Torenware commentedI'd need to think about this, and most likely, some other folks who want OG support should probably chime in.
The big problem here is that it could be expensive. A typical use case here would be a political site where you want to limit people by where they live. You might have thousands of "hits" in the CiviCRM data store, of which only a handful actually have Drupal accounts.
I'll need to check if CiviCRM can do this sort of search efficiently, because otherwise the proposed Drupal hook will take quite a long time to execute.
Comment #10
Torenware commentedMosh,
Giving your points a bit more thought, it sounds like a "external membership source" like CiviCRM might need to handle several types of call backs:
One of the issues I'm less clear on is how this needs to interact with the Node access system. It's possible that there would need to be some support to make sure that if user X loads a subscription to group Y via the hook, that OG's node access queries include these groups. You'd know a lot more about that I would, although I have spent a fair bit of time lately in hook_db_rewrite_sql. Do you have a sense what if anything a patch would need to do for this to work right?
Comment #11
moshe weitzman commentedrob - if we get that user load op working properly, then all node access should work as well. the node access grants are issued based on the user's groups (which are calculated in this patch).
this gets messy though. for instance, there is a count of subscribers shown on the list of groups page. are we goig to have to ask civicrm for those counts?
Comment #12
Torenware commentedI went back and forth on this one with Lobo last night.
Here's what I recommend: the hook needs to ask for 3 things, AFAICT:
In current builds of CiviCRM 1.5, (1) is easy, and (2) and (3) can be done, but not with great efficiency when the number of contacts in CiviCRM is very large.
From the perspective of OG, here's what I suggest:
Tell me what you think of this. I'll procede on the OG code, since this will remove any Drupal side impediment to doing these kinds of external membership source modules.
Comment #13
moshe weitzman commentedwell, lets proceed as you propose. i'm not totally confortable with this path, but i'm not uncomfortable either .. .as long as you are doing a small demo module, could you try out membership lists based on profile.module values? so create a profile field called 'gender' and then create groups whose members are dynamically generated based on the male/female response to this field.
i'm not so sure what 'qualified contact records' really means. what i really want is
- drupal uid if available. if not available, i need much more stuff. basically, see the fields retrieved in og_list_users_sql() ...
- display name (i.e. username)
- email address
- link to 'profile' page
- is user an admin in this group?
- user picture if available
- date user joined this group
- is user a member of just a 'pending acceptance' state.
Comment #14
Torenware commentedMosh,
I did a quick look at DRUPAL-4-7 and HEAD. In both cases, the following call backs are implemented:
I propose adding the following 3:
Does that work for you?
Comment #15
moshe weitzman commentedCan we make an assumption that a group owner has to have a real drupal UID? for any given site it could be hard coded to same UID. not sure how to handle ownership of the group node if memberships is dynamic.
Comment #16
Torenware commentedI don't see this as a problem. Most use cases I've seen make the most sense if the organic group is created on the Drupal side. A module that implements dynamic membership can then be required to have such a group owned by a real Drupal user.
In addition: OG should only have to deal with real Drupal users. A module implementing dynamic membership should only be telling OG about real users. If a module needs to "presubscribe" user in Drupal, it needs to do this transparently to OG.
In practice, this isn't a big problem, since it's relatively easy to write code that hooks civicrm into creating Drupal users on the fly as needed. I suspect it wouldn't be hard for other external membership sources either (e.g., a LDAP user store).
Comment #17
Torenware commentedOK, here's a patch to the DRUPAL_4_7 branch. HEAD is probably pretty similar, but since I don't have an install handy, it makes sense to get this working, and I'll then investigate what's required for HEAD.
I'm doing more error checking than the old patch, to make sure that the subscription arrays look like real subscription arrays.
Also, I'm only doing the 'user load' portion here. Looking through the og.module source, user lists are getting implemented two ways I know of (you may of course know of others):
To hook this safely, it's probably necessary to factor code that is doing either of these two things, so that we can make sure the notion of a "list of subscribers" is abstracted away a bit from the the {og_uid} table.
The enclosed code loads without error; I'll write a small test module to test it, and will upload it when ready.
Comment #18
Torenware commentedMosh,
Here's a simple test module (4.7 compatible, might be 5.0 compatible as well, depending upon what hook_settings does in 5.0).
It's working on my 4.7 install, and makes it clear how to call the proposed hook.
As before, I'm not touching the subscriber listing code yet, since it's a bigger job, and may not be needed for a CiviCRM use of the feature. It might make sense to put the "factoring" of the listing code as a separate issue, but that's up to you.
thanks,
Rob
Comment #19
Torenware commentedJust bumping this a bit to get feedback on my patch.
Just apply the patch to your test system; you can use the test module og_ul_test.module to see an example of a module that calls the modified hook.
Thanks,
Rob
Comment #20
dalinRob I've tried your patch and I get the following error on every page (I will investigate further, after I get that block disabled):
Comment #21
dalinThe error still happened once I got the og blocks off.
I'm a bit baffled by this one. I am using 4-7 and CiviCRM 1.5 and the table 'civicrm_contact' exists (It's in a seperate db though).
The line that the drupal error happens on is:
foreach ($user->og_groups as $key => $sub)But a print_r($user) right before shows that og_groups has not yet been added to the user object.
I can debug some more if you give me some direction.
Comment #22
dalinah,
I needed to disable CiviNode
Comment #23
Torenware commenteddalin,
thanks for testing the patch, BTW.
Just to make sure: which patch did you use?
My latest patch did not actually access CiviNode or CiviCRM directly, since I was simply testing the 'user load' mechanism itself. But if I remember right, CiviNode may still have a version of the 'user load' hook that is incompatible with the current one.
I'll see if I can whip you up an example module that uses the current calling conventions. I'm also working on some of the stuff that Moshe suggested, and that will hopefully get up here soon.
Comment #24
dalinIf retried the patch and test module and all is ok afaict.
Comment #25
Torenware commentedMoshe asked that I look into handling the subscriber listing features of og.
I have a "candidate". The trick here is that you pretty much have to turn a query into an object (since it simply won't work to use Drupal query result objects as "handlers" for a query: some sources aren't doing SQL queries at all). So as a first step, I've taken the current code and wrapped the database queries. This will allow me to support CiviCRM (or even LDAP) as a second pass, when I turn the creations of objects into a "factory method". Then each organic group can have its own private notion of membership, and it will no longer be hard-coded into og.module as it currently is.
I'm putting this up in two parts. The first part is a new .inc file that defines a "membership source object". the second is the patch to og that uses it.
Comment #26
Torenware commentedOops.... minor glitch with og_membership.inc. Here's the fixed version.
Comment #27
Torenware commentedHere is the patch that uses the *revised* og_membership.inc
Comment #28
moshe weitzman commentedthis approach is not consistent with drupal core or contrib and thus not at all desirable.
Comment #29
Torenware commentedIt does indeed use an OO approach. But it's worth remembering that there's more to OO programming than simply classes; the "handle" based approach that database libraries use are also basically OO.
I'm not married to using classes, but what *really* is needed here is abstraction. Your current code is very low level, which is also not good practice. How would you suggest removing your low level references to the og_uid table, which is the root cause of why adding an external membership source is so difficult.
You don't have to like my approach, but I think you are obligated to suggest a reasonable alternative if you reject it. So step up to the plate and tell me what you *will* accept.
This issue has been floating around for around 6 months, and past a certain point, I do think you have an obligation to help get it resolved, one way or another.
Comment #30
moshe weitzman commentedI cannot think of a solution which is consistent with core and achieves all that we desire for this feature.
Comment #31
Torenware commentedSounds like my goals are incompatible with og.module.
But this functionality is needed within Drupal; and og has a lot more features than this class of application really needs. So another module, I guess.
Since the core need is relatively simple, I should have something in a couple of weeks.
Comment #32
moshe weitzman commentedComment #33
dalinThis suggestion may show my naivety:
Instead of fiddling with user_load(), why not just create a hook system for og. There are going to be more and more people who want to create extended functionality on top of og. Moshe I can imagine that at this point you're wanting to limit the number of new features as it might start to detract from the overall quality of the module. So to enable other people to create add-ons we could create an extensible solution of something like hook_og($op, $gid, $uid).
I say this without really knowing what is entailed to make that happen.
Comment #34
joe.murray commentedI have a strong need for og and civicrm to be integrated. I also think it would be useful to integrate og with external user/group validation/sources like LDAP. For certain political and NGO clients, the large number of CiviCRM contacts segmented in various ways into groups should not be mapped to Drupal og's.
Without examining the code or the technical issues, this thread seems to show an unreasonable unhelpfulness in achieving these sorts of objectives.
Rob, has anything happened with regard to your last post's suggestion about another module?
Comment #35
Torenware commentedI am starting a bit of work that may be relevant to this.
The main issue is that OG really does not (and as it is currently going, will not) support integrating lists of users culled from a non-Drupal source. The reason for this is the very tight integration between the node access system and og, which is based upon rewriting queries. If your member list can easily be added to Drupal side query -- which can require constructions of queries with IN () clauses with thousands of items -- you are hosed.
I'm doing a module now that requires tight integration with CiviCRM. The approach I'm taking will require two hooks, both implemented via CiviCRM's callback mechanism:
Doing this in two steps will make it easier to support integration with Joomla as well, since modules that need to know a lot about Drupal don't have to know much about CiviCRM, and visa versa. It also means that we can share code more easily, since the query-rewriter can be shared between the Drupal and Joomla user frameworks, and the "user query" function can be very simple and very framework specific.
Note this scheme might allow you get the effect of some of the other integration schemes that we've tried in the past, but without either doing really scary mirroring, real-time manupulation of node access tables, or G'd forbid patching of og.module, which is just a maintenance nightmare.
If this works well, we can start thinking about how to generalize this. But for now, I consider patching og for this purpose a dead issue.
Comment #36
moshe weitzman commentedRob - please don't comment on my intentions. I want external membership lists for groups. If someone produces a patch which is as simple as possible and as drupalish as possible, then it will be committed. This issue may be dead for you, but it isn't for me.
Comment #37
Torenware commentedMoshe,
If you truly want the feature, facilitate its implementation.
The issue here is architectural, and I do not believe anyone other than yourself can make it happen. Since this issue has been hanging out here for more than a year (this is not the first thread on the topic), some other solution can and will be found. I do not believe it will make large use of OG.
I don't know your intentions. But as a practical measure, a solution for this will likely be found for the CiviCRM community, and fairly soon. I would love to have your help. But I am not expecting it, based upon several previous attempts to solve this problem within the OG framework. I can't maintain patches on a moving target, and I can't ask my clients to do so either.
If you think that this can be resolved within the framework of your module, then prove it to me. But I will not continue to submit patches to OG for this purpose.
Comment #38
majortom commentedJust wanted to check what on the current status of integrating CiviCRM and OG? I am very interested in being able to do this, under Drupal 5.x with CiviCRM 1.6 (and soon I hope 1.7). As CiviCRM adds event registration in 1.7, this integration becomes even more valuable.
Comment #42
majortom commentedIs this issue dead? Drupal 6 version?
Comment #44
Dan Saltman commentedwarning: Invalid argument supplied for foreach() in /home/albertag/public_html/beta/modules/og/og.module on line 71.
unrecoverable error
Sorry. A non-recoverable error has occurred.
Database Error Code: Unknown table 'civicrm_contact' in where clause, 1109
Return to CiviCRM menu.
error
Array
(
[callback] => Array
(
[0] => CRM_Core_Error
[1] => handle
)
[code] => -1
[message] => DB Error: unknown error
[mode] => 16
[debug_info] => SELECT
civicrm_group_contact.id as civicrm_group_contact_id,
civicrm_group.title as group_title,
civicrm_group.visibility as visibility,
civicrm_group_contact.status as status,
civicrm_group.id as group_id,
civicrm_subscription_history.date as date,
civicrm_subscription_history.method as method FROM civicrm_contact contact_a LEFT JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id LEFT JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id LEFT JOIN civicrm_subscription_history
ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id
AND civicrm_group_contact.group_id = civicrm_subscription_history.group_id WHERE civicrm_contact.id = 101 AND civicrm_group.is_active = '1' AND civicrm_group_contact.status = "Added" ORDER BY civicrm_group.title [nativecode=1109 ** Unknown table 'civicrm_contact' in where clause]
[type] => DB_Error
[user_info] => SELECT
civicrm_group_contact.id as civicrm_group_contact_id,
civicrm_group.title as group_title,
civicrm_group.visibility as visibility,
civicrm_group_contact.status as status,
civicrm_group.id as group_id,
civicrm_subscription_history.date as date,
civicrm_subscription_history.method as method FROM civicrm_contact contact_a LEFT JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id LEFT JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id LEFT JOIN civicrm_subscription_history
ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id
AND civicrm_group_contact.group_id = civicrm_subscription_history.group_id WHERE civicrm_contact.id = 101 AND civicrm_group.is_active = '1' AND civicrm_group_contact.status = "Added" ORDER BY civicrm_group.title [nativecode=1109 ** Unknown table 'civicrm_contact' in where clause]
[to_string] => [db_error: message="DB Error: unknown error" code=-1 mode=callback callback=CRM_Core_Error::handle prefix="" info="SELECT
civicrm_group_contact.id as civicrm_group_contact_id,
civicrm_group.title as group_title,
civicrm_group.visibility as visibility,
civicrm_group_contact.status as status,
civicrm_group.id as group_id,
civicrm_subscription_history.date as date,
civicrm_subscription_history.method as method FROM civicrm_contact contact_a LEFT JOIN civicrm_group_contact ON contact_a.id = civicrm_group_contact.contact_id LEFT JOIN civicrm_group ON civicrm_group.id = civicrm_group_contact.group_id LEFT JOIN civicrm_subscription_history
ON civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id
AND civicrm_group_contact.group_id = civicrm_subscription_history.group_id WHERE civicrm_contact.id = 101 AND civicrm_group.is_active = '1' AND civicrm_group_contact.status = "Added" ORDER BY civicrm_group.title [nativecode=1109 ** Unknown table 'civicrm_contact' in where clause]"]
Same error for me...
Comment #54
joe.murray commented