diff options
Diffstat (limited to 'engine/lib/notification.php')
| -rw-r--r-- | engine/lib/notification.php | 344 |
1 files changed, 218 insertions, 126 deletions
diff --git a/engine/lib/notification.php b/engine/lib/notification.php index ad65455f3..be0c359d4 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -3,38 +3,42 @@ * Notifications * This file contains classes and functions which allow plugins to register and send notifications. * - * There are notification methods which are provided out of the box (see notification_init() ). Each method - * is identified by a string, e.g. "email". + * There are notification methods which are provided out of the box + * (see notification_init() ). Each method is identified by a string, e.g. "email". * - * To register an event use register_notification_handler() and pass the method name and a handler function. + * To register an event use register_notification_handler() and pass the method name and a + * handler function. * - * To send a notification call notify() passing it the method you wish to use combined with a number of method - * specific addressing parameters. + * To send a notification call notify() passing it the method you wish to use combined with a + * number of method specific addressing parameters. * * Catch NotificationException to trap errors. * - * @package Elgg - * @subpackage API - - * @author Curverider Ltd - - * @link http://elgg.org/ + * @package Elgg.Core + * @subpackage Notifications */ /** Notification handlers */ +global $NOTIFICATION_HANDLERS; $NOTIFICATION_HANDLERS = array(); /** * This function registers a handler for a given notification type (eg "email") * - * @param string $method The method - * @param string $handler The handler function, in the format "handler(ElggEntity $from, ElggUser $to, $subject, $message, array $params = NULL)". This function should return false on failure, and true/a tracking message ID on success. - * @param array $params A associated array of other parameters for this handler defining some properties eg. supported message length or rich text support. + * @param string $method The method + * @param string $handler The handler function, in the format + * "handler(ElggEntity $from, ElggUser $to, $subject, + * $message, array $params = NULL)". This function should + * return false on failure, and true/a tracking message ID on success. + * @param array $params An associated array of other parameters for this handler + * defining some properties eg. supported msg length or rich text support. + * + * @return bool */ function register_notification_handler($method, $handler, $params = NULL) { global $NOTIFICATION_HANDLERS; - if (is_callable($handler)) { + if (is_callable($handler, true)) { $NOTIFICATION_HANDLERS[$method] = new stdClass; $NOTIFICATION_HANDLERS[$method]->handler = $handler; @@ -51,20 +55,38 @@ function register_notification_handler($method, $handler, $params = NULL) { } /** + * This function unregisters a handler for a given notification type (eg "email") + * + * @param string $method The method + * + * @return void + * @since 1.7.1 + */ +function unregister_notification_handler($method) { + global $NOTIFICATION_HANDLERS; + + if (isset($NOTIFICATION_HANDLERS[$method])) { + unset($NOTIFICATION_HANDLERS[$method]); + } +} + +/** * Notify a user via their preferences. * - * @param mixed $to Either a guid or an array of guid's to notify. - * @param int $from GUID of the sender, which may be a user, site or object. - * @param string $subject Message subject. - * @param string $message Message body. - * @param array $params Misc additional parameters specific to various methods. - * @param mixed $methods_override A string, or an array of strings specifying the delivery methods to use - or leave blank - * for delivery using the user's chosen delivery methods. + * @param mixed $to Either a guid or an array of guid's to notify. + * @param int $from GUID of the sender, which may be a user, site or object. + * @param string $subject Message subject. + * @param string $message Message body. + * @param array $params Misc additional parameters specific to various methods. + * @param mixed $methods_override A string, or an array of strings specifying the delivery + * methods to use - or leave blank for delivery using the + * user's chosen delivery methods. + * * @return array Compound array of each delivery user/delivery method's success or failure. * @throws NotificationException */ function notify_user($to, $from, $subject, $message, array $params = NULL, $methods_override = "") { - global $NOTIFICATION_HANDLERS, $CONFIG; + global $NOTIFICATION_HANDLERS; // Sanitise if (!is_array($to)) { @@ -88,12 +110,15 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth // Are we overriding delivery? $methods = $methods_override; if (!$methods) { - $tmp = (array)get_user_notification_settings($guid); + $tmp = get_user_notification_settings($guid); $methods = array(); - foreach($tmp as $k => $v) { - // Add method if method is turned on for user! - if ($v) { - $methods[] = $k; + // $tmp may be false. don't cast + if (is_object($tmp)) { + foreach ($tmp as $k => $v) { + // Add method if method is turned on for user! + if ($v) { + $methods[] = $k; + } } } } @@ -101,19 +126,25 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth if ($methods) { // Deliver foreach ($methods as $method) { + + if (!isset($NOTIFICATION_HANDLERS[$method])) { + continue; + } + // Extract method details from list $details = $NOTIFICATION_HANDLERS[$method]; $handler = $details->handler; + /* @var callable $handler */ - if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler)) { - error_log(sprintf(elgg_echo('NotificationException:NoHandlerFound'), $method)); + if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler) || (!is_callable($handler))) { + error_log(elgg_echo('NotificationException:NoHandlerFound', array($method))); } elgg_log("Sending message to $guid using $method"); // Trigger handler and retrieve result. try { - $result[$guid][$method] = $handler( + $result[$guid][$method] = call_user_func($handler, $from ? get_entity($from) : NULL, // From entity get_entity($guid), // To entity $subject, // The subject @@ -136,16 +167,22 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth * Get the notification settings for a given user. * * @param int $user_guid The user id - * @return stdClass + * + * @return stdClass|false */ function get_user_notification_settings($user_guid = 0) { $user_guid = (int)$user_guid; if ($user_guid == 0) { - $user_guid = get_loggedin_userid(); + $user_guid = elgg_get_logged_in_user_guid(); } - $all_metadata = get_metadata_for_entity($user_guid); + // @todo: there should be a better way now that metadata is cached. E.g. just query for MD names, then + // query user object directly + $all_metadata = elgg_get_metadata(array( + 'guid' => $user_guid, + 'limit' => 0 + )); if ($all_metadata) { $prefix = "notification:method:"; $return = new stdClass; @@ -168,9 +205,10 @@ function get_user_notification_settings($user_guid = 0) { /** * Set a user notification pref. * - * @param int $user_guid The user id. - * @param string $method The delivery method (eg. email) - * @param bool $value On(true) or off(false). + * @param int $user_guid The user id. + * @param string $method The delivery method (eg. email) + * @param bool $value On(true) or off(false). + * * @return bool */ function set_user_notification_setting($user_guid, $method, $value) { @@ -179,7 +217,7 @@ function set_user_notification_setting($user_guid, $method, $value) { $user = get_entity($user_guid); if (!$user) { - $user = get_loggedin_user(); + $user = elgg_get_logged_in_user_entity(); } if (($user) && ($user instanceof ElggUser)) { @@ -194,142 +232,178 @@ function set_user_notification_setting($user_guid, $method, $value) { } /** - * Notification exception. - * @author Curverider Ltd - */ -class NotificationException extends Exception {} - - -/** * Send a notification via email. * - * @param ElggEntity $from The from user/site/object - * @param ElggUser $to To which user? - * @param string $subject The subject of the message. - * @param string $message The message body - * @param array $params Optional parameters (none taken in this instance) + * @param ElggEntity $from The from user/site/object + * @param ElggUser $to To which user? + * @param string $subject The subject of the message. + * @param string $message The message body + * @param array $params Optional parameters (none taken in this instance) + * * @return bool + * @throws NotificationException + * @access private */ -function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message, array $params = NULL) { +function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message, +array $params = NULL) { + global $CONFIG; if (!$from) { - throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'from')); + $msg = elgg_echo('NotificationException:MissingParameter', array('from')); + throw new NotificationException($msg); } if (!$to) { - throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'to')); + $msg = elgg_echo('NotificationException:MissingParameter', array('to')); + throw new NotificationException($msg); } - if ($to->email=="") { - throw new NotificationException(sprintf(elgg_echo('NotificationException:NoEmailAddress'), $to->guid)); + if ($to->email == "") { + $msg = elgg_echo('NotificationException:NoEmailAddress', array($to->guid)); + throw new NotificationException($msg); } - // Sanitise subject - $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject); // Strip line endings - // To $to = $to->email; // From - $site = get_entity($CONFIG->site_guid); + $site = elgg_get_site_entity(); // If there's an email address, use it - but only if its not from a user. - if ((isset($from->email)) && (!($from instanceof ElggUser))) { + if (!($from instanceof ElggUser) && $from->email) { $from = $from->email; - } else if (($site) && (isset($site->email))) { - // Has the current site got a from email address? + } else if ($site && $site->email) { + // Use email address of current site if we cannot use sender's email $from = $site->email; - } else if (isset($from->url)) { - // If we have a url then try and use that. - $breakdown = parse_url($from->url); - $from = 'noreply@' . $breakdown['host']; // Handle anything with a url } else { // If all else fails, use the domain of the site. $from = 'noreply@' . get_site_domain($CONFIG->site_guid); } - if (is_callable('mb_internal_encoding')) { - mb_internal_encoding('UTF-8'); + return elgg_send_email($from, $to, $subject, $message); +} + +/** + * Send an email to any email address + * + * @param string $from Email address or string: "name <email>" + * @param string $to Email address or string: "name <email>" + * @param string $subject The subject of the message + * @param string $body The message body + * @param array $params Optional parameters (none used in this function) + * + * @return bool + * @throws NotificationException + * @since 1.7.2 + */ +function elgg_send_email($from, $to, $subject, $body, array $params = NULL) { + global $CONFIG; + + if (!$from) { + $msg = elgg_echo('NotificationException:MissingParameter', array('from')); + throw new NotificationException($msg); } - $site = get_entity($CONFIG->site_guid); - $sitename = $site->name; - if (is_callable('mb_encode_mimeheader')) { - $sitename = mb_encode_mimeheader($site->name,"UTF-8", "B"); + + if (!$to) { + $msg = elgg_echo('NotificationException:MissingParameter', array('to')); + throw new NotificationException($msg); + } + + // return TRUE/FALSE to stop elgg_send_email() from sending + $mail_params = array( + 'to' => $to, + 'from' => $from, + 'subject' => $subject, + 'body' => $body, + 'params' => $params + ); + + $result = elgg_trigger_plugin_hook('email', 'system', $mail_params, NULL); + if ($result !== NULL) { + return $result; } $header_eol = "\r\n"; - if ( - (isset($CONFIG->broken_mta)) && - ($CONFIG->broken_mta) - ) { + if (isset($CONFIG->broken_mta) && $CONFIG->broken_mta) { // Allow non-RFC 2822 mail headers to support some broken MTAs $header_eol = "\n"; } - $from_email = "\"$sitename\" <$from>"; - if (strtolower(substr(PHP_OS, 0 , 3)) == 'win') { - // Windows is somewhat broken, so we use a different format from header - $from_email = "$from"; + // Windows is somewhat broken, so we use just address for to and from + if (strtolower(substr(PHP_OS, 0, 3)) == 'win') { + // strip name from to and from + if (strpos($to, '<')) { + preg_match('/<(.*)>/', $to, $matches); + $to = $matches[1]; + } + if (strpos($from, '<')) { + preg_match('/<(.*)>/', $from, $matches); + $from = $matches[1]; + } } - $headers = "From: $from_email{$header_eol}" + $headers = "From: $from{$header_eol}" . "Content-Type: text/plain; charset=UTF-8; format=flowed{$header_eol}" . "MIME-Version: 1.0{$header_eol}" . "Content-Transfer-Encoding: 8bit{$header_eol}"; + + // Sanitise subject by stripping line endings + $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject); + // this is because Elgg encodes everything and matches what is done with body + $subject = html_entity_decode($subject, ENT_COMPAT, 'UTF-8'); // Decode any html entities if (is_callable('mb_encode_mimeheader')) { - $subject = mb_encode_mimeheader($subject,"UTF-8", "B"); + $subject = mb_encode_mimeheader($subject, "UTF-8", "B"); } // Format message - $message = html_entity_decode($message, ENT_COMPAT, 'UTF-8'); // Decode any html entities - $message = strip_tags($message); // Strip tags from message - $message = preg_replace("/(\r\n|\r)/", "\n", $message); // Convert to unix line endings in body - $message = preg_replace("/^From/", ">From", $message); // Change lines starting with From to >From + $body = html_entity_decode($body, ENT_COMPAT, 'UTF-8'); // Decode any html entities + $body = elgg_strip_tags($body); // Strip tags from message + $body = preg_replace("/(\r\n|\r)/", "\n", $body); // Convert to unix line endings in body + $body = preg_replace("/^From/", ">From", $body); // Change lines starting with From to >From - return mail($to, $subject, wordwrap($message), $headers); + return mail($to, $subject, wordwrap($body), $headers); } /** * Correctly initialise notifications and register the email handler. * + * @return void + * @access private */ function notification_init() { // Register a notification handler for the default email method register_notification_handler("email", "email_notify_handler"); // Add settings view to user settings & register action - extend_elgg_settings_page('notifications/settings/usersettings', 'usersettings/user'); - - register_plugin_hook('usersettings:save','user','notification_user_settings_save'); + elgg_extend_view('forms/account/settings', 'core/settings/account/notifications'); - //register_action("notifications/settings/usersettings/save"); - - // Register some APIs - expose_function('user.notification.get', 'get_user_notification_settings', array( - 'user_guid' => array ('type' => 'int') - ), elgg_echo('user.notification.get')); - - expose_function('user.notification.set', 'set_user_notification_settings', array( - 'user_guid' => array ('type' => 'int'), - 'method' => array ('type' => 'string'), - 'value' => array ('type' => 'bool') - ), elgg_echo('user.notification.set')); + elgg_register_plugin_hook_handler('usersettings:save', 'user', 'notification_user_settings_save'); } +/** + * Includes the action to save user notifications + * + * @return void + * @todo why can't this call action(...)? + * @access private + */ function notification_user_settings_save() { global $CONFIG; + //@todo Wha?? include($CONFIG->path . "actions/notifications/settings/usersettings/save.php"); } /** * Register an entity type and subtype to be eligible for notifications * - * @param string $entity_type The type of entity + * @param string $entity_type The type of entity * @param string $object_subtype Its subtype - * @param string $english_name It's English notification string (eg "New blog post") + * @param string $language_name Its localized notification string (eg "New blog post") + * + * @return void */ -function register_notification_object($entity_type, $object_subtype, $english_name) { +function register_notification_object($entity_type, $object_subtype, $language_name) { global $CONFIG; if ($entity_type == '') { @@ -347,15 +421,16 @@ function register_notification_object($entity_type, $object_subtype, $english_na $CONFIG->register_objects[$entity_type] = array(); } - $CONFIG->register_objects[$entity_type][$object_subtype] = $english_name; + $CONFIG->register_objects[$entity_type][$object_subtype] = $language_name; } /** * Establish a 'notify' relationship between the user and a content author * - * @param int $user_guid The GUID of the user who wants to follow a user's content + * @param int $user_guid The GUID of the user who wants to follow a user's content * @param int $author_guid The GUID of the user whose content the user wants to follow - * @return true|false Depending on success + * + * @return bool Depending on success */ function register_notification_interest($user_guid, $author_guid) { return add_entity_relationship($user_guid, 'notify', $author_guid); @@ -364,9 +439,10 @@ function register_notification_interest($user_guid, $author_guid) { /** * Remove a 'notify' relationship between the user and a content author * - * @param int $user_guid The GUID of the user who is following a user's content + * @param int $user_guid The GUID of the user who is following a user's content * @param int $author_guid The GUID of the user whose content the user wants to unfollow - * @return true|false Depending on success + * + * @return bool Depending on success */ function remove_notification_interest($user_guid, $author_guid) { return remove_entity_relationship($user_guid, 'notify', $author_guid); @@ -377,15 +453,23 @@ function remove_notification_interest($user_guid, $author_guid) { * objects and attempts to send notifications to anybody who's interested * * @see register_notification_object + * + * @param string $event create + * @param string $object_type mixed + * @param mixed $object The object created + * + * @return bool + * @access private */ function object_notifications($event, $object_type, $object) { // We only want to trigger notification events for ElggEntities if ($object instanceof ElggEntity) { + /* @var ElggEntity $object */ // Get config data global $CONFIG, $SESSION, $NOTIFICATION_HANDLERS; - $hookresult = trigger_plugin_hook('object:notifications',$object_type,array( + $hookresult = elgg_trigger_plugin_hook('object:notifications', $object_type, array( 'event' => $event, 'object_type' => $object_type, 'object' => $object, @@ -406,29 +490,37 @@ function object_notifications($event, $object_type, $object) { } if (isset($CONFIG->register_objects[$object_type][$object_subtype])) { - $descr = $CONFIG->register_objects[$object_type][$object_subtype]; - $string = $descr . ": " . $object->getURL(); + $subject = $CONFIG->register_objects[$object_type][$object_subtype]; + $string = $subject . ": " . $object->getURL(); // Get users interested in content from this person and notify them // (Person defined by container_guid so we can also subscribe to groups if we want) - foreach($NOTIFICATION_HANDLERS as $method => $foo) { - $interested_users = get_entities_from_relationship('notify' . $method, - $object->container_guid, true, 'user', '', 0, '', 99999); + foreach ($NOTIFICATION_HANDLERS as $method => $foo) { + $interested_users = elgg_get_entities_from_relationship(array( + 'site_guids' => ELGG_ENTITIES_ANY_VALUE, + 'relationship' => 'notify' . $method, + 'relationship_guid' => $object->container_guid, + 'inverse_relationship' => TRUE, + 'type' => 'user', + 'limit' => false + )); + /* @var ElggUser[] $interested_users */ if ($interested_users && is_array($interested_users)) { - foreach($interested_users as $user) { + foreach ($interested_users as $user) { if ($user instanceof ElggUser && !$user->isBanned()) { - if (($user->guid != $SESSION['user']->guid) && has_access_to_entity($object,$user) + if (($user->guid != $SESSION['user']->guid) && has_access_to_entity($object, $user) && $object->access_id != ACCESS_PRIVATE) { - $methodstring = trigger_plugin_hook('notify:entity:message',$object->getType(),array( + $body = elgg_trigger_plugin_hook('notify:entity:message', $object->getType(), array( 'entity' => $object, 'to_entity' => $user, - 'method' => $method),$string); - if (empty($methodstring) && $methodstring !== false) { - $methodstring = $string; + 'method' => $method), $string); + if (empty($body) && $body !== false) { + $body = $string; } - if ($methodstring !== false) { - notify_user($user->guid,$object->container_guid,$descr,$methodstring,NULL,array($method)); + if ($body !== false) { + notify_user($user->guid, $object->container_guid, $subject, $body, + null, array($method)); } } } @@ -440,5 +532,5 @@ function object_notifications($event, $object_type, $object) { } // Register a startup event -register_elgg_event_handler('init','system','notification_init',0); -register_elgg_event_handler('create','object','object_notifications');
\ No newline at end of file +elgg_register_event_handler('init', 'system', 'notification_init', 0); +elgg_register_event_handler('create', 'object', 'object_notifications'); |
