/[drupal]/contributions/modules/messaging/messaging.module
ViewVC logotype

Contents of /contributions/modules/messaging/messaging.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Wed Feb 13 22:21:57 2008 UTC (21 months, 2 weeks ago) by jareyero
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5
Changes since 1.6: +33 -50 lines
File MIME type: text/x-php
- New messaging_sms module by Tim Cullen
- Added 'administer messaging' permission.
- Split the debug functionality out of messaging and messaging_simple: messaging_debug module
- Fixed issues with messaging simple: http://drupal.org/node/218394
1 <?php
2 /**
3 * Drupal Messaging Framework
4 *
5 * This is a messaging framework to allow message sending in a channel independent way
6 * It will provide a common API for sending while allowing plugins for multiple channels
7 *
8 * This module
9 */
10
11 // Method with push delivery. Messages will be pushed to the user using messaging sending methods.
12 define('MESSAGING_TYPE_PUSH', 1);
13 // Method type with pull delivery. Messages will be pulled using messaging pull methods
14 define('MESSAGING_TYPE_PULL', 2);
15
16 /**
17 * Implementation of hook_help().
18 */
19 function messaging_help($section) {
20 if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'messaging' && arg(3) == 'edit' && ($group = arg(4))) {
21 if ($help = messaging_message_group($group, 'help')) {
22 return $help;
23 }
24 }
25 }
26
27 /**
28 * Implementation of hook_menu()
29 */
30 function messaging_menu($may_cache) {
31 $items = array();
32 if ($may_cache) {
33 $items[] = array(
34 'title' => t('Messaging'),
35 'path' => 'admin/settings/messaging',
36 'callback' => 'messaging_admin_page',
37 'access' => user_access('administer messaging'),
38 'description' => t('Administration of messages and sending methods'),
39 );
40 $items[] = array(
41 'title' => t('Messages'),
42 'path' => 'admin/settings/messaging/manage',
43 'type' => MENU_DEFAULT_LOCAL_TASK,
44 );
45 $items[] = array(
46 'title' => t('Messages'),
47 'path' => 'admin/settings/messaging/edit',
48 'callback' => 'messaging_admin_message_edit',
49 'type' => MENU_CALLBACK,
50 );
51 $items[] = array(
52 'title' => t('Settings'),
53 'path' => 'admin/settings/messaging/settings',
54 'callback' => 'drupal_get_form',
55 'callback arguments' => array('messaging_admin_settings'),
56 'type' => MENU_LOCAL_TASK,
57 );
58 }
59 return $items;
60 }
61
62 /**
63 * Implementation of hook_perm()
64 */
65 function messaging_perm() {
66 return array('administer messaging');
67 }
68
69 /**
70 * Implementation of hook_user().
71 *
72 * Adds fieldset and default sending method setting.
73 */
74 function messaging_user($type, $edit, &$user, $category = NULL) {
75 switch ($type) {
76 case 'form':
77 if ($category == 'account' && ($list = messaging_method_list($user))) {
78 $form['messaging'] = array(
79 '#type' => 'fieldset',
80 '#title' => t('Messaging settings'),
81 '#weight' => 5,
82 '#collapsible' => TRUE,
83 );
84 $form['messaging']['messaging_default'] = array(
85 '#type' => 'radios',
86 '#title' => t('Default messaging'),
87 '#default_value' => messaging_method_default($user),
88 '#options' => $list,
89 '#description' => t('Default sending method for getting messages from this system.'),
90 );
91 return $form;
92 }
93 break;
94 }
95 }
96
97 /**
98 * Menu callback. Admin overview page.
99 */
100 function messaging_admin_page($group = NULL, $msgkey = NULL) {
101 if ($group) {
102 return messaging_admin_page_group($group, $msgkey);
103 } else {
104 return messaging_admin_page_overview();
105 }
106 }
107
108 function messaging_admin_page_overview() {
109 $output = '';
110 // List sending methods
111 $methods = messaging_method_list();
112 $output .= theme('box', t('Sending methods'), theme('item_list', $methods));
113 // List message groups
114 $groups = module_invoke_all('messaging', 'message groups');
115 foreach ($groups as $group => $group_info) {
116 $list[] = l($group_info['name'], 'admin/settings/messaging/edit/'.$group);
117 }
118 $output .= theme('box', t('Message groups'), theme('item_list', $list));
119 return $output;
120 }
121
122 /**
123 * Message groups edit page
124 */
125 function messaging_admin_message_edit($group) {
126 $output = '';
127 $groups = module_invoke_all('messaging', 'message groups');
128 if (isset($groups[$group])) {
129 drupal_set_title($groups[$group]['name']);
130
131 $output .= drupal_get_form('messaging_admin_message_form', $group, $groups[$group]);
132 }
133 return $output;
134 }
135
136 /**
137 * Edit message formats
138 */
139 function messaging_admin_message_form($group, $group_info) {
140 $form['group'] = array('#type' => 'value', '#value' => $group);
141 $keylist = module_invoke_all('messaging', 'message keys', $group);
142 $send_methods = array('default' => t('Default'));
143 $send_methods += messaging_method_list();
144 $form['messages'] = array('#tree' => TRUE);
145 foreach ($keylist as $key => $keyname) {
146 $form['messages'][$key] = array(
147 '#type' => 'fieldset',
148 '#title' => $keyname,
149 '#collapsible' => TRUE,
150 '#collapsed' => TRUE,
151 );
152 foreach ($send_methods as $method => $methodname) {
153 $form['messages'][$key][$method] = array(
154 '#title' => $methodname,
155 '#type' => 'textarea',
156 '#default_value' => messaging_message_part($group, $key, $method, FALSE),
157 );
158 }
159 }
160 // Tokens for text replacement
161 if ($tokens = messaging_tokens_get_list($group)) {
162 $form['tokens'] = array(
163 '#title' => t('Available tokens'),
164 '#type' => 'fieldset',
165 '#description' => t('These special strings will be replaced by their real value at run time.'),
166 '#collapsible' => TRUE,
167 '#collapsed' => TRUE,
168 );
169 $form['tokens']['list'] = array(
170 '#value' => theme('item_list', $tokens),
171 );
172 }
173 $form['submit'] = array(
174 '#type' => 'submit',
175 '#value' => t('Save'),
176 );
177 return $form;
178 }
179
180 /**
181 * Get list of tokens for text replacement
182 *
183 * @param $group
184 * Message group to get tokens for
185 * @param $tokens
186 *
187 */
188 function messaging_tokens_get_list($group) {
189 // First compile token types for this message group
190 $type_list = module_invoke_all('messaging', 'tokens', $group);
191
192 // Now get token list from token module for each type
193 $return = array();
194 foreach ($type_list as $type) {
195 if ($list = token_get_list($type)) {
196 foreach ($list as $category => $tokens) {
197 foreach ($tokens as $token => $description) {
198 $return[$token] = "<em>[$token]</em> $description";
199 }
200 }
201 }
202 }
203
204 return $return;
205 }
206 /**
207 * Process and save message parts
208 */
209 function messaging_admin_message_form_submit($form_id, $form_values) {
210 $group = $form_values['group'];
211 foreach ($form_values['messages'] as $key => $messages) {
212 foreach ($messages as $method => $text) {
213 if ($text = trim($text)) {
214 // Save
215 db_query("DELETE FROM {messaging_message_parts} WHERE type = '%s' AND msgkey = '%s' AND method = '%s'", $group, $key, $method);
216 db_query("INSERT INTO {messaging_message_parts} (type, msgkey, method, message) VALUES('%s', '%s', '%s', '%s')", $group, $key, $method, $text);
217 }
218 }
219 }
220 drupal_set_message('The messages have been updated');
221 }
222
223 /**
224 * Admin settings form
225 */
226 function messaging_admin_settings() {
227 $form['general'] = array(
228 '#type' => 'fieldset',
229 '#title' => t('General settings'),
230 );
231 $form['general']['messaging_default_method'] = array(
232 '#title' => t('Default send method'),
233 '#type' => 'radios',
234 '#options' => messaging_method_list(),
235 '#default_value' => variable_get('messaging_default_method', ''),
236 );
237
238 // Sending methods settings
239 $info = messaging_method_info();
240 $form['messaging_methods'] = array('#tree' => TRUE);
241 foreach (filter_formats() as $format) {
242 $format_options[$format->format] = $format->name;
243 }
244 foreach ($info as $method => $options) {
245 // This will preserve settings for disabled modules
246 if (empty($options['name'])) {
247 $form['messaging_methods'][$method]['filter'] = array('#type' => 'value', '#value' => $options['filter']);
248 } else {
249 $form['messaging_methods'][$method] = array(
250 '#type' => 'fieldset',
251 '#title' => t('!name settings', array('!name' => $options['name'])),
252 '#collapsible' => TRUE,
253 );
254 // Output filter
255 $form['messaging_methods'][$method]['filter'] = array(
256 '#type' => 'radios',
257 '#title' => t('Message filter'),
258 '#default_value' => $options['filter'],
259 '#options' => $format_options,
260 );
261 }
262
263 }
264 return system_settings_form($form);
265 }
266
267 /** Messaging API **/
268
269 /**
270 * Send message to user represented by account
271 *
272 * We are applying same output filter for everybody, depending on send method
273 *
274 * The final rendering of the message depends on send method too. I.e. a mail messaging
275 * method may want to insert '--' before the body footer.
276 *
277 * @ TODO Consider whether it makes sense to allow users decide on formatting
278 *
279 * @param $message
280 * Array of message parts that will be compiled depending on send method.
281 * Mandatory message parts, which may have nexted parts are:
282 * - 'subject'
283 * - 'body'. The message body may have 'header', 'content', 'footer', 'etc'
284 * @param $method
285 * Optional send method. Defaults to the user account predefined method
286 */
287 function messaging_message_send_user($account, $message, $method = NULL) {
288 $method = $method ? $method : messaging_method_default($account);
289
290 // Renders subject and body applying filters in the process
291 $info = messaging_method_info($method);
292 if (!empty($info['render'])) {
293 $message = call_user_func($info['render'], $message, $info);
294 } else {
295 $message['subject'] = messaging_message_render($message['subject'], $info);
296 $message['body'] = messaging_message_render($message['body'], $info);
297 }
298
299 // Check for debug option enabled
300 if (variable_get('messaging_debug', 0) && function_exists('messaging_debug_send')) {
301 return messaging_debug_send($account, $message);
302 } elseif (($function = messaging_method_info($method, 'send')) && function_exists($function)) {
303 messaging_log("Message for user: $account->uid, method: $method, subject: ".$message['subject']);
304 return $function($account, $message);
305 } else {
306 watchdog('messaging', t('Message could not be delivered for method %method', array('%method' => $method)), WATCHDOG_ERROR);
307 return FALSE;
308 }
309 }
310
311 /**
312 * Pull pending messages for given methods and user accounts
313 *
314 * Each returned message will be an array with the following elements
315 * - 'to', destination uid
316 * - 'from', originating uid
317 * - 'subject', message subject to be rendered
318 * - 'body', message body to be rendered
319 * @param $method
320 * Send method
321 * @param $users
322 * User id or array of user id's
323 * @param $limit
324 * Optional limit for number of messages
325 * @param $delete
326 * Optional whether to delete messages when fetching
327 * @return array()
328 * Array of pending messages.
329 */
330 function messaging_pull_pending($method, $users, $limit = 0, $delete = TRUE) {
331 // Collect messages checking limit after each module
332 $messages = array();
333 foreach (module_implements('messaging') as $module) {
334 if ($return = module_invoke($module, 'messaging', 'pull', $method, $users, $limit, $delete)) {
335 $messages = array_merge($messages, $return);
336 // Check limit after each fetching
337 if ($limit) {
338 if (count($return) >= $limit) {
339 break;
340 } else {
341 $limit -= count($return);
342 }
343 }
344 }
345 }
346 return $messages;
347 }
348
349 /**
350 * Returns list of messaging methods for a type
351 *
352 * I.e. all messaging methods of pull type
353 */
354 function messaging_method_type($type) {
355 $result = array();
356 foreach (messaging_method_info() as $method => $info) {
357 if ($info['type'] & $type) {
358 $result[$method] = $info;
359 }
360 }
361 return $result;
362 }
363
364 /**
365 * List sending methods
366 *
367 * @param $account
368 * Optional user account, for checking permissions against this account
369 */
370 function messaging_method_list($account = NULL) {
371 $info = messaging_method_info(NULL, 'name');
372 if ($account) {
373 foreach (array_keys($info) as $method) {
374 // Check access for each method
375 if (!messaging_method_permission($method, $account)) {
376 unset($info[$method]);
377 }
378 }
379 }
380 return $info;
381 }
382
383 /**
384 * Check permission for method and account
385 *
386 * @param $method
387 * Sending method id
388 * @param $account
389 * User account to check permission
390 */
391 function messaging_method_permission($method, $account = NULL) {
392 if ($access = messaging_method_info($method, 'access')) {
393 return user_access($access, $account);
394 } else {
395 return TRUE;
396 }
397 }
398
399 /**
400 * Returns default messaging method
401 */
402 function messaging_method_default($account = NULL) {
403 if ($account && $account->messaging_default && messaging_method_permission($account->messaging_default, $account)) {
404 return $account->messaging_default;
405 }
406 elseif ($method = variable_get('messaging_default_method', '')) {
407 return $method;
408 }
409 else {
410 return key(messaging_method_info());
411 }
412 }
413
414 /**
415 * Returns parts of messages, that may be formatted for each sending method
416 *
417 * @ TODO Review logic, optimizations, text pre-fetching
418 * @ TODO Glue text in a method-dependent way
419 *
420 * First checks for message, key, method
421 * Then checks for message, key for method 'default'
422 * Finally checks default values from modules and hook_messaging()
423 *
424 * @param $group
425 * String, specified by the module where the message originates. ie 'subscriptions-event'.
426 * @param $key
427 * String, key for the desired message part.
428 * @param $method
429 * String the mailing method that should be used. OPTIONAL
430 * @param $getdefault
431 * Boolean, whether to use the default if a specific message isn't available for the used method. OPTIONAL, Defaults to true.
432 *
433 * @return
434 * Assembled text of a message part.
435 */
436 function messaging_message_part($group, $key, $method = 'default', $getdefault = TRUE) {
437 static $cache;
438 if (isset($cache[$group][$key][$method])) {
439 $text_part = $cache[$group][$key][$method];
440 } else {
441 if ($method && ($text = db_result(db_query("SELECT message FROM {messaging_message_parts} WHERE type = '%s' AND msgkey = '%s' AND method = '%s'", $group, $key, $method)))){
442 $text_part = $text;
443 } elseif ($method == 'default' && ($text = messaging_message_info($group, $key))) {
444 // Retry with default but also set the cache for this method
445 $text_part = $text;
446 } elseif ($method != 'default' && $getdefault && ($text = messaging_message_part($group, $key, 'default'))) {
447 $text_part = $text;
448 } else {
449 $text_part = FALSE;
450 }
451 // Convert array into plain text
452 if ($text_part && is_array($text_part)) {
453 $text_part = implode("\n", $text_part);
454 }
455 $cache[$group][$key][$method] = $text_part;
456 }
457
458 return $text_part ? $text_part : '';
459 }
460
461 /**
462 * Replaces variables in text
463 *
464 * Obsoleted.
465 */
466 function messaging_text_vars($text, $variables) {
467 // Transform arguments before inserting them
468 foreach ($variables as $key => $value) {
469 switch ($key[0]) {
470 // Escaped only
471 case '@':
472 $args[$key] = check_plain($value);
473 break;
474 // Escaped and placeholder
475 case '%':
476 default:
477 $args[$key] = theme('placeholder', $value);
478 break;
479 // Pass-through
480 case '!':
481 }
482 }
483 foreach ($text as $key => $line) {
484 $text[$key] = strtr($line, $args);
485 }
486 return $text;
487 }
488
489 /**
490 * Returns parts of messages, that may be formatted for each sending method
491 *
492 * @param $group
493 * Message group.
494 * @param $key
495 * Optional message key inside the group. Returns all keys if null.
496 * @return array()
497 * Depending on parameters, may be all message groups and keys or only a specific one.
498 */
499 function messaging_message_info($group, $key = NULL) {
500 static $info;
501 if (!isset($info[$group])) {
502 $info[$group] = module_invoke_all('messaging', 'messages', $group);
503 }
504 if ($key) {
505 return isset($info[$group][$key]) ? $info[$group][$key] : NULL;
506 } elseif ($group) {
507 return isset($info[$group]) ? $info[$group] : array();
508 } else {
509 return $info;
510 }
511 }
512
513 /**
514 * Returns information about message groups
515 *
516 * @param $group
517 * Optional message group. Returns all groups if null.
518 * @param $key
519 * Optional message key inside the group. Returns all keys if null.
520 * @return array()
521 * Depending on parameters, may be all message groups and keys or only a specific one.
522 */
523 function messaging_message_group($group = NULL, $key = NULL) {
524 static $info;
525 if (!isset($info)) {
526 $info = module_invoke_all('messaging', 'message groups');
527 }
528 if ($key) {
529 return isset($info[$group][$key]) ? $info[$group][$key] : NULL;
530 } elseif ($group) {
531 return isset($info[$group]) ? $info[$group] : array();
532 } else {
533 return $info;
534 }
535 }
536
537 /** Plug-in management **/
538
539 /**
540 * Returns messaging methods properties
541 *
542 * @param $method
543 * Optional, Method to get properties for, none or NULL for all methods
544 * @param $property
545 * Optional, Property to get, none or NULL for all properties
546 * @param $default
547 * Optional default value to return when there's not that property for the method
548 */
549 function messaging_method_info($method = NULL, $property = NULL,$default = NULL) {
550 static $info, $properties;
551 if (!$info) {
552 $info = module_invoke_all('messaging', 'send methods');
553 // Merge settings
554 if ($settings = variable_get('messaging_methods', array())) {
555 $info = array_merge_recursive($info, $settings);
556 }
557 }
558 if ($method && $property) {
559 return isset($info[$method][$property]) ? $info[$method][$property] : $default;
560 } elseif ($method) {
561 return isset($info[$method]) ? $info[$method] : array();
562 } elseif ($property) {
563 if (!isset($properties[$property])) {
564 $properties[$property] = array();
565 foreach($info as $method => $values) {
566 if (isset($values[$property])) {
567 $properties[$property][$method] = $values[$property];
568 }
569 }
570 }
571 return $properties[$property];
572 } else {
573 return $info;
574 }
575 }
576
577 /** Message composition and rendering **/
578
579 /**
580 * Composes message from different parts, recursively
581 *
582 * @param $text
583 * Simple string or array of message parts
584 * It may have named elements like #prefix and #text
585 * or it may be single strings to render straight forward
586 * @param $info
587 * Messaging method info. It passes information about glue characters, filters to be used, etc...
588 */
589 function messaging_message_render($text, $info) {
590 $output = '';
591 $glue = !empty($info['glue']) ? $info['glue'] : '';
592 if (!empty($info['filter'])) {
593 $text = messaging_message_filter($text, $info['filter']);
594 // Unset filter so it's not applied again
595 unset($info['filter']);
596 }
597 if (is_array($text)) {
598 if (isset($text['#prefix'])) {
599 $output .= $text['#prefix'].$glue;
600 unset($text['#prefix']);
601 }
602 if (isset($text['#text'])) {
603 $output .= $text['#text'];
604 return $output;
605 }
606 foreach (element_children($text) as $key) {
607 $text[$key] = messaging_message_render($text[$key], $info);
608 }
609 $output .= implode($glue, $text);
610 } else {
611 $output .= $text;
612 }
613 return $output;
614 }
615
616 /**
617 * Filters message parts before final formatting
618 */
619 function messaging_message_filter($text, $format = FILTER_FORMAT_DEFAULT) {
620 if (is_array($text)) {
621 foreach (element_children($text) as $key) {
622 $text[$key] = messaging_message_filter($text[$key], $format);
623 }
624 // Don't forget #text element
625 if (!empty($text['#text'])) {
626 $text['#text'] = messaging_message_filter($text['#text'], $format);
627 }
628 } else {
629 $text = check_markup($text, $format, FALSE);
630 }
631 return $text;
632 }
633
634 /**
635 * Implementation of hook_filter(). Contains a basic set of essential filters.
636 * - Plain text:
637 * Strips out all html
638 * Replaces html entities
639 */
640 function messaging_filter($op, $delta = 0, $format = -1, $text = '') {
641 switch ($op) {
642 case 'list':
643 return array(0 => t('Plain text filter'));
644
645 case 'no cache':
646 return TRUE; // No caching at all
647
648 case 'description':
649 switch ($delta) {
650 case 0:
651 return t('Filters out all html tags and replaces html entities by characters.');
652 }
653
654 case 'process':
655 switch ($delta) {
656 case 0:
657 $text = filter_xss($text, array());
658 $text = htmlspecialchars_decode($text, ENT_QUOTES);
659 return $text;
660 default:
661 return $text;
662 }
663
664 case 'settings':
665 return;
666
667 default:
668 return $text;
669 }
670 }
671
672 /**
673 * Log facility for debugging
674 */
675 function messaging_log($txt = NULL) {
676 static $logs;
677 if ($txt) {
678 $logs[] = $txt;
679 } else {
680 return $logs;
681 }
682 }

  ViewVC Help
Powered by ViewVC 1.1.2