'); // Limit the size of static caches define('MESSAGING_CACHE_LIMIT', variable_get('messaging_cache_limit', 1000)); // Limit the max % of cron time this module can use (this can be overridden by some modules) define('MESSAGING_DEFAULT_CRON_PERCENT', variable_get('messaging_default_cron_percent', 80)); /** * Implementation of hook_autoload_info(). */ function messaging_autoload_info() { return array( 'Messaging_User_Object' => array('file' => 'includes/messaging_object.class.inc'), 'Messaging_Object' => array('file' => 'includes/messaging_object.class.inc'), 'Messaging_Cached_Object' => array('file' => 'includes/messaging_object.class.inc'), 'Messaging_Destination' => array('file' => 'includes/messaging_destination.class.inc'), 'Messaging_Message' => array('file' => 'includes/messaging_message.class.inc'), 'Messaging_Send_Method' => array('file' => 'includes/messaging_method.class.inc'), 'Messaging_Method_Mail' => array('file' => 'includes/messaging_method_mail.class.inc'), 'Messaging_Store' => array('file' => 'includes/messaging_store.class.inc'), 'Messaging_None' => array('file' => 'includes/messaging_object.class.inc'), ); } /** * Implementation of hook_help(). */ function messaging_help($path, $arg) { switch ($path) { case 'admin/help#messaging': $output = '

' . t('The messaging module is the engine that handles outgoing messages and message queueing for different sending methods.') . '

'; $output .= '

' . t('You need to enable one or more of the included plug-ins to be able to actually take advantage of it.') . '

'; return $output; case 'admin/messaging/settings/method/filters': $output = '

' . t('These are the filters for the message body. They should depend on the content and the tokens you are using for messages. This is important for getting the right formatting and also for security.') . '

