PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/elgg/engine/lib/notification.php

https://bitbucket.org/rhizomatik/lorea_production/
PHP | 484 lines | 264 code | 76 blank | 144 comment | 62 complexity | 74d905de74fd514bcd46ea8e3770a47b MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * Notifications
  4. * This file contains classes and functions which allow plugins to register and send notifications.
  5. *
  6. * There are notification methods which are provided out of the box (see notification_init() ). Each method
  7. * is identified by a string, e.g. "email".
  8. *
  9. * To register an event use register_notification_handler() and pass the method name and a handler function.
  10. *
  11. * To send a notification call notify() passing it the method you wish to use combined with a number of method
  12. * specific addressing parameters.
  13. *
  14. * Catch NotificationException to trap errors.
  15. *
  16. * @package Elgg
  17. * @subpackage API
  18. */
  19. /** Notification handlers */
  20. global $NOTIFICATION_HANDLERS;
  21. $NOTIFICATION_HANDLERS = array();
  22. /**
  23. * This function registers a handler for a given notification type (eg "email")
  24. *
  25. * @param string $method The method
  26. * @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.
  27. * @param array $params A associated array of other parameters for this handler defining some properties eg. supported message length or rich text support.
  28. */
  29. function register_notification_handler($method, $handler, $params = NULL) {
  30. global $NOTIFICATION_HANDLERS;
  31. if (is_callable($handler)) {
  32. $NOTIFICATION_HANDLERS[$method] = new stdClass;
  33. $NOTIFICATION_HANDLERS[$method]->handler = $handler;
  34. if ($params) {
  35. foreach ($params as $k => $v) {
  36. $NOTIFICATION_HANDLERS[$method]->$k = $v;
  37. }
  38. }
  39. return true;
  40. }
  41. return false;
  42. }
  43. /**
  44. * This function unregisters a handler for a given notification type (eg "email")
  45. *
  46. * @param string $method The method
  47. * @since 1.7.1
  48. */
  49. function unregister_notification_handler($method) {
  50. global $NOTIFICATION_HANDLERS;
  51. if (isset($NOTIFICATION_HANDLERS[$method])) {
  52. unset($NOTIFICATION_HANDLERS[$method]);
  53. }
  54. }
  55. /**
  56. * Notify a user via their preferences.
  57. *
  58. * @param mixed $to Either a guid or an array of guid's to notify.
  59. * @param int $from GUID of the sender, which may be a user, site or object.
  60. * @param string $subject Message subject.
  61. * @param string $message Message body.
  62. * @param array $params Misc additional parameters specific to various methods.
  63. * @param mixed $methods_override A string, or an array of strings specifying the delivery methods to use - or leave blank
  64. * for delivery using the user's chosen delivery methods.
  65. * @return array Compound array of each delivery user/delivery method's success or failure.
  66. * @throws NotificationException
  67. */
  68. function notify_user($to, $from, $subject, $message, array $params = NULL, $methods_override = "") {
  69. global $NOTIFICATION_HANDLERS, $CONFIG;
  70. // Sanitise
  71. if (!is_array($to)) {
  72. $to = array((int)$to);
  73. }
  74. $from = (int)$from;
  75. //$subject = sanitise_string($subject);
  76. // Get notification methods
  77. if (($methods_override) && (!is_array($methods_override))) {
  78. $methods_override = array($methods_override);
  79. }
  80. $result = array();
  81. foreach ($to as $guid) {
  82. // Results for a user are...
  83. $result[$guid] = array();
  84. if ($guid) { // Is the guid > 0?
  85. // Are we overriding delivery?
  86. $methods = $methods_override;
  87. if (!$methods) {
  88. $tmp = (array)get_user_notification_settings($guid);
  89. $methods = array();
  90. foreach($tmp as $k => $v) {
  91. // Add method if method is turned on for user!
  92. if ($v) {
  93. $methods[] = $k;
  94. }
  95. }
  96. }
  97. if ($methods) {
  98. // Deliver
  99. foreach ($methods as $method) {
  100. if (!isset($NOTIFICATION_HANDLERS[$method])) {
  101. continue;
  102. }
  103. // Extract method details from list
  104. $details = $NOTIFICATION_HANDLERS[$method];
  105. $handler = $details->handler;
  106. if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler)) {
  107. error_log(sprintf(elgg_echo('NotificationException:NoHandlerFound'), $method));
  108. }
  109. elgg_log("Sending message to $guid using $method");
  110. // Trigger handler and retrieve result.
  111. try {
  112. $result[$guid][$method] = $handler(
  113. $from ? get_entity($from) : NULL, // From entity
  114. get_entity($guid), // To entity
  115. $subject, // The subject
  116. $message, // Message
  117. $params // Params
  118. );
  119. } catch (Exception $e) {
  120. error_log($e->getMessage());
  121. }
  122. }
  123. }
  124. }
  125. }
  126. return $result;
  127. }
  128. /**
  129. * Get the notification settings for a given user.
  130. *
  131. * @param int $user_guid The user id
  132. * @return stdClass
  133. */
  134. function get_user_notification_settings($user_guid = 0) {
  135. $user_guid = (int)$user_guid;
  136. if ($user_guid == 0) {
  137. $user_guid = get_loggedin_userid();
  138. }
  139. $all_metadata = get_metadata_for_entity($user_guid);
  140. if ($all_metadata) {
  141. $prefix = "notification:method:";
  142. $return = new stdClass;
  143. foreach ($all_metadata as $meta) {
  144. $name = substr($meta->name, strlen($prefix));
  145. $value = $meta->value;
  146. if (strpos($meta->name, $prefix) === 0) {
  147. $return->$name = $value;
  148. }
  149. }
  150. return $return;
  151. }
  152. return false;
  153. }
  154. /**
  155. * Set a user notification pref.
  156. *
  157. * @param int $user_guid The user id.
  158. * @param string $method The delivery method (eg. email)
  159. * @param bool $value On(true) or off(false).
  160. * @return bool
  161. */
  162. function set_user_notification_setting($user_guid, $method, $value) {
  163. $user_guid = (int)$user_guid;
  164. $method = sanitise_string($method);
  165. $user = get_entity($user_guid);
  166. if (!$user) {
  167. $user = get_loggedin_user();
  168. }
  169. if (($user) && ($user instanceof ElggUser)) {
  170. $prefix = "notification:method:$method";
  171. $user->$prefix = $value;
  172. $user->save();
  173. return true;
  174. }
  175. return false;
  176. }
  177. /**
  178. * Notification exception.
  179. */
  180. class NotificationException extends Exception {}
  181. /**
  182. * Send a notification via email.
  183. *
  184. * @param ElggEntity $from The from user/site/object
  185. * @param ElggUser $to To which user?
  186. * @param string $subject The subject of the message.
  187. * @param string $message The message body
  188. * @param array $params Optional parameters (none taken in this instance)
  189. * @return bool
  190. */
  191. function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message, array $params = NULL) {
  192. global $CONFIG;
  193. if (!$from) {
  194. throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'from'));
  195. }
  196. if (!$to) {
  197. throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'to'));
  198. }
  199. if ($to->email=="") {
  200. throw new NotificationException(sprintf(elgg_echo('NotificationException:NoEmailAddress'), $to->guid));
  201. }
  202. // To
  203. $to = $to->email;
  204. // From
  205. $site = get_entity($CONFIG->site_guid);
  206. // If there's an email address, use it - but only if its not from a user.
  207. if (!($from instanceof ElggUser) && $from->email) {
  208. $from = $from->email;
  209. } else if ($site && $site->email) {
  210. // Use email address of current site if we cannot use sender's email
  211. $from = $site->email;
  212. } else {
  213. // If all else fails, use the domain of the site.
  214. $from = 'noreply@' . get_site_domain($CONFIG->site_guid);
  215. }
  216. return elgg_send_email($from, $to, $subject, $message);
  217. }
  218. /**
  219. * Send an email to any email address
  220. *
  221. * @param string $from Email address or string: "name <email>"
  222. * @param string $to Email address or string: "name <email>"
  223. * @param string $subject The subject of the message
  224. * @param string $body The message body
  225. * @param array $params Optional parameters (none used in this function)
  226. * @return bool
  227. * @since 1.7.2
  228. */
  229. function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
  230. global $CONFIG;
  231. if (!$from) {
  232. throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'from'));
  233. }
  234. if (!$to) {
  235. throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'to'));
  236. }
  237. // return TRUE/FALSE to stop elgg_send_email() from sending
  238. $mail_params = array( 'to' => $to,
  239. 'from' => $from,
  240. 'subject' => $subject,
  241. 'body' => $body,
  242. 'params' => $params);
  243. $result = trigger_plugin_hook('email', 'system', $mail_params, NULL);
  244. if ($result !== NULL) {
  245. return $result;
  246. }
  247. $header_eol = "\r\n";
  248. if (isset($CONFIG->broken_mta) && $CONFIG->broken_mta) {
  249. // Allow non-RFC 2822 mail headers to support some broken MTAs
  250. $header_eol = "\n";
  251. }
  252. // Windows is somewhat broken, so we use just address for to and from
  253. if (strtolower(substr(PHP_OS, 0 , 3)) == 'win') {
  254. // strip name from to and from
  255. if (strpos($to, '<')) {
  256. preg_match('/<(.*)>/', $to, $matches);
  257. $to = $matches[1];
  258. }
  259. if (strpos($from, '<')) {
  260. preg_match('/<(.*)>/', $from, $matches);
  261. $from = $matches[1];
  262. }
  263. }
  264. $headers = "From: $from{$header_eol}"
  265. . "Content-Type: text/plain; charset=UTF-8; format=flowed{$header_eol}"
  266. . "MIME-Version: 1.0{$header_eol}"
  267. . "Content-Transfer-Encoding: 8bit{$header_eol}";
  268. // Sanitise subject by stripping line endings
  269. $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject);
  270. if (is_callable('mb_encode_mimeheader')) {
  271. $subject = mb_encode_mimeheader($subject,"UTF-8", "B");
  272. }
  273. // Format message
  274. $body = html_entity_decode($body, ENT_COMPAT, 'UTF-8'); // Decode any html entities
  275. $body = strip_tags($body); // Strip tags from message
  276. $body = preg_replace("/(\r\n|\r)/", "\n", $body); // Convert to unix line endings in body
  277. $body = preg_replace("/^From/", ">From", $body); // Change lines starting with From to >From
  278. $message = wordwrap($message);
  279. return mail($to, $subject, wordwrap($body), $headers);
  280. }
  281. /**
  282. * Correctly initialise notifications and register the email handler.
  283. *
  284. */
  285. function notification_init() {
  286. // Register a notification handler for the default email method
  287. register_notification_handler("email", "email_notify_handler");
  288. // Add settings view to user settings & register action
  289. extend_elgg_settings_page('notifications/settings/usersettings', 'usersettings/user');
  290. register_plugin_hook('usersettings:save','user','notification_user_settings_save');
  291. //register_action("notifications/settings/usersettings/save");
  292. }
  293. function notification_user_settings_save() {
  294. global $CONFIG;
  295. include($CONFIG->path . "actions/notifications/settings/usersettings/save.php");
  296. }
  297. /**
  298. * Register an entity type and subtype to be eligible for notifications
  299. *
  300. * @param string $entity_type The type of entity
  301. * @param string $object_subtype Its subtype
  302. * @param string $english_name It's English notification string (eg "New blog post")
  303. */
  304. function register_notification_object($entity_type, $object_subtype, $english_name) {
  305. global $CONFIG;
  306. if ($entity_type == '') {
  307. $entity_type = '__BLANK__';
  308. }
  309. if ($object_subtype == '') {
  310. $object_subtype = '__BLANK__';
  311. }
  312. if (!isset($CONFIG->register_objects)) {
  313. $CONFIG->register_objects = array();
  314. }
  315. if (!isset($CONFIG->register_objects[$entity_type])) {
  316. $CONFIG->register_objects[$entity_type] = array();
  317. }
  318. $CONFIG->register_objects[$entity_type][$object_subtype] = $english_name;
  319. }
  320. /**
  321. * Establish a 'notify' relationship between the user and a content author
  322. *
  323. * @param int $user_guid The GUID of the user who wants to follow a user's content
  324. * @param int $author_guid The GUID of the user whose content the user wants to follow
  325. * @return true|false Depending on success
  326. */
  327. function register_notification_interest($user_guid, $author_guid) {
  328. return add_entity_relationship($user_guid, 'notify', $author_guid);
  329. }
  330. /**
  331. * Remove a 'notify' relationship between the user and a content author
  332. *
  333. * @param int $user_guid The GUID of the user who is following a user's content
  334. * @param int $author_guid The GUID of the user whose content the user wants to unfollow
  335. * @return true|false Depending on success
  336. */
  337. function remove_notification_interest($user_guid, $author_guid) {
  338. return remove_entity_relationship($user_guid, 'notify', $author_guid);
  339. }
  340. /**
  341. * Automatically triggered notification on 'create' events that looks at registered
  342. * objects and attempts to send notifications to anybody who's interested
  343. *
  344. * @see register_notification_object
  345. */
  346. function object_notifications($event, $object_type, $object) {
  347. // We only want to trigger notification events for ElggEntities
  348. if ($object instanceof ElggEntity) {
  349. // Get config data
  350. global $CONFIG, $SESSION, $NOTIFICATION_HANDLERS;
  351. $hookresult = trigger_plugin_hook('object:notifications',$object_type,array(
  352. 'event' => $event,
  353. 'object_type' => $object_type,
  354. 'object' => $object,
  355. ), false);
  356. if ($hookresult === true) {
  357. return true;
  358. }
  359. // Have we registered notifications for this type of entity?
  360. $object_type = $object->getType();
  361. if (empty($object_type)) {
  362. $object_type = '__BLANK__';
  363. }
  364. $object_subtype = $object->getSubtype();
  365. if (empty($object_subtype)) {
  366. $object_subtype = '__BLANK__';
  367. }
  368. if (isset($CONFIG->register_objects[$object_type][$object_subtype])) {
  369. $descr = $CONFIG->register_objects[$object_type][$object_subtype];
  370. $string = $descr . ": " . $object->getURL();
  371. // Get users interested in content from this person and notify them
  372. // (Person defined by container_guid so we can also subscribe to groups if we want)
  373. foreach($NOTIFICATION_HANDLERS as $method => $foo) {
  374. $interested_users = elgg_get_entities_from_relationship(array(
  375. 'relationship' => 'notify' . $method,
  376. 'relationship_guid' => $object->container_guid,
  377. 'inverse_relationship' => TRUE,
  378. 'types' => 'user',
  379. 'limit' => 99999
  380. ));
  381. if ($interested_users && is_array($interested_users)) {
  382. foreach($interested_users as $user) {
  383. if ($user instanceof ElggUser && !$user->isBanned()) {
  384. if (($user->guid != $SESSION['user']->guid) && has_access_to_entity($object,$user)
  385. && $object->access_id != ACCESS_PRIVATE) {
  386. $methodstring = trigger_plugin_hook('notify:entity:message',$object->getType(),array(
  387. 'entity' => $object,
  388. 'to_entity' => $user,
  389. 'method' => $method),$string);
  390. if (empty($methodstring) && $methodstring !== false) {
  391. $methodstring = $string;
  392. }
  393. if ($methodstring !== false) {
  394. notify_user($user->guid,$object->container_guid,$descr,$methodstring,NULL,array($method));
  395. }
  396. }
  397. }
  398. }
  399. }
  400. }
  401. }
  402. }
  403. }
  404. // Register a startup event
  405. register_elgg_event_handler('init','system','notification_init',0);
  406. register_elgg_event_handler('create','object','object_notifications');