With message_notify being an API type module (and the module name not specifying email), I think it would be more useful to make it more general, instead of having it email specific (in which case it might be better called message_email).

What I was thinking was a message type category of message_type_notification instead of message_type_email.

Then instead of having a message_type_email bundle (in the .install file) you could have a message_type_notification bundle and in addition to the message_text_subject field it would have a message_short_msg field, which has a short version of the message (this field probably doesn't have to be required).
This short version can be used for other message types.

message_notify_send_mail() remains as is (it could potentially be in a sub module in case users didn't want email notifications).

There would also be additional functions along the same lines as message_notify_send_mail() that send messages to twitter, facebook, sms etc. These could also go into sub modules (or different add-on projects if preferred), so users are only enabling what they need.

If the module worked in this way then you could have one message entity, that could be sent out to different users via different methods depending on user preference, or sent out to the same user via multiple methods.

Currently if you wanted to do things like that you will have to create different message types for each medium and then create a different message to send out to each.

If you don't want to do that with this module that is fine, however I think if I was to make a module along these lines it will be duplicating code from this module and adding more functionality on top, which is not a great situation.
Your module also has the desirable module name for such a module.

Comments

rooby’s picture

I'm happy to provide a patch to illustrate what I mean, as I will be doing it for a project anyway.

amitaibu’s picture

Yes, I'm ok with adding support to other notification systems. Infact the module is called Message notify, and not Message email -- to indicate it's not hard-coded with emails.

The reason we have a message category called email, is that unlike SMS, it needs a subject field.

Anyway, I'm waiting for your patches :)

rooby’s picture

Status: Active » Needs review
StatusFileSize
new11.09 KB

This is the kind of direction I was thinking.

The main differences being that the notification message type has an extra short msg field and the send mail function is now a send message function which uses ctools plugins for notifiers.

I have only added the email notifier, but for example a twitter notifier might compose it's message from the short msg field (using the body text if the short msg is empty - because the body field is required and a twitter only msg wouldn't have a long version). Then it would send out via the twitter module's api or something.

The part that is a bit messy is the message type with the extra field.
On one hand it allows you to cover pretty much any use case with the notification type.
For example, if you were to add to the users account a setting where they can choose their preferred notification method and you have am admin page for sending out messages. You want the admin to be able to go to the message page and write one message, which is then sent out to all the users. Having the separate fields allows for this. Alternately, the admin would have to complete one form for long form messages and one for short messages.

But then for people who only want email notifications you have an extra field getting in the way.
And to account for people who only want to use short messages you would want the body text not required, and then it is there unnecessarily.
Or as you mentioned before, in that case you don't really need the extra small msg field because the body text would become the small message text.

I don't really like that aspect of the patch.

Maybe a better solution is to have the main message_notify module have message_notify_send_message() and the ctools plugins like in the patch (or something similar), but then have the email plugin living in a sub module, and the creation of the message_type_email bundle would be done in the sub module. The message_notify module itself wouldn't make any message type categories or bundles, it would just be the send message related functions.

Then the message_type_notification bundle as I have done in the patch could be a separate module of it's own for it's own special use case.

Ideas, comments, criticism welcome.

I will probably do another patch with the email notifier split out to a sub module that provides the message_type_email as it is in the current version of the module and the email notifier as in this patch. - Doing away with the message_type_notification that is in this patch - it can live elsewhere.

rooby’s picture

Damn that patch is incomplete.
I'm working on a revised version at the moment anyway, after further thought.
I'll post it soon.

rooby’s picture

Status: Needs review » Needs work
rooby’s picture

Status: Needs work » Needs review
StatusFileSize
new9.88 KB

OK, here is a version I like much more.

message_notify is just an API type module and has a message sending function that can be used by any notifier. It handles saving messages & error reporting etc.
CTools plugins are used for notifiers.

There is a new sub-module message_notify_email, which adds the message_type_email message type as in the current module. It also adds the email notifier plugin.

This solution makes the send message function a bit more complex but also more generic.

The only downside in having the send message function do all the message field saving is that it is hard to account for unusuall fields like node reference fields, which have an nid instead of a value.

I was tempted to abandon the idea due to that and just stick with completely separate modules for each notifier that are completely separate from message_notify, although I really want to avoid the duplicate code that would come as a result.

So it also has a (currently undocumented) callback that the notifier plugins can declare to handle any rendered values for unusual fields like that.

I have tested using the message notify example module and it is working.
That module would probably be called message_notify_email_example instead.

The plugin system would need to be properly documented so others could make their own more easily.

Let me know if you like the idea, have some changes for the idea or hate the idea. - Or any other comments/criticisms.

Uploading the whole module instead of a patch.

amitaibu’s picture

Can you please attach as a patch, it's easier to review.

rooby’s picture

StatusFileSize
new15.26 KB

Here you go.

amitaibu’s picture

Status: Needs review » Needs work

Thank you for working on this.

+++ b/message_notify.moduleundefined
@@ -2,45 +2,89 @@
+function message_notify_send_message(Message $message, $notifier_name = 'email', $options = array()) {

I don't think we need to provide a default notifier name.

+++ b/message_notify.moduleundefined
@@ -2,45 +2,89 @@
+  // Defaults can be overridden by notifiers and directly via the function
+  // parameter.

Use array_merge() here. The $options should override the notifier default options.

+++ b/message_notify.moduleundefined
@@ -2,45 +2,89 @@
+  // Allow the notifier to do some custom field rendering if they have any

Do you have a use case for this?

+++ b/message_notify.moduleundefined
@@ -52,37 +96,43 @@ function message_notify_send_mail(Message $message, $options = array()) {
+  static $notifiers;
+  // Get all the notifiers.

ctools_get_plugins() is already cached, so no need for extra static.

+++ b/message_notify.moduleundefined
@@ -52,37 +96,43 @@ function message_notify_send_mail(Message $message, $options = array()) {
+/** ¶

Trailing space.

+++ b/message_notify.moduleundefined
@@ -52,37 +96,43 @@ function message_notify_send_mail(Message $message, $options = array()) {
+ * Implementation of hook_ctools_plugin_api().

In D7 we use "Implements", instead of "Implementation of"

+++ b/message_notify_email/message_notify_email.moduleundefined
@@ -0,0 +1,62 @@
+function message_notify_email_ctools_plugin_api($module, $api) {

Do we need this here?

+++ b/message_notify_email/message_notify_email.moduleundefined
@@ -0,0 +1,62 @@
+  if ($module == 'message_notify' && !empty($plugin)) {

I don't think $plugin is ever empty.

+++ b/message_notify_email/plugins/notifier/email.incundefined
@@ -0,0 +1,35 @@
+$plugin = array(
+  'title' => t("Email"),
+  'description' => t('Send notification messages via email'),
+  'send_callback' => 'message_notify_email_send',
+  'default_options' => array(
+    'rendered fields' => array(
+      'rendered_subject' => 'message_text_subject',
+      'rendered_body' => MESSAGE_FIELD_MESSAGE_TEXT,
+    ),
+  ),

Let's use classes here (you can look at entityreference module, to see how we use classes). So we won't need to do things like function_exists() when calling a method.

rooby’s picture

Thanks for the feedback, I'll make the changes probably later today, but for a couple of your notes:

+++ b/message_notify.moduleundefined
@@ -2,45 +2,89 @@
+  // Allow the notifier to do some custom field rendering if they have any

Do you have a use case for this?

Not really, I added that bit in at the last minute.
The reason I added it is because I haven't had a chance to fully learn about entity wrappers and for some reason I figured (without investigating) that it would default to trying to save data to the 'value' column, which I thought would break for fields like node references, with their 'nid' column.

Now I realise this isn't the case, so the code below should be able to handle any type of field right?

+++ b/message_notify.module
@@ -2,45 +2,89 @@
+          if (!empty($wrapper->type->{$rendered_fields[$rendered_name]}->format)) {
+            $format = $wrapper->type->{$rendered_fields[$rendered_name]}->format->value();
+            $wrapper->{$field_name}->set(array('value' => $message->$rendered_name, 'format' => $format));
+          }
+          else {
+            $wrapper->{$field_name}->set($message->$rendered_name);
+          }

So I can do away with the extra callback.

Let's use classes here (you can look at entityreference module, to see how we use classes). So we won't need to do things like function_exists() when calling a method.

So create a notifier handler class with a send function and then the email notifier handler class implements that?
That sounds cleaner.

amitaibu’s picture

> Now I realise this isn't the case, so the code below should be able to handle any type of field right?

Haven't tried with non-text fields, but I assume it should work :)

> So create a notifier handler class with a send function and then the email notifier handler class implements that?

Yes, as I suspect we will want to extend it (e.g. add also settingsForm() so notifiers can add their settings to the Message type form).

rooby’s picture

It's super busy at the moment so it'll probably be a few days before I get back to this. Not too long though.

amitaibu’s picture

fyi, This feature -- #1433978: Add "partials", to allow rendering only part of a message might help when needing multiple fields. (I'll soon roll a new Message release).

rooby’s picture

Thanks, looks interesting. I'll have to have a play with that.
Also, I will be able to finish this pretty soon.

amitaibu’s picture

@rooby, and plans to get back to this? Seems there is some related interest in http://groups.drupal.org/node/222599

rooby’s picture

Yeah, sorry about that.

I'll update the patch as soon as possible this week.

rooby’s picture

Partly done, hopefully I will have something for you tomorrow.

amitaibu’s picture

great :)

amitaibu’s picture

Partly done, hopefully I will have something for you tomorrow.

? :)

crazyrohila’s picture

StatusFileSize
new21.56 KB

Started some work (didn't test it as working or not) . Just cleared some issues in rooby's patch pointed by amitaibu .

crazyrohila’s picture

Status: Needs work » Needs review
StatusFileSize
new19.28 KB

Here is a working patch with class implementation. I test it with example module. Still learning about ctools plugin sytem. I found a nice article here.

Suggestions, Idea criticism ? :)

rooby’s picture

Status: Needs review » Needs work

Thank you so much for working on this.

I just went back to find the work I mentioned in #17 and can't find it anywhere. Damn annoying.

Some review of your latest patch:
Amitaibu, feel free to correct me on any of this. Particularly the class part as I am least familiar with that aspect.

+++ b/message_notify.module
@@ -2,66 +2,89 @@
   // Set default values.
-  $options += array(
+  // Defaults can be overridden by notifiers and directly via the function
+  // parameter.
+  $options += $notifier['default_options'] += array(
     'save on fail' => TRUE,
     'save on success' => TRUE,
-    'rendered subject field' => FALSE,
-    'rendered body field' => FALSE,
-    // Override email address.
-    'mail' => FALSE,
-    // Override user's language, and use Message language.
-    'language override' => FALSE
+    'rendered fields' => array(
+      'rendered_body' => MESSAGE_FIELD_MESSAGE_TEXT,
+    ),
+    'save rendered fields' => array(),
   );

This part should be replaced with PHP's array_merge_recursive() or maybe even just array_merge() so that the hard coded array valudes are overridden by $notifier['default_options'], which are overridden again by $options.

+++ b/message_notify.module
@@ -2,66 +2,89 @@
+  // Allow the notifier to do some custom field rendering if they have any
+  // special requirements.
+  if (!empty($notifier['render_callback']) && function_exists($notifier['render_callback'])) {
+    $notifier['render_callback']($wrapper, $message, $account);
+  }

This part can be removed. There isn't really any use case for it.

+++ b/message_notify_email/message_notify_email.module
@@ -0,0 +1,62 @@
+/**
+ * Implementation of hook_ctools_plugin_api().
+ */
+function message_notify_email_ctools_plugin_api($module, $api) {
+  if ($module == 'message_notify' && $api == 'notifier') {
+    return array('version' => 1);
+  }
+}

This is not required.

+++ b/message_notify_email/message_notify_email.module
@@ -0,0 +1,62 @@
+/**
+ * Implementation of hook_ctools_plugin_dierctory().
+ *
+ * Used to let the system know we implement plugins.
+ */
+function message_notify_email_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'message_notify' && !empty($plugin)) {
+    return 'plugins/' . $plugin;
+  }
+}

&& !empty($plugin) is not required because $plugin will not be empty.

+++ b/message_notify_email/plugins/notifier/MessageNotifier.class.php
@@ -0,0 +1,19 @@
+class MessageNotifier {

Now that a class system is being used, maybe message_notify should implement an abstract notifier class with all the required functions at this stage maybe just sendMessage and settingsForm are required.

Then MessageNotifier can extend the abstract class and provide its implementation of sendMessage but doesn't have to implement settingsForm if it doesn't need to.

There is some examples of this in the entityreference module. There is also some decent help in ctools via the advanced help module and

+++ b/message_notify_email/plugins/notifier/MessageNotifier.class.php
@@ -0,0 +1,19 @@
+  function message_notify_email_send(Message $message, $account) {

This can be called something more generic like sendMessage as the same function needs to be used for all notifier classes.

+++ b/message_notify.module
@@ -2,66 +2,89 @@
+  // Send out via the requested notification method.
+  if (!empty($notifier['send_callback']) && function_exists($notifier['send_callback'])) {
+    $result = $notifier['send_callback']($message, $account);
   }

Now that the plugin is going to be a class this part needs to be replaced.
There is no longer need for function_exists etc.
You can load the notifier class using ctools_plugin_load_class() and then call the class's sendMessage function.

Because of the new class structure we can be sure that function will exist.


I still have to have a look into the settingsForm part to see what is actually required in that area.
This is only a visual code review at this stage. I won't have time to actually test any patches hands on or make any additions to the patch for a few days.

rooby’s picture

Damn, that review was for the patch in #20.

Having a quick look at the new one now.

rooby’s picture

A quick look through.
A number of the above comments have been addressed already.
(there are some more detailed comments in the previous review)

+++ b/message_notify.module
@@ -2,66 +2,91 @@
   // Set default values.
-  $options += array(
+  // Defaults can be overridden by notifiers and directly via the function
+  // parameter.
+  $options += $notifier['default_options'] += array(
     'save on fail' => TRUE,
     'save on success' => TRUE,
-    'rendered subject field' => FALSE,
-    'rendered body field' => FALSE,
-    // Override email address.
-    'mail' => FALSE,
-    // Override user's language, and use Message language.
-    'language override' => FALSE
+    'rendered fields' => array(
+      'rendered_body' => MESSAGE_FIELD_MESSAGE_TEXT,
+    ),
+    'save rendered fields' => array(),
   );

As above. replace with array merge.

+++ b/message_notify.module
@@ -2,66 +2,91 @@
+  // Allow the notifier to do some custom field rendering if they have any
+  // special requirements.
+  if (!empty($notifier['render_callback']) && function_exists($notifier['render_callback'])) {
+    $notifier['render_callback']($wrapper, $message, $account);
+  }

As above, this can be removed.

+++ b/message_notify.module
@@ -2,66 +2,91 @@
+  if (!empty($notifier['handler'])) {
+    $result = $mailnotifier->message_notify_send_email($message, $account);
   }

message_notify_send_email should be called something more generic as it will be used for non-email notifiers as well.

+++ b/message_notify_email/plugins/notifier/email.inc
@@ -0,0 +1,41 @@
+class EmailNotifier {

As above, it would be good to have an abstract class for notifiers defining all the available functions and then extend it here.

+++ b/message_notify_email/plugins/notifier/email.inc
@@ -0,0 +1,41 @@
+public function message_notify_send_email(Message $message, $account) {

Needs a more generic function name.

amitaibu’s picture

thanks to the both of you! here's my review:

+++ b/message_notify.moduleundefined
@@ -2,66 +2,91 @@
+  if (!$notifier) {
+    watchdog('message_notify', t('Could not send notification using the "@notifier" notifier to user ID @uid because the notifier could not be found.'), array('@notifier' => $notifier_name, '@uid' => $message->uid), WATCHDOG_ERROR);
+    return FALSE;

Let's be more dramatic and throw an Exception here -- you should add a new MessageNotifyException class.

+++ b/message_notify.moduleundefined
@@ -2,66 +2,91 @@
-    // Override user's language, and use Message language.
-    'language override' => FALSE

Language probably needs to stay here, as it's not notifier specific.

+++ b/message_notify.moduleundefined
@@ -2,66 +2,91 @@
+    // Rendered field saving. Fields must be present in both arrays.
+    if (!empty($options['save rendered fields']) && is_array($options['save rendered fields'])) {
+      $save_fields = array_filter($options['save rendered fields']);
+      // We can't save fields that aren't in the rendered fields array.
+      $save_fields = array_intersect_key($save_fields, $rendered_fields);
+      if (!empty($save_fields)) {
+        $wrapper = entity_metadata_wrapper('message', $message);
+        foreach ($save_fields as $rendered_name => $field_name) {
+          // Get the format from the field.
+          if (!empty($wrapper->type->{$rendered_fields[$rendered_name]}->format)) {
+            $format = $wrapper->type->{$rendered_fields[$rendered_name]}->format->value();
+            $wrapper->{$field_name}->set(array('value' => $message->$rendered_name, 'format' => $format));
+          }
+          else {
+            $wrapper->{$field_name}->set($message->$rendered_name);
+          }
+        }

I think we should probably move this into the Notifier plugin -- as for example in the emailNotifier we have 2 fields we need to save. postSend() function maybe?

+++ b/message_notify.moduleundefined
@@ -2,66 +2,91 @@
+  // Allow the notifier to do some custom field rendering if they have any
+  // special requirements.
+  if (!empty($notifier['render_callback']) && function_exists($notifier['render_callback'])) {
+    $notifier['render_callback']($wrapper, $message, $account);

Yeah, as rooby said -- lets remove.

+++ b/message_notify.moduleundefined
@@ -2,66 +2,91 @@
+    watchdog('message_notify', t('Could not send notification using the "@notifier" notifier to user ID @uid because the notifer has no send callback.'), array('@notifier' => $notifier_name, '@uid' => $message->uid), WATCHDOG_ERROR);
+    return FALSE;

No silent errors -- if a plugin is incorrect throw Excpetion.

+++ b/message_notify.moduleundefined
@@ -73,37 +98,43 @@ function message_notify_send_mail(Message $message, $options = array()) {
+  $notifiers;
+  // Get all the notifiers.
+  if (empty($notifiers)) {
+    $notifiers = ctools_get_plugins('message_notify', 'notifier');

? :)

Probably meant to do some caching, but ctools_get_plugins() is already cached. Anyway, I don't think we really need this wrapper function.

+++ b/message_notify.moduleundefined
@@ -73,37 +98,43 @@ function message_notify_send_mail(Message $message, $options = array()) {
+function message_notify_ctools_plugin_api($module, $api) {

I think it's actually needed for CTools, not sure. I'm ok with keeping it, for versioning in case one day we will need it.

+++ /dev/nullundefined
@@ -1,104 +0,0 @@
-/**
- * @file
- * Rules integration for the Message notify module.
- *
- * @addtogroup rules
- * @{
- */
-
-/**

Please don't kill the Rules integration ;)

+++ b/message_notify_email/plugins/notifier/email.incundefined
@@ -0,0 +1,41 @@
+  'default_options' => array(

Rename key to "defaults"

+++ b/message_notify_email/plugins/notifier/email.incundefined
@@ -0,0 +1,41 @@
+class EmailNotifier {

This should extend the plugin that *should* exist in message_notify core called MessageNotifier

+++ b/message_notify_email/plugins/notifier/email.incundefined
@@ -0,0 +1,41 @@
+public function message_notify_send_email(Message $message, $account) {

And the function it will override should be called public function send()

It shouldn't get any argument, as the $message and $account should be in $this argument (Instead move those variables to a __constructor function in MessaegeNotifier)

+++ b/message_notify_email/plugins/notifier/email.incundefined
@@ -0,0 +1,41 @@
+  $result = drupal_mail('message_notify_email', $message->type, $account->mail, $lang, $params);

Return the $result.

+++ b/message_notify_example/message_notify_example.infoundefined
@@ -1,17 +1,19 @@
+version = 7.x-1.x-dev

No need for version.

rooby’s picture

+++ b/message_notify.moduleundefined
@@ -73,37 +98,43 @@ function message_notify_send_mail(Message $message, $options = array()) {
+function message_notify_ctools_plugin_api($module, $api) {

I think it's actually needed for CTools, not sure. I'm ok with keeping it, for versioning in case one day we will need it.

Yeah, I think in my patch it was in message_notify & message_notify_email.

It should be in message_notify but doesn't need to be in message_notify_email.

crazyrohila’s picture

Thanks for review. I will update patch soon (may be tomorrow) :)

crazyrohila’s picture

Status: Needs work » Needs review
StatusFileSize
new17.28 KB

I have not done Exceptions stuff yet . I will do that soon. Now Posting patch to review that DIR structure is okay or not ? Are the classes implementations right ?

amitaibu’s picture

Status: Needs review » Needs work

Haven't gone over all code, just few remarks:

+++ b/message_notify.moduleundefined
@@ -2,66 +2,87 @@
+    'language override' => FALSE,
+    'rendered fields' => array(
+      'rendered_body' => MESSAGE_FIELD_MESSAGE_TEXT,
+    ),

We can actually move these defaults to the plugin defaults. See entity-reference as example.

+++ b/message_notify.moduleundefined
@@ -2,66 +2,87 @@
+    if (!empty($options['save rendered fields']) && is_array($options['save rendered fields'])) {

This should be moved to the class under postSend()

Also:
* change class name to notifier => MessageNotifier and place it under plugins. Please have a close look in Entity reference's plugin strcture -- you should copy from there.
* Please don't place for review code that is commented out. Just delete if not necessary.
* Please read previous review --

public function message_notify_send()

=> public function send()

amitaibu’s picture

I had an idea as a result of #1690520: Displaying message text using view modes, that will make it even more generic.
Instead of using more fields (e.g. for the subjects), we should use View modes, as it allows creating a single message that can be delivered in several methods.

Example - Create node | Site-Install.jpg

I've started to code. This patch is completely broken and untested, but I think it's a good start. Working is done in 1439524 branch.

What do you think?

amitaibu’s picture

StatusFileSize
new12.9 KB

This patch can already send emails. Depends on #1690520: Displaying message text using view modes

amitaibu’s picture

@crazyrohila,
any chance for getting tests from you? :)

If so please grab branch 1439524, as its code is a bit more updated.

rooby’s picture

I will test ASAP (within the next day or so).

Sounds like a good idea though.

crazyrohila’s picture

Sorry for late reply. I gave my laptop to repair center, So I was offline last week.

@amitaibu I think above patch not commited in 1439524 branch. BTW I will apply patch and will test.

amitaibu’s picture

Sorry, I will re-push all commits to 1439524 in the evening

crazyrohila’s picture

StatusFileSize
new44.9 KB
new70.31 KB

Installed module with patch. Changed function name in example module and working fine. Email format is messed up .

email-format.png

I created a patch for branch-1439524. It will make your work easy.

amitaibu’s picture

Yeah, I fixed the email subject, but forgot to push. It will happen later on today.

amitaibu’s picture

I've re-pushed the branch, so you can delete the old one and re-track it again

git branch -D 1439524
git checkout -t origin/1439524
amitaibu’s picture

@crazyrohila,
any chance for getting tests from you? :)

btw, Since the Notifier is pluggable, we can have message_notify_test module that will declare a Test Notifier.

crazyrohila’s picture

I have noticed one thing that mail is sending to wrong user (comment author). It should be send to node author .

So changed

$message = message_create('comment_insert', array('uid' => $comment->uid));

to

$message = message_create('comment_insert', array('uid' => $node->uid));

in message_notify_example.module file.

@amitaibu sure I will do.

amitaibu’s picture

Regarding #40, please open a separate issue for this with a patch.

crazyrohila’s picture

@amitaibu issue created http://drupal.org/node/1701826 .

amitaibu’s picture

I've pushed more fixes + tests. Note work depends on Message's branch 1690520

amitaibu’s picture

Status: Needs work » Needs review
StatusFileSize
new44.28 KB

I think its ready (+tests). I'll probably create a 2.x branch, as it's a complete - and much better! - overhaul.

amitaibu’s picture

StatusFileSize
new44.28 KB

Re-load patch for testbot.

amitaibu’s picture

Status: Needs review » Fixed

Issue #1439524 by Amitaibu, rooby, crazyrohila: Rewrite module to allow other delivery methods and use of view-modes.

Ran test locally, and they all pass, so we have a shinny new 7.x-2.x! :)

ezra-g’s picture

Awesome work, guys!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.