'; $oubput .= t('If using raw tokens for templates, possibly you\'ll need some additional formatting here.'); $items[] = t('You can set up Input formats for specific message parts on the Message templates pages. These will be run first on each piece of text.', array('@message_templates' => url('admin/messaging/template'))); $items[] = t('Once the message body is built you can set a Format filter on this page to format and filter it. Set up the filters you need using the Input formats page', array('@input_formats' => url('admin/settings/filters'))); $items[] = t('Last, the Final filter will be run for adjusting the text to the format required by each Send method.'); $output .= theme('item_list', $items); return $output; } } /** * Implementation of hook_menu() */ function messaging_menu() { $items['admin/messaging'] = array( 'title' => 'Messaging', 'access arguments' => array('administer messaging'), 'description' => 'Administer and configure messaging', 'page callback' => 'system_admin_menu_block_page', 'file' => 'system.admin.inc', 'file path' => drupal_get_path('module', 'system'), ); $items['admin/messaging/settings'] = array( 'title' => 'Messaging settings', 'description' => 'Configuration of messaging framework', 'page callback' => 'drupal_get_form', 'page arguments' => array('messaging_admin_settings'), 'access arguments' => array('administer messaging'), 'file' => 'messaging.admin.inc', ); $items['admin/messaging/settings/overview'] = array( 'title' => 'Messaging', 'description' => 'Configuration of sending methods', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/messaging/settings/method'] = array( 'title' => 'Send methods', 'description' => 'Configuration of sending methods', 'page callback' => 'drupal_get_form', 'page arguments' => array('messaging_admin_method_settings'), 'access arguments' => array('administer messaging'), 'weight' => -10, 'file' => 'messaging.admin.inc', 'type' => MENU_LOCAL_TASK, ); $items['admin/messaging/settings/method/overview'] = array( 'title' => 'Options', 'description' => 'General settings', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/messaging/settings/method/filters'] = array( 'title' => 'Filters', 'description' => 'Filters and formatting', 'page callback' => 'drupal_get_form', 'page arguments' => array('messaging_admin_method_filters'), 'access arguments' => array('administer filters'), 'file' => 'messaging.admin.inc', 'type' => MENU_LOCAL_TASK, ); // If we have any mail methods enabled, provide a specific tab messaging_include('mail.inc'); if (messaging_mail_methods()) { $items['admin/messaging/settings/method/mail'] = array( 'title' => 'Mail', 'description' => 'Common options for mail methods', 'page callback' => 'drupal_get_form', 'page arguments' => array('messaging_mail_admin_settings'), 'access arguments' => array('administer messaging'), 'file' => 'includes/mail.inc', 'type' => MENU_LOCAL_TASK, ); } // Test tab, moving from messaging_debug() $items['admin/messaging/settings/test'] = array( 'title' => 'Test', 'description' => 'Test message sending', 'page callback' => 'drupal_get_form', 'page arguments' => array('messaging_admin_test_post_form'), 'access arguments' => array('administer messaging'), 'file' => 'messaging.admin.inc', 'weight' => -10, 'type' => MENU_LOCAL_TASK, ); return $items; } /** * Implementation of hook_perm() */ function messaging_perm() { return array('administer messaging'); } /** * Implementation of hook_user(). * * Adds fieldset and default sending method setting. */ function messaging_user($type, $edit, &$user, $category = NULL) { switch ($type) { case 'delete': // Delete user data from tables Messaging_Destination::delete_multiple(array('uid' => $user->uid)); messaging_store()->delete_multiple(array('uid' => $user->uid)); break; case 'update': // Claim user mail address if in destinations table messaging_include('mail.inc'); messaging_mail_update_user($user); break; case 'form': if ($category == 'account' && ($list = messaging_method_list($user))) { $form['messaging'] = array( '#type' => 'fieldset', '#title' => t('Messaging settings'), '#weight' => 5, '#collapsible' => TRUE, ); $form['messaging']['messaging_default'] = array( '#type' => 'select', '#title' => t('Default send method'), '#default_value' => messaging_method_default($user), '#options' => $list, '#description' => t('Default sending method for getting messages from this system.'), '#disabled' => count($list) == 1, ); return $form; } break; } } /** Messaging API **/ /** * Get send method object * * @param $method * Method name or Messaging_Method object * @param $method_info * Array of properties to build a custom send method or override existing ones */ function messaging_send_method($method, $method_info = array()) { $send_methods = &messaging_static(__FUNCTION__); if (is_object($method)) { return $method; } if (!isset($send_methods[$method])) { // This will init the method list if not done before if ($info = messaging_method_info($method)) { $class = !empty($info['class']) ? $info['class'] : 'Messaging_Send_Method'; $send_methods[$method] = new $class($method, $method_info + $info); } else { // No info available from modules. This is a custom method or a disabled one $send_methods[$method] = new Messaging_Send_Method($method, $method_info); // Add to static cache so it can be found later $static_info = &messaging_info('send methods'); $static_info[$method] = $method_info; } } return $send_methods[$method]; } /** * Build message object from object or array */ function messaging_message_build($message) { return messaging_check_object($message, 'Messaging_Message', TRUE); } /** * Send message to array of destinations. The message is rendered just once. * * @param $destinations * Single destination or array of destinations for sending. * The element type depends on sending method so it can be a list of e-mail addresses, user accounts, etc * @param $message * Message object or array of message parts. * @param $method * Sending method. Unlike for messaging_message_send_user() for which the sending method may be user's default * it is not an optional parameter for this function. * @param $queue * Optional flag to force queueing. * We may want to force queueing for bulk messaging. Otherwise it will depend on the sending method * wether to queue the messages (for pull methods) or not (push methods) */ function messaging_message_send($destinations, $message, $method, $queue = FALSE) { messaging_debug('Sending message', array('destinations' => $destinations, 'message' => $message, 'method' => $method, 'queue' => $queue)); // Convert into an object and add all the information into the message object $message = messaging_message_build($message); $message->method = $method; if (is_array($destinations)) { $message->set_multiple($destinations); } else { $message->set_destination($destinations); } // This will return true if the message was sent or queued for delivery return $queue ? $message->queue() : $message->send(); } /** * Send message to destination object * * @param $destination * Messaging Destination object to recieve message. * @param $message * Message object or array of message parts. * @param $queue * Optional send method. Defaults to the user account predefined method */ function messaging_message_send_destination($method, $destination, $message, $queue = FALSE) { messaging_debug('Sending message to destination', array('destination' => $destination, 'message' => $message)); // Build array of parameters so they can be overridden by callbacks $message = messaging_message_build($message); $message->method = $method; $message->set_destination($destination); return $queue ? $message->queue() : $message->send(); } /** * Send message to user represented by account * * We are applying same output filter for everybody, depending on send method * * The final rendering of the message depends on send method too. I.e. a mail messaging * method may want to insert '--' before the body footer. * * @ TODO Consider whether it makes sense to allow users decide on formatting * * @param $account * User object to recieve message. * @param $message * Array of message parts that will be compiled depending on send method. * Mandatory message parts, which may have nexted parts are: * - 'type' * - 'subject' * - 'body'. The message body may have 'header', 'content', 'footer', 'etc' * @param $method * Optional send method. Defaults to the user account predefined method */ function messaging_message_send_user($account, $message, $method = NULL, $queue = FALSE) { messaging_debug('Sending message to user', array('account' => $account, 'message' => $message, 'method' => $method)); // Build array of parameters so they can be overridden by callbacks $message = messaging_message_build($message); if (isset($method)) { $message->method = $method; } // Check user and availability if there's a callback for that. Depending on the sending method, // when user is not available it may just be discarded / queued / redirected through other method if ($message->set_user($account)) { messaging_debug('Found destination for user, sending message', array('method' => $message->method, 'destination' => $message->destination)); return $queue ? $message->queue() : $message->send(); } else { watchdog('messaging', 'Cannot send message to user !username. @message', array('!username' => theme('username', $account), '@message' => (string)$message), WATCHDOG_WARNING); return FALSE; } } /** * Build destination object from user account (and create destination if not exists) */ function messaging_account_build_destination($account, $method = NULL, $address = NULL) { if ($account->uid) { if ($method && !$address) { $address = messaging_user_destination($account, $method); } if ($method && $address) { return Messaging_Destination::create_method($method, $address, $account->uid); } elseif (($fallback = messaging_method_default($account)) && $fallback != $method) { // Retry with new method return messaging_account_build_destination($account, $fallback); } } elseif ($method && $address) { // Anonymous users // @todo check the address doesn't belong to any user ??? return Messaging_Destination::create_method($method, $address, 0); } } /** * Get destination from user account. * * This will handle also anonymous user accounts that should have a 'destination' property */ function messaging_user_destination($account, $method, $message = NULL) { return messaging_send_method($method)->get_user_address($account); } /** * Test sending, just log message */ function messaging_message_test($message, $destinations = array()) { $message->prepare(); $message->render(); messaging_log('Emulating message sending (test run)', array('summary' => (string)$message, 'message' => $message, 'destinations' => $destinations)); return $message->success = TRUE; } /** * Implementation of hook_cron() * * Process queued messages for delivery, update, do clean up... */ function messaging_cron() { if (variable_get('messaging_queue_process_cron', TRUE)) { messaging_store()->cron_process(); } } /** * Returns list of messaging methods for a type * * I.e. all messaging methods of pull type */ function messaging_method_type($type) { $result = array(); foreach (messaging_method_info() as $method => $info) { if ($info['type'] & $type) { $result[$method] = $info; } } return $result; } /** * List sending methods * * @param $account * Optional user account, for checking permissions against this account * If no account passed it is a list for the administrator */ function messaging_method_list($account = NULL) { $info = messaging_method_info(NULL, 'name'); if ($account) { foreach (array_keys($info) as $method) { // Check access for each method and check destination if (!messaging_method_permission($method, $account) || !messaging_user_destination($account, $method)) { unset($info[$method]); } else { $info[$method] = messaging_translate("method:$method:name", $info[$method]); } } } return $info; } /** * Check permission for method and account * * @param $method * Sending method id * @param $account * User account to check permission */ function messaging_method_permission($method, $account = NULL) { return messaging_method_info($method, 'enabled') && messaging_send_method($method)->user_access($account); } /** * Returns default messaging method */ function messaging_method_default($account = NULL) { if ($account && !empty($account->messaging_default) && messaging_method_permission($account->messaging_default, $account)) { return $account->messaging_default; } elseif ($method = variable_get('messaging_default_method', '')) { return $method; } else { return key(messaging_method_info()); } } /** * Get setting from user account or get default setting if not available * * If first checks for a 'messaging_$name' property in the user account * and returns the value of the variable 'messaging_default_$name' if not set * * There's an optional variable 'messaging_peruser_$name' that if true will block * per user settings and use only global settings. * * @param $name * Option name * @param $account * Optional account to check setting for * @param $default * Default value if no option set */ function messaging_user_setting($name, $account = NULL, $default = NULL) { $variable = 'messaging_' . $name; if ($account && isset($account->$variable) && variable_get('messaging_peruser_' . $name, 1)) { return $account->$variable; } else { return variable_get('messaging_default_' . $name, $default); } } /** * Returns messaging methods properties * * @param $method * Optional, Method to get properties for, none or NULL for all methods * @param $property * Optional, Property to get, none or NULL for all properties * @param $default * Optional default value to return when there's not that property for the method */ function messaging_method_info($method = NULL, $property = NULL, $default = NULL) { static $info; if (!isset($info)) { // Collect method info without 'alter', we do it later $info = &messaging_info('send methods', NULL, FALSE, FALSE); // Get list of enabled methods. All will be enabled by default. $enabled = variable_get('messaging_method_enabled', array()); // Merge settings from variable for each enabled method foreach (array_keys($info) as $name) { $info[$name] = array_merge( $info[$name], variable_get('messaging_method_' . $name, array()), variable_get('messaging_filters_' . $name, array()) ); // If not set enabled flag, the method will be enabled by default $info[$name]['enabled'] = isset($enabled[$name]) ? $enabled[$name] : TRUE; } // Allow altering by other modules after we've set the variables drupal_alter('messaging_send_methods', $info); } return messaging_array_info($info, $method, $property, $default); } /** * Returns messaging address properties */ function messaging_address_info($type = NULL, $property = NULL, $default = NULL) { $info = &messaging_info('address types'); return messaging_array_info($info, $type, $property, $default); } /** * Implementation of hook_messaging() */ function messaging_messaging($op, $type = NULL) { switch ($op) { case 'text filters': messaging_include('text.inc'); return _messaging_text_filter_info(); case 'address types': // Get some built in address types $types['mail'] = array( 'name' => t('E-mail address'), // Name for display 'validate callback' => 'valid_email_address', // Validation callback 'account_property' => 'mail', // Property on user account 'address2uid callback' => 'messaging_address_get_uid', 'field_name' => 'mail', // Field on which this address is stored 'field_table' => 'users', // Table on which this address is stored ); $types['user'] = array( 'name' => t('User name'), // Name of the address for this method 'format callback' => 'messaging_user_format_name', // Format address for display 'account_property' => 'uid', // Property on user account 'field_name' => 'uid', // Field on which this address is stored 'field_table' => 'users', // Table on which this address is stored ); return $types; } } /** * Map mail to uid */ function messaging_address_get_uid($address, $property = 'mail') { return db_result(db_query("SELECT uid FROM {users} WHERE $property = '%s'", $address)); } /** * Entry point for the storage and queueing API * * Default methods are implemented by Messaging_Store class * * This API can be replaced by setting a new class name in the 'messaging_store' variable * Or the whole thing can be skipped by settings 'messaging_store' to 'Messaging_None' */ function messaging_store() { static $messaging_store; if (!isset($messaging_store)) { $class = variable_get('messaging_store', 'Messaging_Store'); $messaging_store = new $class(); } return $messaging_store; } /** * Helper user loading function with static caching */ function messaging_load_user($uid) { $cache = &messaging_static(__FUNCTION__); if (!$cache || !array_key_exists($uid, $cache)) { $cache[$uid] = $uid ? user_load($uid) : drupal_anonymous_user(); } return $cache[$uid]; } /** * Helper function for message loading from the store * * @param $mqid * Message id */ function messaging_message_load($mqid, $refresh = FALSE) { return messaging_store()->message_load($mqid, $refresh); } /** * Implementation of hook_requirements() */ function messaging_requirements($phase) { $requirements = array(); // Ensure translations don't break at install time $t = get_t(); if ($phase == 'runtime') { $methods = messaging_method_list(); // Ensure that we have some sending methods available if (!$methods) { $requirements['messaging'] = array( 'title' => $t('Messaging sending methods'), 'value' => $t('No sending method plug-ins available. Please enable some Sending Method on the !admin-modules page.', array('!admin-modules' => l($t('Modules administration'), 'admin/build/modules'))), 'severity' => REQUIREMENT_ERROR, ); } } return $requirements; } /** * Process incoming message. This is the entry point for plug-in modules * * This is just a wrapper for handling incoming in messaging_incoming module */ function messaging_message_in($method, $channel, $message, $params = array()) { if (function_exists('messaging_incoming_post')) { return messaging_incoming_post($method, $channel, $message, $params); } else { return FALSE; } } /** * Update messaging method. * * When a messaging method is disabled, we need to update current settings for this and other modues * * @param $method * Method to disable * @param $replace * Optional replacement method suggested by the disabled one. */ function messaging_method_disable($method, $replace = NULL) { module_load_include('install', 'messaging'); $replace = isset($replace) ? $replace : messaging_update_method_replace($method, TRUE); messaging_update_method_disable($method, $replace); if ($replace) { drupal_set_message(t('Disabled messaging sending method %method and replaced by %replace', array('%method' => messaging_method_info($method, 'title'), '%replace' => messaging_method_info($replace, 'title')))); } else { // It seems all methods are disabled, print warning drupal_set_message(t('Disabled messaging sending method but cannot find a replacement. Please, enable some other sending method.'), 'error'); } } /** * Build callback structure to be invoked later * * A callback structure will be an array containing the function name to invoke * and a list of arguments for that function * * @param $function * Function name * @param $arg1, $arg1, $arg3 */ function _messaging_callback() { $args = func_get_args(); if (count($args) > 1) { // Array with function name, param1, param2.. return $args; } else { // Simple string with function name return array_shift($args); } } /** * Invoke callback with variable arguments * * We don't check whether the function exists so it will crash if it's missing * * @param $callback * Function name or array(function, arg1, arg2..) * @param $arg1, $arg2... * Variable number of arguments */ function _messaging_callback_invoke() { $args = func_get_args(); $callback = array_shift($args); if (is_array($callback)) { // It is an array: function, arg1, arg2... $function = array_shift($callback); $params = $callback; } else { // It is just a function name $function = $callback; $params = array(); } // Merge parameters and go for it $params = array_merge($params, $args); return call_user_func_array($function, $params); } /** * Check that a parameter is an instance of a given class */ function messaging_check_object($object, $class, $create = FALSE) { if ($object && is_object($object) && is_a($object, $class)) { return $object; } elseif ($create) { return new $class($object); } } /** * Implementation of hook_token_list(). Documents the individual * tokens handled by the module. */ function messaging_token_list($type = 'all') { $tokens = array(); if ($type == 'message' || $type == 'all') { $tokens['message']['message-subject'] = t('The message subject.'); $tokens['message']['message-body'] = t('The message body.'); $tokens['message']['message-author-name'] = t('The message\'s author name.'); $tokens['message']['message-method'] = t('The message\'s method name.'); $tokens['message']['message-date'] = t('The message\'s sending date.'); } if ($type == 'destination' || $type == 'all') { $tokens['destination']['destination-address'] = t('Destination address.'); $tokens['destination']['destination-type'] = t('Destination address type.'); } return $tokens; } /** * Implementation of hook_token_values() */ function messaging_token_values($type, $object = NULL, $options = array()) { switch ($type) { case 'message': if ($message = messaging_check_object($object, 'Messaging_Message')) { $values['message-subject'] = check_plain($message->get_subject()); $values['message-body'] = filter_xss($message->get_body()); $values['message-author-name'] = check_plain($message->get_sender_name()); $values['message-method'] = messaging_method_info($message->method, 'name'); $values['message-date'] = format_date($message->sent); return $values; } break; case 'destination': // Messaging destinations if ($destination = messaging_check_object($object, 'Messaging_Destination')) { $values['destination-address'] = $destination->format_address(FALSE); $values['destination-type'] = $destination->address_name(); return $values; } break; } } /** * Implementation of hook_theme() */ function messaging_theme() { return array( 'messaging_admin_methods_table' => array( 'arguments' => array('elements' => NULL), 'file' => 'messaging.admin.inc', ), 'messaging_admin_settings_table' => array( 'arguments' => array('elements' => NULL), 'file' => 'messaging.admin.inc', ), ); } /** * Add a callback to a callback collection * * @param $callback_list * Existing callback list or method info array * @param $type * Callback type * @param $callback * Callback function name or array('function_name', arg1, arg2...) * */ function _messaging_callback_add(&$callback_list, $type, $callback) { $name = $type . ' callback'; // If the existing callback is a single function name, make it an array if (!empty($callback_list[$name]) && !is_array($callback_list[$name])) { $callback_list[$name] = array($callback_list[$name]); } $callback_list[$name][] = $callback; } /** * Get a callback from the information array if present * * @param $info * Information array, typically a messaging method info array * @param $type * Callback type, the search key will be '[type] callback' * @param $default * Default callback to return if not in the $info array */ function _messaging_callback_get($info, $type, $default = NULL) { if (!empty($info[$type . ' callback'])) { return $info[$type . ' callback']; } else { return $default; } } /** * Helper function to get property from an info array * * Depending on method and property, returns the full array or a specific property */ function messaging_array_info($info, $method = NULL, $property = NULL, $default = NULL) { if ($method && $property) { return isset($info[$method][$property]) ? $info[$method][$property] : $default; } elseif ($method) { return isset($info[$method]) ? $info[$method] : $default; } elseif ($property) { // Get this property as a list indexed by method $props = array(); foreach($info as $method => $values) { if (isset($values[$property])) { $props[$method] = $values[$property]; } } return $props; } else { return $info; } } /** * Helper function for query builders. * * Using schema data get 'field = [placeholder]' and args arrays * * @param $table * Table name (to be prefixed) * @param $params * Array of field => value conditions * @param $table_alias * Optional table alias to prefix fields in conditions */ function _messaging_query_conditions($table, $params, $table_alias = NULL) { $query = _messaging_query_where($table, $params, $table_alias); // Return arrwy with conditions and arguments. Also full where clause. return array('conditions' => $query['where'], 'args' => $query['args'], 'where' => implode(' AND ', $query['where'])); } /** * Query builder: Build where conditions and arguments using schema data * * Using schema data get 'field = [placeholder]' and args arrays * * @param $table * Table name (to be prefixed) * @param $params * Array of field => value conditions * @param $table_alias * Optional table alias to prefix fields in conditions * * @return array() * Array with 'where' (array of where conditions) and 'args' (array of query arguments) */ function _messaging_query_where($table, $params, $table_alias = NULL) { $schema = drupal_get_schema($table); $conditions = $args = array(); foreach ($params as $field => $value) { // If not a field we ignore the parameter if (isset($schema['fields'][$field])) { $type = $schema['fields'][$field]['type']; $field_name = $table_alias ? "$table_alias.$field" : $field; // For array values, build IN conditions if (is_array($value)) { $conditions[] = $field_name . ' IN (' . db_placeholders($value, $type) . ')'; $args = array_merge($args, $value); } elseif (is_null($value)) { $condtions[] = $field_name . ' IS NULL'; } else { $conditions[] = $field_name . ' = ' . db_type_placeholder($type); $args[] = $value; } } } // Return arrwy with conditions and arguments. Also full where clause. return array('where' => $conditions, 'args' => $args); } /** * Build the SQL statement from query elements * * It will build INSERT + SELECT or SELECT queries from its elements * * @param $query * Array of query parameters * @param $execute * Whether to execute the query right away or return parameters * @return mixed * If not $execute, list($sql, $args) * If execute, query result */ function messaging_query_sql($query, $execute = FALSE) { $sql = !empty($query['sql']) ? $query['sql'] : ''; if (!empty($query['insert'])) { $sql .= 'INSERT INTO ' . $query['into'] . ' ('. implode(', ', $query['insert']) .') '; } if (!empty($query['select'])) { $sql .= !empty($query['distinct']) ? 'SELECT DISTINCT ' : 'SELECT '; $sql .= implode(', ', $query['select']); $sql .= ' FROM '. implode(', ', $query['from']); } if (!empty($query['join'])) { $sql .= ' '. implode(' ', $query['join']); } if (!empty($query['where'])) { $sql .= ' WHERE ('. implode(') AND (', $query['where']) .')'; } if (!empty($query['group'])) { $sql .= ' GROUP BY '. implode(', ', $query['group']); } if (!empty($query['having'])) { $sql .= ' HAVING '. implode(' AND ', $query['having']); } // Merge all args, start with generic ones for subscription queries, then other groups $args = !empty($query['args']) ? $query['args'] : array(); foreach (array('select', 'join', 'where', 'having') as $key) { if (!empty($query[$key .' args'])) { $args = array_merge($args, $query[$key .' args']); } } // Add order by if (!empty($query['order'])) { $sql .= ' ORDER BY ' . implode(', ', $query['order']); } // Return parameters or execute query if (!$execute) { return array($sql, $args); } elseif (!empty($query['pager'])) { return pager_query($sql, $query['limit'], $query['pager'], NULL, $args); } elseif (!empty($query['limit'])) { return db_query_range($sql, $args, 0, $query['limit']); } else { return db_query($sql, $args); } } /** * Short hand for info logs */ function messaging_log($txt = NULL, $variables = NULL) { return _messaging_log('info', $txt, $variables); } /** * Get logs without formatting */ function messaging_log_get() { if ($logs = _messaging_log('return')) { _messaging_log('reset'); return $logs; } } /** * Init logging system so logs are saved from now on */ function messaging_log_start() { return _messaging_log('start'); } /** * Short hand for debug logs */ function messaging_debug($txt = NULL, $variables = NULL) { if (function_exists('messaging_debug_log')) { return messaging_debug_log($txt, $variables); } } /** * Format logs */ function messaging_log_format($logs) { $rows = array(); foreach ($logs as $log) { list($type, $string, $append, $objects) = _messaging_log_format($log); // Full objects/arrays are only rendered when debug module is enabled if ($objects && function_exists('_messaging_debug_format_log')) { $text = _messaging_debug_format_log($type, $string, $append, $objects); } else { $text = $string; if ($append) { $text .= '
' . implode(' ', $append); } } $rows[] = array( $type, $text, ); } return theme('table', array(t('Type'), t('Message')), $rows); } /** * Quick logging for debugging and manual queue processing */ function _messaging_log($type, $txt = NULL, $variables = NULL, $severity = WATCHDOG_NOTICE) { static $enabled = FALSE; switch ($type) { case 'info': case 'debug': if ($enabled) { $_SESSION['messaging_log'][] = array($type, $txt, $variables, $severity); } break; case 'return': return isset($_SESSION['messaging_log']) ? $_SESSION['messaging_log'] : NULL; break; case 'reset': unset($_SESSION['messaging_log']); break; case 'start': $enabled = TRUE; break; case 'stop': $enabled = FALSE; break; } } /** * Format messaging / notifications log as single text line */ function _messaging_log_format($log) { list($type, $string, $args, $severity) = $log; $append = $replace = $objects = array(); if ($args) { // Transform arguments before inserting them. foreach ($args as $key => $value) { if (is_numeric($value) || is_string($value)) { switch ($key[0]) { case '@': // Escaped only. $replace[$key] = check_plain($value); break; case '%': $replace[$key] = theme('placeholder', $value); break; case '!': // Pass-through. $replace[$key] = $value; break; default: // Append to string a key value pair, different from watchdog format $append[$key] = '' . $key . '= ' . check_plain($value); break; } } elseif (is_array($value) || is_object($value)) { $objects[$key] = $value; } } $string = strtr($string, $replace); } return array($type, $string, $append, $objects); } /** * Wrapper function for 1i8nstrings() if i18nstrings enabled. */ function messaging_translate($name, $string, $langcode = NULL, $textgroup = 'messaging') { return function_exists('i18nstrings') ? i18nstrings($textgroup . ':' . $name, $string, $langcode) : $string; } /** * Implementation of hook_locale(). */ function messaging_locale($op = 'groups') { switch ($op) { case 'groups': return array('messaging' => t('Notifications')); case 'info': $info['messaging']['refresh callback'] = 'messaging_locale_refresh'; $info['messaging']['format'] = TRUE; // Strings may have format return $info; } } /** * Refresh strings for translation system */ function messaging_locale_refresh() { foreach (messaging_method_info() as $name => $info) { i18nstrings_update("messaging:method:$name:name", $info['name']); } return TRUE; } /** * Set data on static cache, checking whether the cache is under limits. */ function messaging_static_cache_set($cache_name, $key, $value) { $cache = &messaging_static($cache_name); if ($cache && MESSAGING_CACHE_LIMIT && count($cache) > MESSAGING_CACHE_LIMIT) { $cache = array(); } $cache[$key] = $value; } /** * Get data from static cache */ function messaging_static_cache_get($cache_name, $key) { $cache = &messaging_static($cache_name); return isset($cache[$key]) ? $cache[$key] : NULL; } /** * Central static variable storage, Drupal 7 core backport * * See http://api.drupal.org/api/function/drupal_static/7 */ function &messaging_static($name, $default_value = NULL, $reset = FALSE) { static $data = array(), $default = array(); if (!isset($name)) { // All variables are reset. This needs to be done one at a time so that // references returned by earlier invocations of drupal_static() also get // reset. foreach ($default as $name => $value) { $data[$name] = $value; } // As the function returns a reference, the return should always be a // variable. return $data; } if ($reset) { // The reset means the default is loaded. if (array_key_exists($name, $default)) { $data[$name] = $default[$name]; } else { // Reset was called before a default is set and yet a variable must be // returned. return $data; } } elseif (!array_key_exists($name, $data)) { // Store the default value internally and also copy it to the reference to // be returned. $default[$name] = $data[$name] = $default_value; } return $data[$name]; } /** * Reset static variable. Drupal 7 core backport. * * See http://api.drupal.org/api/function/drupal_static_reset/7 * * @param $name * Name of the static variable to reset. Omit to reset all variables. */ function messaging_static_reset($name = NULL) { messaging_static($name, NULL, TRUE); } /** * Load destination from key. Menu autoloading. */ function messaging_destination_load($mdid) { return Messaging_Destination::load($mdid); } /** * Include module files as necessary. * * The files must be in an 'includes' subfolder inside the module folder. */ function messaging_include($file, $module = 'messaging') { static $included = array(); if (!isset($included[$module][$file])) { require_once './' . drupal_get_path('module', $module) . '/includes/' . $file; $included[$module][$file] = TRUE; } } /** * Invoke hook_notifications($name) on all modules * * This is like module_invoke all with some differences: * - The results are just merged (not recursively) * - The module name is added to each resulting array * - We cache all the results */ function &messaging_info($name, $param = NULL, $refresh = FALSE, $alter = TRUE) { $info = &messaging_static('messaging_info_' . $name); if (!isset($info) || $refresh) { $info = messaging_module_invoke_all('messaging', $name, $param); // Provide alter hook if ($alter) { drupal_alter('messaging_' . strtr($name, ' ', '_'), $info); } } return $info; } /** * Invoke hook on all modules * * This is like module_invoke_all with some differences: * - The results are just merged (not recursively) * - The module name is added to each resulting array * * @param $hook * The name of the hook to invoke. * @param ... * Arguments to pass to the hook. * @return * An array of return values of the hook implementations. If modules return * arrays from their implementations, those are merged into one array. */ function messaging_module_invoke_all() { $args = func_get_args(); $hook = $args[0]; unset($args[0]); $return = array(); foreach (module_implements($hook) as $module) { $function = $module .'_'. $hook; $result = call_user_func_array($function, $args); if (isset($result) && is_array($result)) { foreach ($result as $key => &$value) { if (is_array($value)) { $value += array('module' => $module); } } $return = array_merge($return, $result); } else if (isset($result)) { $return[] = $result; } } return $return; } /** * Helper function, get uid from account parameter (object or uid) */ function messaging_user_uid($account) { return is_object($account) ? $account->uid : $account; } /** * Helper function, get object from account parameter (object or uid) */ function messaging_user_object($account) { return is_object($account) ? $account : messaging_load_user($account); } /** * Helper function, get user object property from account parameter (object or uid) */ function messaging_user_property($account, $property) { $account = messaging_user_object($account); return $account && isset($account->$property) ? $account->$property : NULL; } /** * Callback for printing user names */ function messaging_user_format_name($account, $html = FALSE) { $account = messaging_user_object($account); return $html ? theme('username', $account) : check_plain($account->name); }