t('Messaging & Notifications'), 'path' => 'admin/messaging', 'callback' => 'messaging_admin_overview_page', 'access' => user_access('administer messaging') || user_access('administer notifications'), 'description' => t('Administration of messaging and notifications'), ); // Notifications settings $items[] = array( 'path' => 'admin/messaging/notifications', 'title' => t('Notifications settings'), 'description' => t('Site settings for user notifications.'), 'callback' => 'drupal_get_form', 'callback arguments' => 'notifications_settings_form', 'access' => user_access('administer site configuration'), ); $items[] = array( 'path' => 'admin/messaging/notifications/settings', 'title' => t('Settings'), 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items[] = array( 'path' => 'admin/messaging/notifications/intervals', 'title' => t('Intervals'), 'callback' => 'drupal_get_form', 'callback arguments' => 'notifications_send_intervals_form', 'type' => MENU_LOCAL_TASK, ); $items[] = array( 'path' => 'admin/messaging/notifications/events', 'title' => t('Events'), 'callback' => 'drupal_get_form', 'callback arguments' => 'notifications_admin_events_form', 'type' => MENU_LOCAL_TASK, ); // Notifications status and queue management $items[] = array('path' => 'admin/messaging/notifications-status', 'title' => t('Notifications status'), 'description' => t('Manage notifications status queue'), 'callback' => 'notifications_admin_status_page', 'access' => user_access('administer notifications'), ); $items[] = array('path' => 'admin/messaging/notifications-status/overview', 'title' => t('Overview'), 'description' => t('Notifications queue overview.'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items[] = array('path' => 'admin/messaging/notifications-status/queue', 'title' => t('Queue'), 'description' => t('Manage notifications queue.'), 'callback' => 'notifications_admin_queue', 'type' => MENU_LOCAL_TASK ); // Subscribe links. For this items access will be checked later in the page $items[] = array( 'path' => 'notifications/subscribe', 'type' => MENU_CALLBACK, 'callback' => 'notifications_page_subscribe', 'access' => $user->uid, ); // Unsubscribe links This page will need to work with anonymous users $items[] = array( 'path' => 'notifications/unsubscribe', 'type' => MENU_CALLBACK, 'callback' => 'notifications_page_unsubscribe', 'access' => TRUE, ); } else { if (arg(0) == 'notifications' || arg(1) == 'notifications' || arg(2) == 'notifications' || arg(2) == 'notifications-status') { include_once drupal_get_path('module', 'notifications') .'/notifications.admin.inc'; } if ($user->uid && arg(0) == 'user' && is_numeric(arg(1)) && ($user->uid == arg(1) && user_access('maintain own subscriptions') || user_access('administer notifications'))) { $account = ($user->uid == arg(1)) ? $user : user_load(array('uid' => arg(1))); $items[] = array( 'path' => 'user/'. $account->uid .'/notifications', 'type' => MENU_LOCAL_TASK, 'title' => t('Notifications'), 'callback' => 'notifications_page_user_overview', 'callback arguments' => array($account) ); $items[] = array( 'path' => 'user/'. $account->uid .'/notifications/overview', 'type' => MENU_DEFAULT_LOCAL_TASK, 'title' => t('Overview'), 'weight' => -10, ); } } return $items; } /** * Implementation of hook_perms() * */ function notifications_perm() { return array_merge(array('administer notifications', 'maintain own subscriptions')); } /** * Implementation of hook_user(). */ function notifications_user($type, $edit, &$user, $category = NULL) { switch ($type) { case 'delete'; // Delete related data on tables notifications_delete_subscriptions(array('uid' => $user->uid)); break; case 'form': if ((user_access('maintain own subscriptions') || user_access("admin users subscriptions") )&& $category == 'account') { // Add settings into Messaging group $form = messaging_user($type, $edit, $user, $category); /* $form['notifications']['notifications_teaser'] = array( '#type' => 'checkbox', '#title' => t('Include teaser'), '#default_value' => isset($edit['notifications_teaser']) ? $edit['notifications_teaser'] : variable_get('notifications_teaser', 0), '#description' => t('Checking this box adds an excerpt of the post to the subscription email.'), ); */ //return $form; } break; } } /** * Implementation of hook_form_alter() * - Override title for messaging settings */ function notifications_form_alter($form_id, &$form) {//dsm($form); if ($form_id == 'user_edit' && $form['_category']['#value'] == 'account' && (user_access('maintain own subscriptions') || user_access('administer notifications'))) { $form['messaging']['#title'] = t('Messaging and Notifications settings'); $send_intervals = _notifications_send_intervals(); $form['messaging']['notifications_send_interval'] = array( '#type' => 'select', '#title' => t('Default send interval'), '#options' => $send_intervals, '#default_value' => notifications_user_setting('send_interval', $form['_account']['#value']), '#disabled' => count($send_intervals) == 1, '#description' => t('Default send interval for subscriptions.'), ); } } /** * Gets a user setting, defaults to default system setting for each * * @param $name * Setting name * @param $account * Optional user account, will default to current user * @param $default * Optional default to return if this is not set */ function notifications_user_setting($name, $account = NULL, $default = NULL) { global $user; $account = $account ? $account : $user; // Default send method is taken from messaging module if ($name == 'send_method') { return messaging_method_default($account); } $field = 'notifications_' . $name; if (isset($account->$field)) { return $account->$field; } else { return variable_get('notifications_default_' . $name, $default); } } /** * Process subscriptions events */ function notifications_event($event) { global $user; // Fill in event with default values, that can be changed by modules implementing hook_notifications('event trigger', ..) // Such modules can mark the event to be discarded changing the 'save' and 'queue' options $event += array( 'uid' => $user->uid, 'load_args' => '', 'created' => time(), 'module' => 'notifications', // Module that triggered the event 'type' => '', // Object/event type 'action' => '', // Action that happened to the object 'params' => array(), // Object parameters ); // Check whether we have to save and queue this event, defaults to yes if not set $info = variable_get('notifications_events', array()); if (isset($info[$event['type']][$event['action']]) && !$info[$event['type']][$event['action']]) { // Do not store nor queue this event, can be changed by plug-in modules $event += array( 'save' => FALSE, 'queue' => FALSE, ); } else { $event += array( 'save' => TRUE, 'queue' => TRUE, ); } $event = (object)$event; // Notify other modules we are about to trigger some subscriptions event // Modules can do cleanup operations or modify event properties notifications_module_invoke('event trigger', $event); // Store event, unles marked not to be saved if ($event->save) { notifications_event_save($event); } // Send event to queue for subscriptions, unless marked not to if ($event->queue) { notifications_queue($event); } return $event; } /** * Queue events for notifications * * @param $event * Event array. */ function notifications_queue($event) { global $notifications_send_inmediate; // Build query fields for this event type. If no arguments retrieved, skip this step if ($query_args = notifications_module_information('query', 'event', $event->type, $event)) { $query['args'] = array($event->eid, $event->created, $event->type); foreach ($query_args as $query_params) { $query = notifications_query_build($query_params, $query); } // We throw in all the conditions and check the number of matching conditions // that must be equal to the subscription conditions number $sql = 'INSERT INTO {notifications_queue} (uid, sid, module, eid, send_interval, send_method, cron, created, conditions) '. 'SELECT DISTINCT s.uid, s.sid, s.module, %d, s.send_interval, s.send_method, s.cron, %d, s.conditions '. 'FROM {notifications} s INNER JOIN {notifications_fields} f ON s.sid = f.sid '. implode(' ', $query['join']); $sql .= " WHERE s.status = 1 AND s.event_type = '%s' AND s.send_interval >= 0 AND ((". implode(') OR (', $query['where']) .')) '; // Add one more condition if we don't send notifications on own posts if (!variable_get('notifications_sendself', 0)) { $sql .= 'AND s.uid != %d '; $query['args'][] = $event->uid; } // Some group by fields are not really needed but added for pgsql compatibility $sql .= 'GROUP BY s.uid, s.sid, s.module, s.send_interval, s.send_method, s.cron, s.conditions HAVING s.conditions = count(f.sid)'; db_query($sql, $query['args']); } // Modules can do cleanup operations or modify the queue notifications_module_invoke('event queued', $event); // If immediate sending enabled, store eid for sending on page exit. if (variable_get('notifications_send_immediate', 0)) { $notifications_send_inmediate[] = $event->eid; } } /** * Implementation of hook_exit() * * This is where the inmediate sending is done if enabled, so we are sure all other modules * have finished node processing when node update. */ function notifications_exit() { global $notifications_send_inmediate; if (!empty($notifications_send_inmediate)) { require_once drupal_get_path('module', 'notifications') .'/notifications.cron.inc'; foreach ($notifications_send_inmediate as $eid) { notifications_process_rows(array('cron' => 1, 'eid' => $eid, 'send_interval' => 0)); } } } /** * Query builder for subscriptions * * @param $params * Array of query conditions */ function notifications_query_build($params, $base = array()) { $query = $base + array('select' => array(), 'join' => array(), 'where' => array(), 'args' => array()); foreach ($params as $name => $elements) { if ($name == 'fields') { foreach ($elements as $field => $value ) { $query['where'][] = "f.field = '%s' AND f.value = '%s'"; $query['args'][] = $field; $query['args'][] = $value; } } elseif (is_array($elements)) { $query[$name] = array_merge($query[$name], $elements); } else { $query[$name][] = $elements; } } return $query; } /** * Stores new events * * @param $event * Event object * @ TODO: Avoid to and from array conversion */ function notifications_event_save(&$event) { $event = (array)$event; $event['eid'] = db_next_id('{notifications_event}_eid'); db_query("INSERT INTO {notifications_event} (eid, module, type, action, uid, created, params) VALUES (%d, '%s', '%s', '%s', %d, %d, '%s')", $event['eid'], $event['module'], $event['type'], $event['action'], $event['uid'], $event['created'], serialize($event['params'])); $event = (object)$event; } /** * Get subscription for a given user * * @param $uid * User id * @param $event_type * Event type * @param $oid * Object id for caching. I.e. for a node it will be nid * @param $object * Object to check subscriptions to. I.e. $node * * @return * Array of subscriptions for this user and object indexed by sid */ function notifications_user_get_subscriptions($uid, $event_type, $oid, $object = NULL, $refresh = FALSE) { static $subscriptions; if ($refresh || !isset($subscriptions[$uid][$event_type][$oid])) { $subscriptions[$uid][$event_type][$oid] = array(); $query_args = notifications_module_information('query', 'user', $event_type, $object); // Base query $query = array( 'args' => array($uid, $event_type), ); foreach ($query_args as $query_params) { $query = notifications_query_build($query_params, $query); } // Build the query merging all the parts $sql = 'SELECT s.*, f.* FROM {notifications} s INNER JOIN {notifications_fields} f ON s.sid = f.sid '; if (!empty($query['join'])) { $sql .= implode(' ', $query['join']); } $sql .= " WHERE s.uid = %d AND event_type = '%s' "; if (!empty($query['where'])) { $sql .= " AND ((". implode(') OR (', $query['where']) .'))'; } $result = db_query($sql, $query['args']); while ($sub = db_fetch_object($result)) { if (!isset($subscriptions[$uid][$event_type][$oid][$sub->sid])) { $subscriptions[$uid][$event_type][$oid][$sub->sid] = $sub; } $subscriptions[$uid][$event_type][$oid][$sub->sid]->fields[$sub->field] = $sub->value; } } return $subscriptions[$uid][$event_type][$oid]; } /** * Update or create subscription * * This function checks for duplicated subscriptions before saving. * If a similar subscription is found it will be updated. * If no subscription is found and it is new, the sid will be added into the object. * * @param $subscription * Subscription object or array */ function notifications_save_subscription(&$subscription) { global $user; $subscription = (object)$subscription; $subscription->conditions = count($subscription->fields); $account = $subscription->uid ? user_load(array('uid' => $subscription->uid)) : $user; // Default values for fields: send_interval, send_method, etc... foreach (_notifications_subscription_defaults($account) as $field => $value) { if (!isset($subscription->$field)) { $subscription->$field = $value; } } // The cron parameter will be always enabled because we are not managing push/pull methods anymore at this level. $subscription->cron = 1; if ($subscription->sid) { $op = 'update'; db_query("UPDATE {notifications} SET uid = %d, type = '%s', event_type = '%s', conditions = %d, send_interval = '%d', send_method = '%s', cron = %d, module = '%s', status = %d WHERE sid = %d", $subscription->uid, $subscription->type, $subscription->event_type, $subscription->conditions, $subscription->send_interval, $subscription->send_method, $subscription->cron, $subscription->module, $subscription->status, $subscription->sid); db_query("DELETE FROM {notifications_fields} WHERE sid = %d", $subscription->sid); } elseif ($duplicate = notifications_get_subscriptions(array('uid' => $subscription->uid, 'type' => $subscription->type, 'event_type' => $subscription->event_type, 'module' => $subscription->module), $subscription->fields)) { // We've found duplicates, resolve conflict updating first, deleting the rest $update = array_shift($duplicate); $update->send_interval = $subscription->send_interval; $uddate->send_method = $subscription->send_method; $subscription = $update; // If there are more, delete, keep the table clean while (array_shift($duplicate)) { notifications_delete_subscription($duplicate->sid); } return notifications_save_subscription($subscription); } else { $op = 'insert'; $subscription->sid = db_next_id('{notifications}_sid'); db_query("INSERT INTO {notifications} (sid, uid, type, event_type, conditions, send_interval, send_method, cron, module, status) VALUES(%d, %d, '%s', '%s', %d, %d, '%s', %d, '%s', %d)", $subscription->sid, $subscription->uid, $subscription->type, $subscription->event_type, $subscription->conditions, $subscription->send_interval, $subscription->send_method, $subscription->cron, $subscription->module, $subscription->status); } // There may be subscriptions with no fields, some people are coding such plug-ins. if ($subscription->fields) { foreach ($subscription->fields as $name => $value) { db_query("INSERT INTO {notifications_fields} (sid, field, value) VALUES(%d, '%s', '%s')", $subscription->sid, $name, $value); } } notifications_module_invoke($op, $subscription); } /** * Get an individual subscription. * * @param $subs * Either a subscription object or a subscription id (sid). * @param $refresh * Force cache refresh * @return * Subscriptions object. */ function notifications_load_subscription($subs, $refresh = FALSE) { static $cache = array(); if (is_object($subs)) { $sid = $subs->sid; $subscription = $subs; } else { $sid = $subs; } if ($refresh || !array_key_exists($sid, $cache)) { if (!isset($subscription)) { $subscription = db_fetch_object(db_query("SELECT * FROM {notifications} WHERE sid = %d", $sid)); } if ($subscription) { $subscription->fields = array(); $result = db_query("SELECT * FROM {notifications_fields} WHERE sid = %d", $sid); while ($condition = db_fetch_object($result)) { $subscription->fields[$condition->field] = $condition->value; } } $cache[$sid] = $subscription; } return $cache[$sid]; } /** * Delete subscription and clean up related data. * * It also removes pending notifications related to that subscription * * @param $sid * Id of subscriptin to delete */ function notifications_delete_subscription($sid) { foreach (array('notifications', 'notifications_fields', 'notifications_queue') as $table) { db_query("DELETE FROM {". $table ."} WHERE sid = %d", $sid); } } /** * Delete multiple subscriptions and clean up related data (pending notifications, fields). * * Warning: It will delete also subscriptions with more conditions than the fields passed. * * @param array $params * Array of multiple conditions in the notifications table to delete subscriptions * @param array $conditions * Array of multiple conditions in the notifications_fields table to delete subscriptions */ function notifications_delete_subscriptions($params, $conditions = array()) { $join = $where = $args = array(); foreach ($params as $field => $value) { $where[] = 'n.'. $field ." = '%s'"; $args[] = $value; } // Now we need to join once the fields table for each condition if ($conditions) { $index = 0; foreach ($conditions as $field => $value) { $alias = 'nf'. $index++; $join[] = "INNER JOIN {notifications_fields} $alias ON n.sid = $alias.sid"; $where[] = "$alias.field = '%s'"; $where[] = "$alias.value = '%s'"; $args[] = $field; $args[] = $value; } } // Query notificatinons that meet these conditions and build an array $result = db_query('SELECT n.sid FROM {notifications} n '. implode(' ', $join) .' WHERE '. implode(' AND ', $where), $args); $delete = array(); while ($n = db_fetch_object($result)) { $delete[] = $n->sid; } // This is the actual deletion. We've fetched the values from the db so this needs no escaping. if ($delete) { $str_sids = implode(',', $delete); foreach (array('notifications_fields', 'notifications_queue', 'notifications') as $table) { db_query("DELETE FROM {". $table ."} WHERE sid IN ($str_sids)"); } } } /** * Get subscriptions that fit a set of conditions. * * @param $params * Array of parameters for the query * @param $conditions * Optional array of condition fields * @param $limit * Whether to limit the result to subscriptions with exatly that condition fields * @param $key * Optional key field to use as the array index. Will default to sid * * @return * Array of subscriptions indexed by uid, module, field, value, author */ function notifications_get_subscriptions($params, $conditions = array(), $limit = TRUE, $key = 'sid', $pager = NULL) { // Build query $join = $where = array(); if ($conditions) { if ($limit) { $params += array('conditions' => count($conditions)); } $index = 0; foreach ($conditions as $name => $value) { $alias = "f$index"; $join[] = "INNER JOIN {notifications_fields} $alias ON s.sid = $alias.sid "; $params["$alias.field"] = $name; if (!is_null($value)) { $params["$alias.value"] = $value; } $index++; } } foreach ($params as $field => $value) { $name = strstr($field, '.') ? $field : 's.'. $field; $where[] = (is_numeric($value) && strstr($field, 's.')) ? $name .' = %d' : "$name = '%s'"; } $sql = 'SELECT * FROM {notifications} s '. implode(' ', $join) . ' WHERE '. implode(' AND ', $where); if ($pager) { $sql .= ' ORDER BY s.sid'; $result = pager_query($sql, $pager, 0, NULL, $params); } else { $result = db_query($sql, $params); } $subscriptions = array(); while ($s = db_fetch_object($result)) { $subscriptions[$s->$key] = notifications_load_subscription($s); } return $subscriptions; } /** * Get info about subscription types * * @param $type * String, the subscriptions type OPTIONAL * @param $field * String, a specific field to retrieve info from OPTIONAL * * @return * Information for a given field and type * or information for a given field for all types */ function notifications_subscription_types($type = NULL, $field = NULL) { static $types; if (!isset($types)) { $types = notifications_module_information('subscription types'); notifications_alter('subscription_types', $types); } if ($field && $type) { return isset($types[$type][$field]) ? $types[$type][$field] : NULL; } elseif ($field) { $return = array(); foreach ($types as $id => $info) { $return[$id] = $info[$field]; } return $return; } elseif ($type) { return isset($types[$type]) ? $types[$type] : array(); } else { return $types; } } /** * Invokes hook_notifications() with a single parameter or more but not needing * an object to be passed as reference. */ function notifications_module_information($op, $arg0 = NULL, $arg1 = NULL, $arg2 = NULL) { $object = NULL; return notifications_module_invoke($op, $arg0, $arg1, $arg2); } /** * Invokes hook_notifications() in every module. * * We cannot use module_invoke() for this, because the arguments need to * be passed by reference. */ function notifications_module_invoke($op, &$arg0, $arg1 = NULL, $arg2 = NULL) { $result = array(); foreach (module_implements('notifications') as $module) { $function = $module .'_notifications'; if ($return = $function($op, $arg0, $arg1, $arg2)) { $result = array_merge($result, $return); } } return $result; } /** * Implementation of hook_messaging() * * This hook provides information about the mensaje templates this module uses and related tokens. * * Depending on $op, this hook takes different parameters and returns different pieces of information: * * - 'message groups' * Get array of message groups, each of which will have one or more keys for different templates * Each group should have a unique key, so it should start with the module name * - 'message keys' * Get message template parts for a given group ($arg1) * Return array of key => name for each part * - 'messages' * Get default message templates for a given group ($arg1). * It should return default texts, indexed by message key that will be the default templates * These templates may be edited on the 'Messaging templates' page * - 'tokens' * Get available tokens for a given message group and key ($arg1). * Return array of token keys that will be available for this message templates * The tokens themselves may be default tokens (provided by token module) or we can add new * tokens implementing hook_token_list() and hook_token_value() * * @param $op * Operation, type of information to retrieve * @param $arg1, $arg2... * Different parameters depending on $op */ function notifications_messaging($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL, $arg4 = NULL) { switch ($op) { case 'message groups': // Generic notifications event $info['notifications-event'] = array( 'module' => 'notifications', 'name' => t('Notifications event'), ); $info['notifications-digest'] = array( 'module' => 'notifications', 'name' => t('Notifications digest'), ); return $info; case 'message keys': $type = $arg1; switch ($type) { case 'notifications-event': // Event notifications return array( 'subject' => t('Subject for event notifications'), 'header' => t('Header for event notifications'), 'main' => t('Content for event notifications'), 'footer' => t('Footer for event notifications'), ); case 'notifications-digest': return array( 'subject' => t('Subject for digested notifications'), 'header' => t('Header for digested notifications'), 'main' => t('Line for digested events'), 'footer' => t('Footer for digested notifications'), ); } break; case 'messages': $type = $arg1; // Event notifications if ($type == 'notifications-event') { return array( 'subject' => t('Event notification for [user] from [site-name]'), 'header' => t("Greetings [user],"), 'main' => t("A item to which you are subscribed has been updated"), 'footer' => array( t('This is an automatic message from [site-name]'), t('To manage your subscriptions, browse to [subscriptions-manage]'), t('You can unsubscribe at [unsubscribe-url]'), ), ); } // Digested messages if ($type == 'notifications-digest') { return array( 'subject' => t('[site-name] subscription update for [user]'), 'header' => t("Greetings, [user].\n\nThese are your messages"), 'main' => t("A [type] has been updated: [title]\n\n[event_list]"), 'footer' => array( t('This is an automatic message from [site-name]'), t('To manage your subscriptions, browse to [subscriptions-manage]'), ), ); } break; case 'tokens': $type = explode('-', $arg1); $tokens = array(); // These are the token groups that will be used for this module's messages if ($type[0] == 'notifications') { $tokens = array('global', 'subscription', 'user'); if ($type[1] == 'event') { $tokens[] = 'event'; } } return $tokens; } } /** * Implementation of hook_token_values() * * @ TODO: Work out event tokens */ function notifications_token_values($type, $object = NULL, $options = array()) { switch ($type) { case 'subscription': $values = array(); if ($subscription = $object) { $link = notifications_get_link('unsubscribe', array('sid' => $subscription->sid, 'signed' => TRUE, 'destination' => FALSE)); $values['unsubscribe-url'] = url($link['href'], $link['query'], NULL, TRUE); } return $values; case 'user': if ($account = $object) { return array( 'subscriptions-manage' => $account->uid ? url("user/$account->uid/notifications", NULL, NULL, TRUE) : '', ); } } } /** * Implementation of hook_token_list(). Documents the individual * tokens handled by the module. */ function notifications_token_list($type = 'all') { $tokens = array(); if ($type == 'user' || $type == 'all') { $tokens['user']['subscriptions-manage'] = t('The url for the current user to manage subscriptions.'); } if ($type == 'subscription' || $type == 'all') { $tokens['subscription']['unsubscribe-url'] = t('The url for disabling a specific subscription.'); } if ($type == 'event' || $type == 'all') { $tokens['event']['event-list'] = t('List of events for message digests'); $tokens['event']['event-detail'] = t('Detailed information for event'); } return $tokens; } /** * Get event types */ function notifications_event_types($type = NULL, $action = NULL) { static $info; if (!$info) { $types = notifications_module_information('event types'); foreach ($types as $type_info) { $info[$type_info['type']][$type_info['action']] = $type_info; } notifications_alter('event_types', $info); } if ($action) { return isset($info[$type][$action]) ? $info[$type][$action] : array(); } elseif ($type) { return isset($info[$type]) ? $info[$type] : array(); } else { return $info; } } /** * Implementation of hook_cron() */ function notifications_cron() { include_once drupal_get_path('module', 'notifications') .'/notifications.cron.inc'; notifications_process_run(); } /** * Return url for subscriptions */ function notifications_get_link($type, $params) { global $user; $params += array( 'uid' => $user->uid, 'confirm' => TRUE, 'signed' => FALSE, 'destination' => $_GET['q'], ); switch ($type) { case 'subscribe': $elements = array( 'subscribe', $params['uid'], $params['type'], implode(',', array_keys($params['fields'])), implode(',', $params['fields']) ); break; case 'unsubscribe': $elements = array( 'unsubscribe', $params['sid'] ); break; } // Build query string $query = array(); if ($params['destination']) { $query[] = 'destination='. $params['destination']; } if ($params['confirm']) { $query[] = 'confirm=1'; } if ($params['signed']) { $query[] = 'signature='. _notifications_signature($elements); } return array( 'href' => 'notifications/'. implode('/', $elements), 'query' => implode('&', $query), ); } /** * Check access to objects * * This will check permissions for subscriptions and events before subscribing * and before getting updates. * * @param $type * Type of object to check for access. Possible values: * - 'event', will check access to event objects * - 'subscription', will check access to subscribed objects */ function notifications_user_allowed($type, $account, $object = NULL) { // Invoke notifications hook and check for a FALSE return value $permissions = notifications_module_information('access', $type, $account, $object); if ($permissions) { return !in_array(FALSE, $permissions); } else { // If no module has anthing to say about access I guess it will be true return TRUE; } } /** * Implementation of notifications_hook() * * Check access permissions to subscriptions */ function notifications_notifications($op, &$arg0, $arg1 = NULL, $arg2 = NULL) { switch ($op) { case 'access': if ($arg0 == 'subscription') { $account = $arg1; $subscription = $arg2; // First we check valid subscription type $access = FALSE; if ($subscription->type && ($info = notifications_subscription_types($subscription->type))) { // To allow mixed subscription types to work we dont have a fixed field list // Then check specific access to this type. Each type must have a permission if (!empty($info['access callback'])) { $access = call_user_func($info['access callback'], $account, $subscription); } elseif (!empty($info['access']) && user_access($info['access'], $account)|| user_access('administer notifications', $account)) { // Check matching fields if (!array_diff($info['fields'], array_keys($subscription->fields))) { $access = TRUE; } } } return array($access); } } } /** * List of send intervals. These may be overriden in a variable. */ function _notifications_send_intervals() { return variable_get('notifications_send_intervals', array( -1 => t('Never'), 0 => t('Immediately'), 3600 => t('Every hour'), 43200 => t('Twice a day'), 86400 => t('Daily'), 604800 => t('Weekly'), ) ); } /** * List of send methods */ function _notifications_send_methods($account = NULL) { return variable_get('notifications_send_methods', messaging_method_list($account)); } /** * Signature for url parameters */ function _notifications_signature($params) { return md5('notifications:'. drupal_get_private_key() .':'. implode(':', $params)); } /** * Default values for subscription */ function _notifications_subscription_defaults($account = NULL) { return array( 'send_interval' => notifications_user_setting('send_interval', $account, 0), 'send_method' => notifications_user_setting('send_method', $account, ''), 'module' => 'notifications', 'status' => 1, ); } /** * Implementation of hook_simpletest() */ function notifications_simpletest() { $dir = drupal_get_path('module', 'notifications') . DIRECTORY_SEPARATOR .'tests'; $tests = file_scan_directory($dir, '\.test'); return array_keys($tests); } /** * Callback for module dependent data * * Some data stored in the notifications system is meant to be processed by other modules and * this is indicated by a module column in the data. * * This function calls the module function if available, defaulting to the notifications provided * function when not. The arguments are passed as is * * @param $module * Module name * @param $function * Function name in module */ function notifications_callback() { $args = func_get_args(); $module = array_shift($args); $function = array_shift($args); if ($module && function_exists($module .'_'. $function)) { $callback = $module .'_'. $function; } else { $callback = 'notifications_'. $function; } return call_user_func_array($callback, $args); } /** * This dispatch function hands off structured Drupal arrays to type-specific * _notifications_*_alter implementations. Modelled after Drupal 6 drupal_alter() * * @param $type * The data type of the structured array. 'form', 'links', * 'node_content', and so on are several examples. * @param $data * The structured array to be altered. * @param ... * Any additional params will be passed on to the called * hook_notifications_$type_alter functions. */ function notifications_alter($type, &$data) { // Now, use func_get_args() to pull in any additional parameters passed into // the drupal_alter() call. $args = array(&$data); $additional_args = func_get_args(); array_shift($additional_args); array_shift($additional_args); $args = array_merge($args, $additional_args); foreach (module_implements('notifications_'. $type .'_alter') as $module) { $function = $module .'_notifications_'. $type .'_alter'; call_user_func_array($function, $args); } } /** * PHP4 Compatibility * * These modules are being developed and tested with PHP 5. * * However, patches for PHP 4.x compatibility will be welcomed here * (as long as they don't break the PHP5 version or course). */ if (!function_exists('array_combine')) { function array_combine($arr1, $arr2) { $out = array(); $arr1 = array_values($arr1); $arr2 = array_values($arr2); foreach($arr1 as $key1 => $value1) { $out[(string)$value1] = $arr2[$key1]; } return $out; } } if (!function_exists('array_diff_key')) { function array_diff_key() { $arrs = func_get_args(); $result = array_shift($arrs); foreach ($arrs as $array) { foreach ($result as $key => $v) { if (array_key_exists($key, $array)) { unset($result[$key]); } } } return $result; } }