Last week I wrote about Organic Groups in Drupal and how to customize the way it controls access to content. This article describes how you can use the Rules module to create a custom, lightweight solution for sending email notifications to members of a group whenever new content is added.
Note: this article is outdated. It covers Drupal 6, not 7.
Email notifications with Organic Groups usually spells Notifications and Messaging. OG has built-in integration with Notifications, which makes it the natural choice when building a group site. But Notifications and Messaging are fairly complicated to set up and can be unnecessarily complex for sites that really just need simple email notifications without all the extra features that the Notifications/Messaging framework provides. (You can for example give your users the choice of additional delivery methods such as SMS, and it's possible to send out periodic message digests etc.)
Rules
Enter Rules. This immensely powerful Drupal module is one of those building blocks that really makes Drupal the flexible toolbox that it is. While CCK helps you model your project's data, and Views allows you to query and output it in all sorts of ways, you could argue that Rules plays the same role when it comes to your project's business logic; it provides a web interface for defining actions that your site should take when different types of events occur, depending on various conditions. (Drupal core has similar functionality with triggers and actions, but Rules really takes all this to a whole different level.)
So we'll have a look at how you can use Rules to reproduce some of the functionality of Notifications/Messaging. The beauty of this solution is that it's completely flexible and configurable. You, or an administrator, may at any time log in and change things like how the notification messages are formatted, what node types trigger notifications, etc. However, before you go off building your own solution in this way, you should be aware of two things:
- You will have to define a new Rule action in a custom module.
- Because of some limitations in the Token module, you won't be able to include the standard node body field in messages. You can work around this however by using a CCK field for the message body.
Describing exactly how Rules is used is out of scope for this article, but there's lots of documentation available for those who are new to this module. The handbook page on drupal.org is a good start.
An event and a condition
So how does Rules fit in this particular scenario? If you consider the problem at hand, we want to send an email to the members of a group, whenever something is posted to that group. In Rules lingo, we want to trigger an action whenever a particular event occurs based on certain conditions. In other words, our problem fits the Rules paradigm very well.
Let's begin with the event. Nodes or comments being created is one of the basic built-in events in Rules, so it's no match to quickly setup the base of our rule:
- Go to Administer > Rules > Triggered rules > Add a new rule.
- Give the rule a name like "Notify group members of new content".
- Choose the proper event: "After saving new content".
Once you're done with this rule, you might want to create a second rule which instead uses the event "After publishing a new comment" so that users can also get notified about comments to group posts.
Now, so far this rule will run everytime any node is created. We need to add a condition which will tell Rules to only run the action if the node is within a group. Luckily for us, OG integrates with Rules' API to provide the condition "Content is a group post", which is exactly what we need:
- Click the Add a condition link.
- Choose Created content as the Argument for Group post. The argument mechanism is used throughout Rules in order to allow for different input data for its conditions and actions.
- You can leave the other options at their default values and click Save.
Where's the action?
What's left is the actual thing that we want this rule to do: send an email to the groups members. This time, things aren't as simple any more. None of the actions that come with Rules and OG provides us with exactly what we need. There are a couple of built-in actions for sending e-mail, but none of them knows anything about groups. The action that most resembles our use case is "Send an email to all users of a role". There's just one problem, how can we replace "users of a role" with "members of a group"?
Well, it's time to get our hands dirty. In order to define a new Rules action of your own you have to create a custom module – but don't worry, the code is actually quite short and straightforward. We'll be able to take the role-based action mentioned above and modify it to fit our needs. And in the meantime, you'll get a nice quick introduction to a couple of Rules' and Drupal's APIs.
First, we need to tell Rules the basics about the action we're about to define. Create a stub module with a suitable name, paste in the following code in a mymodule.rules.inc file, and change "mymodule" everywhere in the code to whatever name you chose:
<?php
/**
* Implementation of hook_rules_action_info().
*/
function mymodule_rules_action_info() {
return array(
'mymodule_action_mail_to_users_of_group' => array(
'label' => t('Send an email to all users of a group'),
'module' => 'MyModule',
'eval input' => array('subject', 'message', 'from', 'group'),
),
);
}
?>What we do here is rather straightforward:
- We give the rule a unique machine readable name and a human readable label.
- We specify which module it belongs to.
- We define the settings that should be available to the rule: subject, message, from, and group. These settings will appear as fields on the action configuration page.
The next step is to define this configuration form, which we'll use later on:
<?php
/**
* Action "Send a mail to members of a group" configuration form.
*/
function mymodule_action_mail_to_users_of_group_form($settings = array(), &$form) {
$form['settings']['group'] = array(
'#type' => 'textfield',
'#size' => 10,
'#title' => t('Group ID'),
'#prefix' => t('WARNING: This may cause problems if there are too many members in this group, as your server may not be able to handle all the mail requests all at once.'),
'#required' => TRUE,
'#default_value' => isset($settings['group']) ? $settings['group'] : array(),
'#description' => t('Supply the node ID (nid) of the target group.'),
);
// We rely on the Mail to user action to build the rest of the form fields.
rules_action_mail_to_user_form($settings, $form);
}
?>This function uses regular FormAPI arrays to define form fields for the settings page. As you may see, we only define one field here: Group ID. For the rest of the fields (subject, message and from) we rely on another action that Rules provides: "Send a mail to a user".
As the description text states, there is a potential problem with this action when a group has a very large number of members. In that case, the Notifications/Messaging solution might be a better option.
Now for the actual action implementation:
<?php
/**
* Action: Send mail to all users of a specific group.
*/
function mymodule_action_mail_to_users_of_group($settings) {
$from = ($settings['from']) ? str_replace(array("\r", "\n"), '', $settings['from']) : NULL;
if ($group = node_load($settings['group'])) {
// Query the database for all members of this group.
$sql = og_list_users_sql();
$res = db_query($sql, $group->nid);
// Send the messages.
$message = array('result' => TRUE);
while ($member = db_fetch_object($res)) {
$user = user_load(array('uid' => $member->uid));
if ($user->status) {
$message = drupal_mail('mymodule', 'action_mail_to_users_of_group', $member->mail, language_default(), $settings, $from);
}
}
if ($message['result']) {
watchdog('rules', 'Successfully sent email to the members of %group.', array('%group' => $group->title));
}
}
}
?>Again, this is mostly based on the "Send an email to all users of a role" action. The difference is that it loads a particular group node and fetches the members of that group, rather than the users of a particular role.
Update: This snippet now checks if a user is active or blocked before sending the notification.
Just like in the original action, we use drupal_mail() to send the messages. The way Drupal's mail API works, we have to implement hook_mail(), where we'll set the message's subject and body text:
<?php
/**
* Implementation of hook_mail().
*
* Sets the message subject and body as configured in the $settings of the action.
*/
function mymodule_mail($key, &$message, $settings) {
$message['subject'] .= str_replace(array("\r", "\n"), '', $settings['subject']);
// Process the mail body.
$message['body'][] = drupal_html_to_text($settings['message']);
}
?>The $message array is passed by reference, and there's no need for this function to return anything.
The other thing to note about this piece of code is the use of drupal_html_to_text(). It's a handy function that converts HTML to text, while maintaining things like bold type and link URLs. Since the messages that are posted to a group will usually be in HTML, and we want to include those messages in the email, it's easiest to assume that the whole email message is composed in HTML. This is something you have to take into account in the next step.
With that done, your very own Rules action should be ready to use! Activate your module and go back to the rule that you created earlier. If you click the Add action link you should now see your action, labelled "Send an email to all users of a group".
Configuring the action

You'll now see the configuration page for the new action, looking similar to the screen shot above. The first thing that we have to think about is how to deal with the Group field. Of course we can't enter a static value here, since each group will have a unique ID. The solution is to use the text tokens provided by the Token module. If you have Token installed and enabled, you'll see an expandable field set which contains all the available tokens. The one that we're interested in here is [node:og-id], which will be replaced by the ID of the group that the posted node belongs to when the action runs.
Similarly, we want the subject line to contain some information about what has been posted. The following example contains both the name of the group and the post's title: MySite [node:ogname-raw]: [node:title-raw].
Finally, you can now compose the message text that you want your users to receive. As you can see in the screen shot, we used a number of different tokens to compose a rather extensive message, containing the posted content itself as well as a number of convenient links. As we noted above, all of the message is composed in HTML, which eliminates any possible conflicts with the format of the actual node content, although everything will be recoded into plain text before the mail is sent.
The additional tokens that we used for the message text are:
- [node:site-url]
- [node:title]
- [node:author-name]
- [node:ogname]
- [node:nid]
- [node:ogalias]
- [node:og-id]
- [node:field_mybodyfield-formatted] (shown as [node:field_sff_group_discussion_body-formatted] in the screen shot)
That last one is probably the one that seems a little odd. As I mentioned earlier, there's no token for the standard node body field, nor for the total contents of the node. You have to either use a custom CCK field for the body text, which is what we did, or implement a custom token. More information is available in this issue. Another option is of course to not include the node body at all and rely on the user to follow a link to the actual node page.
Testing
Once you're done with this configuration screen, you should be all set! Time for some testing.
Before you begin testing and re-testing your configuration, do yourself a favor and install the Devel module. On its settings page, you can specify that all mail going out from your site should end up in the site log rather than actually being sent, which is great for testing.
Homework
Before class is dismissed, here's some homework to finish on your own:
- The second rule. Remember, the rule above only deals with new posts. You probably want a second one for when comments are posted.
- User configurable notifications. It's certainly a good idea to allow your users to switch email notifications on and off. We've implemented this for our client by adding a hidden user profile field which is presented to the user as an on/off checkbox. Then, we added a couple of lines of code in
mymodule_action_mail_to_users_of_group()which loads each user and checks the value of this field before sending the notification. - Turn it into a feature. Rules are exportable, which means you should be able to export your rules as a module with Features. And why not bundle it up with your custom Rules action? That way you'll have everything defined in code, all in one place.
- Teacher's pets: Make Group ID a proper Rules argument. If you feel that this walkthrough has been a little below your skill level, why not help improve it with some finishing touches? Remember the group ID field? Wouldn't it be neat if that could be replaced with a Group post argument selector which lets you choose Created content, just like we did in the condition configuration? It'll be a piece of cake for a whiz-kid like you!
Good luck!


Comments
Thanks for this very useful
Thanks for this very useful tutorial !
But I have a problem :
Must I create a new module with a Mymodule.rules.inc inside or must I put the Mymodule.rules.inc file in the subfolder modules/rules/modules/ ?
I have merge all parts of code descirbed in this tutorial in the Mymodule.rules.inc but I can't active this in the Module adaministration page.
Can you help me ?
I have created a new folder
I have created a new folder in the main module folder. In this folder I put two files : a .info file and a Mymodule.module file. After that, I can active the module and I see the new action "Send a mail to the members of a group". But when I use this, the mail send is BLANK ! (though I fill the subject and body mail field). Do you know why ?
Yes, you need a complete
Yes, you need a complete module directory with the following files:
mymodule/mymodule.info
mymodule/mymodule.module
mymodule/mymodule.rules.inc
I didn't write this specifically, but I suspect you have to put the hook_mail() implementation (the last code snippet) in mymodule.module. The wierd thing is, mine is actually in the .rules.inc file and it still works!
Also, make sure you've replaced 'mymodule' everywhere with the name of your actual module.
thanks but I have the same
thanks but I have the same problem !
Resume :
I have created 3 files in a "ogrules" folder putting in the main module folder (and after I have activated the module) :
1. ogrules.info :
; $Id$
name = Organic Group Rules Mail
description = Send a mail to members of the group.
core = 6.x
2. ogrules.module :
<?php
/**
* Implementation of hook_rules_action_info().
*/
function ogrules_rules_action_info() {
return array(
'ogrules_action_mail_to_users_of_group' => array(
'label' => t('Send an email to all users of a group'),
'module' => 'ogrules',
'eval input' => array('subject', 'message', 'from', 'group'),
),
);
}
3. ogrules.rules.inc :
<?php
/**
* Action "Send a mail to members of a group" configuration form.
*/
function ogrules_action_mail_to_users_of_group_form($settings = array(), &$form) {
$form['settings']['group'] = array(
'#type' => 'textfield',
'#size' => 10,
'#title' => t('Group ID'),
'#prefix' => t('WARNING: This may cause problems if there are too many members in this group, as your server may not be able to handle all the mail requests all at once.'),
'#required' => TRUE,
'#default_value' => isset($settings['group']) ? $settings['group'] : array(),
'#description' => t('Supply the node ID (nid) of the target group.'),
);
// We rely on the Mail to user action to build the rest of the form fields.
rules_action_mail_to_user_form($settings, $form);
}
/**
* Action: Send mail to all users of a specific group.
*/
function ogrules_action_mail_to_users_of_group($settings) {
$from = ($settings['from']) ? str_replace(array("\r", "\n"), '', $settings['from']) : NULL;
if ($group = node_load($settings['group'])) {
// Query the database for all members of this group.
$sql = og_list_users_sql();
$res = db_query($sql, $group->nid);
// Send the messages.
$message = array('result' => TRUE);
while ($member = db_fetch_object($res)) {
$message = drupal_mail('sff_groups', 'action_mail_to_users_of_group', $member->mail, language_default(), $settings, $from);
}
if ($message['result']) {
watchdog('rules', 'Successfully sent email to the members of %group.', array('%group' => $group->title));
}
}
}
/**
* Implementation of hook_mail().
*
* Sets the message subject and body as configured in the $settings of the action.
*/
function ogrules_mail($key, &$message, $settings) {
$message['subject'] .= str_replace(array("\r", "\n"), '', $settings['subject']);
// Process the mail body.
$message['body'][] = drupal_html_to_text($settings['message']);
}
The mail is send but with blank subject and body message. What is wrong ? thanks for your help !
My bad, I left some project
My bad, I left some project specific names in there which you need to change.
In your ogrules_action_mail_to_users_of_group(), you need to change the drupal_mail() call to:
$message = drupal_mail('ogrules', 'action_mail_to_users_of_group', $member->mail, language_default(), $settings, $from);
I'll update the original post to reflect that.
Thanks ! It works ! Another
Thanks ! It works !
Another question :
Is it possible to use the Mimemail module with your code to send an html mail ?
That should work, but you may
That should work, but you may want to remove the call to drupal_html_to_text() so the message remains in HTML.
Finally, I have used HTMLMAIL
Finally, I have used HTMLMAIL module and it works ! Yes I have removed the drupal_html_to_text().
Your tutorial is very useful for me ! I can create a really complex system of notifications without Messagin and notifications modules !!
Specific Role within OGs
This is very helpful. Thank You.
Is there a way to send notify a specific user role within a group when a content is posted to that group?
You'll probably have to
You'll probably have to customize the action to fit your needs. The easiest way would be to just hard-code the role check in the action code, but you could also create a new setting for this, like "Only send to these roles".
I have the same question. But
I have the same question. But I don't find the solution : How send the notification only to a specific role of users in the group ?
I don't understand hat action I must create to use that with your module.
Post to FB style status instead?
What if a user is a member of OG group A, any time a new post or change is made in that Group by the group owner, can we post to the fb status field instead of sending an email..
so when you log in, and view your stream, you could see something like Company a added widget B .. link to post?
Sure. The same mechanism
Sure. The same mechanism could be used for that scenario. I'd take a look at the Facebook related modules that are available and see if they already have Rules actions that you can use. If not, they might have an API that you can use in place of
drupal_mail()in a custom action like the one above.Hi very good work, need this
Hi very good work, need this to,
but what could be the cause, that I can't see the
$form['settings']['group'] = array(
in rules?
Threre is no text field in my rules.
What the secret to register a form?
is the name of the module important? mymodule.rules.module?
got the a snippet for members
got the a snippet for members of group by role:
$rid= 3;
$gid = 2;
$sql = "SELECT users.uid AS uid
FROM {users} users
INNER JOIN {users_roles} users_roles ON users.uid = users_roles.uid
LEFT JOIN {og_uid} og_uid ON users.uid = og_uid.uid
WHERE (users_roles.rid = %d) AND (og_uid.nid = %d)";
$result = db_query(($sql),$rid,$gid);
while ($members = db_fetch_array($result)) {
print_r ($members);
}
How to connect this to your query ?
It is important that you
It is important that you replace "mymodule" with whatever name you choose everywhere in the code, and also that you name your files accordingly. The
_form()function should then automatically get picked up by Rules.If you want to modify or
If you want to modify or replace the query used to fetch the users of a group in the example above, then these are the lines of code you should look at.
<?php$sql = og_list_users_sql();
$res = db_query($sql, $group->nid);
?>
Hi, what could be the cause,
Hi, what could be the cause, that there is no gid transmitted? I get in settings "group" only "dummy value"? The nod is saved in the right way to the group, but during rules action the gid seems not to be there?
I come back to your tutorial
I come back to your tutorial because I don't find help in the Rules Issues :
Is it possible to except the author to the send of the email ? In other words, I want to send an email to everybody except the author of the new published node ?
Can you help me ?
Logged-in user
The author of the published node will usually be the currently logged-in user. So a quick-and-dirty solution would be to wrap the mail sending code in something like this:
<?php
if ($member->uid =! $GLOBALS['user']->uid) {
...
}
?>
Hello, thanks for this very
Hello, thanks for this very good description for Rules based notifications for Organic Groups. This is excacty the thing I miss in rules and og. (I want to notificate group admins when editors publish content in order to create a suitable workflow). After following your instructions I am able to add the action "Send an email to all users of a group", but after that the correct configuration page is missing, like in your screenshot(my screenshot). I'm working with Rules 6.x-1.3. Do you have any ideas to solve this???
thank you very much and sorry for my bad english
peter
Not sure
Hi Peter!
Sorry, I'm not sure what to tell you, other than to make sure that all the above code is properly copied to your module files. Also make sure you didn't miss to replace any "mymodule" strings in the code.
/Hannes
Thanks!
WOW! This totally removes the need for subscriptions/notifications ....a clunky system for the end user that I cannot stand.
Thank you 1,000 times over for these detailed instructions, it worked a treat on my site.
I only wish I could figure out how to limit the emails sent to those of a particular role within the group. I have tried 6 ways from Sunday to no avail. Rules allows you to isolate to a user, but the users referenced can only be "acting" or "referenced" neither of which fit.
I am sure there is a query I can run from the custom module itself, and I have tried doing so but I suppose my php skills are not as great as I thought :)
Thanks again for a great custom module!!
Thank you!
Thanks Samantha for those kind words!
With this approach, Rules doesn't know about the individual users that are being sent the notifications, so the only way of achieving what you want is by altering the action itself. If you inspect the variables $group and $member in mymodule_action_mail_to_users_of_group(), you'll hopefully find the information you need to limit the emails by group role.
Good luck!
/Hannes
After Hannes Help I got roles to work!
Hannes reminded me the query was not looking specifically at each user within the group. I ended up duplicating the "og_list_users_sql" function (navigable in og.module), placing that duplicate function in to my custom module ".inc" file, pointing the "$sql=" statement to my new custom function and in the function itself, joining the users to the roles table. Then, I had the SQL statement look for my role id. So glad I had to get my hands dirty - it was quite educational. Thanks again, Hannes!!
Thanks
Thank you for this great tutorial. It helped me a lot.
Mails to Og groups admin when the user is post something
I want to create rule to sent the mails to OG group admins. When the one content field field changed.
Thank you
Hi Peter, I was having the
Hi Peter,
I was having the same issue and I think it's because I had the function that creates the form named wrong. It looks like it should be whatever your function defined in the hook_rules_action_info() is plus "_form"
For instance, I have my function defined as 'rules_og_admin_notify_action_mail_to_admins_of_group'
So, the function name for the second block of code would be 'rules_og_admin_notify_action_mail_to_admins_of_group_form'
James
Thanks
Thank you for this great tutorial. It helped me a lot.
drupal 7?
how is this different for drupal 7? I get stuck right at the top as rules is different, so I guess the code is also going to be different? or not? Would be great to have this functionality on my community education site...
Hi, what could be th
Hi, what could be the cause, that there is no gid transmitted? I get in settings "group" only "dummy value"? The nod is saved in the right way to the group, but during rules action the gid seems not to be there?
_________
Name: starlas eymour
company:http://www.plaque-cuisson.net/
I'm confused because I'm not
I'm confused because I'm not an expert at making module. I name all the files as you said but the module isn't showing up. Can you please specifically tell me the name of the file that each of the codes go to? I've never created my own module before.
Please see my comment above,
Please see my comment above, and also Drupal's handbook pages. Basically all the code above goes into the .rules.inc file.
I try this code but didn't work i cant send email
hello i try this code but i cant send the email can some on send me the three files again so i can upload these three files
please help me i really need it urgent
Thanks in advance
thank you
this article has really spared me some trouble. trying to make the notifications / messaging modules do the custom things i needed to do was killing me. this has allowed me the flexibility. so thank you for the ideas - and for the code.
Got an error
Hi All,
I'm currently working og in my site(drupal 7). And yes, I need to create rules notifications based on og global roles...
I followed the steps above however I got this error.
You guys have any idea?
Thanks.
sorry this is the error I
sorry this is the error I got:
Fatal error: Call to undefined function rules_action_mail_to_user_form()
PHP Error Help
Hi, a little late to the game, but trying to implement your module after pulling teeth on OG node comments.
This is for a D6.2x site running (among too many others):
- OG 6.x-2.2
- Token 6.x-1.18
- CiviCRM 3.3
- Messaging module set to use PHPMailer
I tried moving the hook_mail() implementation from the .inc to .module file (called ognotifier.module).
When I add a comment, the rule is firing but token doesn't seem to like your nifty array and I get the following error.
"The messaging_text_replace() function called token replacement with an array rather than a string for $text in /home/"mysite"/www/www/sites/all/modules/token/token.module on line 263.
Since your post is almost 2 years old, perhaps updates to the token module are the problem? Line 263 is a piece of code to "Ensure that the $text parameter is a string and not an array which is an invalid input."
Any ideas? Thanks in advance for paying it forward!
@Claire: The code will
@Claire: The code will probably need a rewrite for D7. Unfortunately I haven't worked on any of this in D7 so I can't help you.
@Ivan: This recipe is supposed to work without Messaging. The error message suggests that Messaging is part of the problem, so I would try disabling that first. If that doesn't work, there may be some API change in one of the other modules that messed things up.
Great post. I have been
Great post. I have been struggling to create a rule that , when an Ubercart product (an Organic Group 'membership' product that allocates a role to the purchasing customer) changes order status (action) to 'Complete' (condition), AND on condition the ordered product(s) contain a product of type 'my-group-membership' - here I get order:products:0:node:type to order:products:3:node:type and believe this refers to first 4 products ordered in which case what if my membership product is number 5 in the cart? Then join order:customer to the group related to the ordered product. It all looks like it should work, but doesn't. So I wonder if I need to create my own rules module as above? I am new to php and its been a long time since I wrote any sql, but this seems as if it ought to be easy... Any help or advice would be much appreciated. :o)
In 7.2. Rule's version, you
In 7.2. Rule's version, you can send an email to all members of a group with selectors. (there is an example of rule when you update the OG module)
Thanks for this Hannes but I
Thanks for this Hannes but I'm having the same issue as Claire, which you said is because you wrote this for Drupal 6, so it won't work on D7. Have you looked into this at all, or the suggestion made by "friend of a friend" about using selectors?
I have not looked into
I have not looked into implementing this in Drupal 7 unfortunately. I would look into the example rule in OG that 'friend of a friend' talks about above.