notifications.module in Notifications 5
Same filename and directory in other branches
Notifications module
This is the base module of the notifications framework. It handles event processing, queueing, message composition and sending.
Different subscriptions types are provided by plug-in modules implementing hook_notifications() Most of the UI is implemented in notifications_ui module The messaging framework is used for message delivery Token module is used for token replacement in messages
This is based on the previous subscriptions module
Development Seed, http://www.developmentseed.org, 2007
File
notifications.moduleView source
<?php
/**
* @file
* Notifications module
*
* This is the base module of the notifications framework. It handles event processing, queueing,
* message composition and sending.
*
* Different subscriptions types are provided by plug-in modules implementing hook_notifications()
* Most of the UI is implemented in notifications_ui module
* The messaging framework is used for message delivery
* Token module is used for token replacement in messages
*
* This is based on the previous subscriptions module
*
* Development Seed, http://www.developmentseed.org, 2007
*
*/
// Define some values for subscription status
// Blocked subscriptions, for blocked users
define('NOTIFICATIONS_SUBSCRIPTION_BLOCKED', 0);
// Enabled ones, will produce notifications
define('NOTIFICATIONS_SUBSCRIPTION_ACTIVE', 1);
// Temporarily disabled ones, maybe user on holidays
define('NOTIFICATIONS_SUBSCRIPTION_INACTIVE', 2);
/**
* Implementation of hook_menu().
*/
function notifications_menu($may_cache) {
global $user;
// we need the user to to build some urls
if ($may_cache) {
// Administration. Override messaging main item
$items[] = array(
'title' => 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/subscriptions',
'title' => t('Subscription types'),
'callback' => 'drupal_get_form',
'callback arguments' => 'notifications_admin_subscriptions_form',
'type' => MENU_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 'update':
if (isset($edit['status'])) {
if ($edit['status'] == 0) {
// user is being blocked now
// Delete pending notifications and block existing active subscriptions
db_query('DELETE FROM {notifications_queue} WHERE uid = %d', $user->uid);
db_query('UPDATE {notifications} SET status = %d WHERE status = %d AND uid = %d', NOTIFICATIONS_SUBSCRIPTION_BLOCKED, NOTIFICATIONS_SUBSCRIPTION_ACTIVE, $user->uid);
}
else {
// User may be being unblocked, unblock subscriptions if any
db_query('UPDATE {notifications} SET status = %d WHERE status = %d AND uid = %d', NOTIFICATIONS_SUBSCRIPTION_ACTIVE, NOTIFICATIONS_SUBSCRIPTION_BLOCKED, $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);
}
break;
}
}
/**
* Implementation of hook_form_alter()
* - Override title for messaging settings
*/
function notifications_form_alter($form_id, &$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(),
);
// 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) {
// Handle array values with IN conditions
if (is_array($value)) {
$placeholders = array_fill(0, count($value), "'%s'");
$query['where'][] = "f.field = '%s' AND f.value IN (" . implode(', ', $placeholders) . ")";
$query['args'][] = $field;
$query['args'] = array_merge($query['args'], $value);
}
else {
$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
* @param $default
* Default value when querying a specific field
*
* @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, $default = NULL) {
static $types;
if (!isset($types)) {
$types = notifications_module_information('subscription types');
// Variable overrides
if ($settings = variable_get('notifications_subscription_types', NULL)) {
foreach ($settings as $key => $value) {
// Check, because there may be settings for types that have been disabled later
if (isset($types[$key]) && is_array($value)) {
$types[$key] = array_merge($types[$key], $value);
}
}
}
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] = isset($info[$field]) ? $info[$field] : $default;
}
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'),
'description' => t('Fallback for all Notifications events.'),
);
$info['notifications-digest'] = array(
'module' => 'notifications',
'name' => t('Notifications digest'),
'description' => t('Common parts for Notifications digests.'),
);
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'),
'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() {
require_once drupal_get_path('module', 'notifications') . '/notifications.cron.inc';
notifications_process_run();
}
/**
* Return url for subscriptions. Confirmation form is not optional.
*/
function notifications_get_link($type, $params) {
global $user;
$params += array(
'uid' => $user->uid,
'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['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,
);
}
break;
case 'digest methods':
// Return array of digesting engines
$info['short'] = array(
'type' => 'short',
'name' => t('Short'),
'description' => t('Produces one line per event, grouped by object'),
'digest callback' => 'notifications_process_digest_short',
);
$info['long'] = array(
'type' => 'long',
'name' => t('Long'),
'description' => t('Adds full information for each event'),
'digest callback' => 'notifications_process_digest_long',
);
return $info;
}
}
/**
* 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'),
));
}
/**
* Information about digesting method for a send interval.
*
* @return array()
* Ditest information for that interval, or all the information if no interval
*/
function notifications_digest_method($send_interval = NULL, $refresh = FALSE) {
static $digest_methods, $intervals;
if (!isset($digest_methods) || $refresh) {
// Method information
foreach (notifications_module_information('digest methods') as $method) {
$digest_methods[$method['type']] = $method;
}
// Mapping interval -> method
$intervals = variable_get('notifications_digest_methods', array());
}
if (is_null($send_interval)) {
return $digest_methods;
}
elseif (!empty($intervals[$send_interval]) && isset($digest_methods[$intervals[$send_interval]])) {
return $digest_methods[$intervals[$send_interval]];
}
else {
// Default, that will be 'short' if interval > 0, none otherwise
return $send_interval > 0 ? $digest_methods['short'] : NULL;
}
}
/**
* 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,
);
}
/**
* 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;
}
}
Functions
Name | Description |
---|---|
notifications_alter | This dispatch function hands off structured Drupal arrays to type-specific _notifications_*_alter implementations. Modelled after Drupal 6 drupal_alter() |
notifications_callback | Callback for module dependent data |
notifications_cron | Implementation of hook_cron() |
notifications_delete_subscription | Delete subscription and clean up related data. |
notifications_delete_subscriptions | Delete multiple subscriptions and clean up related data (pending notifications, fields). |
notifications_digest_method | Information about digesting method for a send interval. |
notifications_event | Process subscriptions events |
notifications_event_save | Stores new events |
notifications_event_types | Get event types |
notifications_exit | Implementation of hook_exit() |
notifications_form_alter | Implementation of hook_form_alter() |
notifications_get_link | Return url for subscriptions. Confirmation form is not optional. |
notifications_get_subscriptions | Get subscriptions that fit a set of conditions. |
notifications_load_subscription | Get an individual subscription. |
notifications_menu | Implementation of hook_menu(). |
notifications_messaging | Implementation of hook_messaging() |
notifications_module_information | Invokes hook_notifications() with a single parameter or more but not needing an object to be passed as reference. |
notifications_module_invoke | Invokes hook_notifications() in every module. |
notifications_notifications | Implementation of notifications_hook() |
notifications_perm | Implementation of hook_perms() |
notifications_query_build | Query builder for subscriptions |
notifications_queue | Queue events for notifications |
notifications_save_subscription | Update or create subscription |
notifications_subscription_types | Get info about subscription types |
notifications_token_list | Implementation of hook_token_list(). Documents the individual tokens handled by the module. |
notifications_token_values | Implementation of hook_token_values() |
notifications_user | Implementation of hook_user(). |
notifications_user_allowed | Check access to objects |
notifications_user_get_subscriptions | Get subscription for a given user |
notifications_user_setting | Gets a user setting, defaults to default system setting for each |
_notifications_send_intervals | List of send intervals. These may be overriden in a variable. |
_notifications_send_methods | List of send methods |
_notifications_signature | Signature for url parameters |
_notifications_subscription_defaults | Default values for subscription |