notifications.module in Notifications 6
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() {
// Administration. This one will override messaging menu item
$items['admin/messaging'] = array(
'title' => 'Messaging & Notifications',
'access arguments' => array(
'administer notifications',
),
'description' => 'Administer and configure messaging and notifications',
'page callback' => 'system_admin_menu_block_page',
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
);
$items['admin/messaging/subscriptions'] = array(
'title' => 'Manage subscriptions',
'description' => 'Manage existing subscriptions and queue.',
'page callback' => 'notifications_admin_status_page',
'access arguments' => array(
'administer notifications',
),
'file' => 'notifications.admin.inc',
);
$items['admin/messaging/subscriptions/overview'] = array(
'title' => 'Overview',
'description' => 'Subscriptions overview.',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'file' => 'notifications.admin.inc',
);
$items['admin/messaging/subscriptions/admin'] = array(
'title' => 'Administer',
'description' => 'Administer subscriptions.',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_manage_admin_subscriptions',
),
'access arguments' => array(
'administer notifications',
),
'file' => 'notifications.manage.inc',
);
$items['admin/messaging/subscriptions/queue'] = array(
'title' => 'Queue',
'description' => 'Notifications queue.',
'page callback' => 'notifications_admin_queue',
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer notifications',
),
'file' => 'notifications.admin.inc',
);
// Site settings
$items['admin/messaging/notifications'] = array(
'title' => 'Notifications Settings',
'description' => 'Site settings for user notifications.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_settings_form',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'notifications.admin.inc',
);
$items['admin/messaging/notifications/settings'] = array(
'title' => 'General',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'notifications.admin.inc',
);
$items['admin/messaging/notifications/intervals'] = array(
'title' => 'Intervals',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_send_intervals_form',
),
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer site configuration',
),
'file' => 'notifications.admin.inc',
);
$items['admin/messaging/notifications/events'] = array(
'title' => 'Events',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_admin_events_form',
),
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer site configuration',
),
'file' => 'notifications.admin.inc',
);
// Subscribe links. For this items access will be checked later in the page
$items['notifications/subscribe/%user'] = array(
'type' => MENU_CALLBACK,
'page callback' => 'notifications_page_subscribe',
'page arguments' => array(
2,
),
'access callback' => 'notifications_access_subscribe',
'access arguments' => array(
2,
),
'file' => 'notifications.pages.inc',
);
// Unsubscribe links This page will need to work with anonymous users
$items['notifications/unsubscribe'] = array(
'type' => MENU_CALLBACK,
'page callback' => 'notifications_page_unsubscribe',
'page arguments' => array(
2,
3,
),
'access callback' => TRUE,
'file' => 'notifications.pages.inc',
);
// Edit subscription
$items['notifications/subscription/%notifications_subscription'] = array(
'type' => MENU_CALLBACK,
'title' => 'Subscription',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_subscription_form',
2,
),
'access callback' => 'notifications_subscription_access',
'access arguments' => array(
'edit',
2,
),
'file' => 'notifications.pages.inc',
);
$items['user/%user/notifications'] = array(
'type' => MENU_LOCAL_TASK,
'title' => 'Notifications',
//'page callback' => 'notifications_page_user_overview',
//'page arguments' => array(1),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_user_overview',
1,
),
'access callback' => 'notifications_access_user',
'access arguments' => array(
1,
),
'file' => 'notifications.pages.inc',
);
$items['user/%user/notifications/overview'] = array(
'type' => MENU_DEFAULT_LOCAL_TASK,
'title' => 'Overview',
'weight' => -10,
);
$items['user/%user/notifications/subscriptions'] = array(
'type' => MENU_LOCAL_TASK,
'title' => 'Subscriptions',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_manage_user_subscriptions',
1,
),
'access callback' => 'notifications_access_user',
'access arguments' => array(
1,
'manage',
),
'file' => 'notifications.manage.inc',
);
// Some autocomplete callbacks
$items['notifications/autocomplete/node/title'] = array(
'title' => 'Node title autocomplete',
'page callback' => 'notifications_node_autocomplete_title',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
'file' => 'notifications.node.inc',
);
// Some autocomplete callbacks
$items['notifications/autocomplete/node/type'] = array(
'title' => 'Node title autocomplete',
'page callback' => 'notifications_node_autocomplete_type',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
'file' => 'notifications.node.inc',
);
return $items;
}
/**
* Menu access callback for user subscriptions
*
* @param $account
* User account to which these subscriptions below
* @param $op
* - maintain = create / delete
* - manage = use the per account administration page
*/
function notifications_access_user($account, $op = 'maintain') {
global $user;
if (user_access('administer notifications') || user_access('manage all subscriptions')) {
return TRUE;
}
else {
return $account->uid && $user->uid == $account->uid && ($op == 'maintain' && user_access('maintain own subscriptions') || $op == 'manage' && user_access('manage own subscriptions'));
}
}
/**
* Menu access callback, add a given subscription type
*/
function notifications_access_user_add($account = NULL, $type = NULL) {
global $user;
$account = $account ? $account : $user;
if (notifications_access_user($account)) {
if ($type && ($access = notifications_subscription_types($type, 'access'))) {
return user_access($access, $account);
}
else {
return TRUE;
}
}
}
/**
* Menu access callback for subscribe links
*
* More access checking depending on subscription type will be done at the destination page
*/
function notifications_access_subscribe($account) {
global $user;
if (user_access('administer notifications') || user_access('manage all subscriptions')) {
return TRUE;
}
return $account && $account->uid && $user->uid == $account->uid && user_access('maintain own subscriptions');
}
/**
* Menu loading, subscription
*/
function notifications_subscription_load($sid) {
return notifications_load_subscription($sid);
}
/**
* Menu access callback
*/
function notifications_subscription_access($op, $subscription, $account = NULL) {
global $user;
$account = $account ? $account : $user;
if (user_access('administer notifications') || user_access('manage all subscriptions')) {
return TRUE;
}
switch ($op) {
case 'edit':
case 'unsubscribe':
return $account->uid && $subscription->uid == $account->uid && user_access('maintain own subscriptions');
}
return FALSE;
}
/**
* Implementation of hook_perms()
*
* This module defines the following permissions
* - administer notifications = Full access to all administration for the module
* - maintain own subscriptions = Create / delete own subscriptions
* - manage own subscriptions = Access the subscriptions management tab
* - manage all subscriptions = Administer other users subscriptions
*/
function notifications_perm() {
return array(
'administer notifications',
'maintain own subscriptions',
'manage own subscriptions',
'manage all 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;
}
}
/**
* Implementation of hook_form_alter()
*/
function notifications_form_alter(&$form, $form_state, $form_id) {
switch ($form_id) {
// Default send interval for user form
case 'user_profile_form':
if ($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
$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 adding query conditions from plug-ins
*
* This is an example of the resulting query
*
* INSERT INTO {notifications_queue} (uid, sid, module, eid, send_interval, send_method, cron, created, conditions)
* SELECT DISTINCT s.uid, s.sid, s.module, 34, s.send_interval, s.send_method, s.cron, 1230578161, s.conditions FROM notifications s
* INNER JOIN notifications_fields f ON s.sid = f.sid
* WHERE s.status = 1 AND s.event_type = 'node' AND s.send_interval >= 0
* AND ((f.field = 'nid' AND f.value = '2') OR (f.field = 'type' AND f.value = 'story') OR (f.field = 'author' AND f.value = '1'))
* GROUP BY s.uid, s.sid, s.module, s.send_interval, s.send_method, s.cron, s.conditions
* HAVING s.conditions = count(f.sid)
*
* @param $event
* Event array.
*/
function notifications_queue($event) {
global $notifications_send_immediate;
$query = array();
// Build big insert query using the query builder. The fields for this event type will be added by the plug-ins.
// If no arguments retrieved, skip this step
if ($query_args = notifications_module_information('query', 'event', $event->type, $event)) {
$query['insert'] = array(
'uid',
'sid',
'module',
'eid',
'send_interval',
'send_method',
'cron',
'created',
'conditions',
);
$query['into'] = '{notifications_queue}';
$query['distinct'] = TRUE;
$query['select'] = array(
's.uid',
's.sid',
's.module',
'%d',
's.send_interval',
's.send_method',
's.cron',
'%d',
's.conditions',
);
$query['from'] = array(
'{notifications} s',
);
$query['select args'] = array(
$event->eid,
$event->created,
);
$query['join'] = array(
'INNER JOIN {notifications_fields} f ON s.sid = f.sid',
);
$query['where'] = array(
's.status = 1',
"s.event_type = '%s'",
's.send_interval >= 0',
);
$query['where args'] = array(
$event->type,
);
// Add one more condition if we don't send notifications on own posts
if (!variable_get('notifications_sendself', 0) && !empty($event->uid)) {
$query['where'][] = 's.uid <> %d';
$query['where args'][] = $event->uid;
}
// Some group by fields are not really needed but added for pgsql compatibility
$query['group'] = array(
's.uid',
's.sid',
's.module',
's.send_interval',
's.send_method',
's.cron',
's.conditions',
);
// We throw in all the conditions and check the number of matching conditions
// that must be equal to the subscription conditions number
$query['having'] = array(
's.conditions = count(f.sid)',
);
// We add parameters for each module separately
foreach ($query_args as $query_params) {
$query = notifications_query_build($query_params, $query);
}
// Give a chance to other modules to alter the query or empty it so we don't throw it
drupal_alter('notifications_query', $query);
// Finally we build the SELECT part of the query and glue it to the INSERT
if ($query) {
list($sql, $args) = notifications_query_sql($query);
db_query($sql, $args);
}
}
// Modules can do cleanup operations or modify the queue
notifications_module_invoke('event queued', $event, $query);
// Now update event counter with rows in notifications_queue or delete if no rows
if ($count = db_result(db_query('SELECT COUNT(*) FROM {notifications_queue} WHERE eid = %d', $event->eid))) {
db_query('UPDATE {notifications_event} SET counter = %d WHERE eid = %d', $count, $event->eid);
// If immediate sending enabled, store eid for sending on page exit.
if (variable_get('notifications_send_immediate', 0)) {
$notifications_send_immediate[] = $event->eid;
}
}
else {
db_query('DELETE FROM {notifications_event} WHERE eid = %d', $event->eid);
}
}
/**
* Implementation of hook_exit()
*
* This is where the immediate 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_immediate;
if (!empty($notifications_send_immediate)) {
require_once drupal_get_path('module', 'notifications') . '/notifications.cron.inc';
foreach ($notifications_send_immediate as $eid) {
notifications_process_rows(array(
'cron' => 1,
'eid' => $eid,
'send_interval' => 0,
));
}
}
}
/**
* Query builder for subscriptions
*
* This adds up query elements into a big array so they can be later rendered as SQL
*
* @see notifications_query_sql()
*
* @param $params
* Array of query conditions
* @param $query
* Base query to build upon
*/
function notifications_query_build($params, $query = array()) {
foreach ($params as $name => $elements) {
if ($name == 'fields') {
// Fields elements have some special handling, they have the form: field => value
foreach ($elements as $field => $value) {
// Use field definition provided by hook_notifications('subscription fields') and handle array values with IN conditions
// Workaround to have a valid one because not all modules provide the information yet (og?)
if (notifications_subscription_fields($field, 'type') == 'int') {
$type = 'int';
$fieldval = 'intval';
}
else {
$type = 'char';
$fieldval = 'value';
}
if (is_array($value)) {
$query['fields'][] = "f.field = '%s' AND f.{$fieldval} IN (" . db_placeholders($value, $type) . ")";
$query['fields args'][] = $field;
$query['fields args'] = empty($query['fields args']) ? $value : array_merge($query['fields args'], $value);
}
else {
$query['fields'][] = "f.field = '%s' AND f.{$fieldval} = " . db_type_placeholder($type);
$query['fields args'][] = $field;
$query['fields args'][] = $value;
}
}
}
else {
if ($name == 'fields sql') {
// These are added as 'fields' parameters without further parsing
$name = 'fields';
}
if (is_array($elements)) {
$query[$name] = empty($query[$name]) ? $elements : array_merge($query[$name], $elements);
}
else {
$query[$name][] = $elements;
}
}
}
return $query;
}
/**
* Build the SQL statement from query elements
*
* It will build INSERT + SELECT or SELECT queries from its elements
*
* @return array()
* list($sql, $args);
*/
function notifications_query_sql($query) {
$sql = '';
if (!empty($query['insert'])) {
$sql .= 'INSERT INTO ' . $query['into'] . '(' . implode(', ', $query['insert']) . ') ';
}
$sql .= !empty($query['distinct']) ? 'SELECT DISTINCT ' : 'SELECT ';
$sql .= implode(', ', $query['select']);
$sql .= ' FROM ' . implode(', ', $query['from']);
if (!empty($query['join'])) {
$sql .= ' ' . implode(' ', $query['join']);
}
// Where conditions come from 'where' and 'fields' elements
// Field conditions are OR'd and added into the other conditions
$where = !empty($query['where']) ? $query['where'] : array();
if (!empty($query['fields'])) {
$where[] = '(' . implode(') OR (', $query['fields']) . ')';
}
if ($where) {
$sql .= ' WHERE (' . implode(') AND (', $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',
'fields',
'having',
) as $key) {
if (!empty($query[$key . ' args'])) {
$args = array_merge($args, $query[$key . ' args']);
}
}
return array(
$sql,
$args,
);
}
/**
* Helper function for the query builder
*/
/*
function _notifications_query_sql($type, $query) {
if (!empty($query[$type])) {
switch ($type) {
case 'select':
return implode()
}
} else {
return '';
}
}
*/
/**
* Stores new events
*
* @param $event
* Event object
* @TODO: Avoid to and from array conversion
*/
function notifications_event_save(&$event) {
$event = (array) $event;
db_query("INSERT INTO {notifications_event} (module, type, action, uid, created, params) VALUES ('%s', '%s', '%s', %d, %d, '%s')", $event['module'], $event['type'], $event['action'], $event['uid'], $event['created'], serialize($event['params']));
$event['eid'] = db_last_insert_id('notifications_event', 'eid');
$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(
'select' => array(
's.*',
'f.*',
),
'from' => array(
'{notifications} s',
),
'join' => array(
'INNER JOIN {notifications_fields} f ON s.sid = f.sid',
),
'where' => array(
's.uid = %d',
"s.event_type = '%s'",
),
'where 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
list($sql, $args) = notifications_query_sql($query);
dsm($sql);
dsm($args);
$result = db_query($sql, $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
* @return integer
* Failure to write a record will return FALSE. Otherwise SAVED_NEW or SAVED_UPDATED is returned depending on the operation performed.
*/
function notifications_save_subscription(&$subscription) {
global $user;
$result = FALSE;
$subscription = (object) $subscription;
$subscription->conditions = count($subscription->fields);
$account = $subscription->uid ? messaging_load_user($subscription->uid) : $user;
// Default values for fields: send_interval, send_method, cron, etc...
foreach (_notifications_subscription_defaults($account) as $field => $value) {
if (!isset($subscription->{$field})) {
$subscription->{$field} = $value;
}
}
// Fill in event type if not set
if (empty($subscription->event_type)) {
$subscription->event_type = notifications_subscription_types($subscription->type, 'event_type');
}
if (!empty($subscription->sid)) {
$op = 'update';
$result = drupal_write_record('notifications', $subscription, 'sid');
}
elseif ($duplicate = notifications_get_subscriptions(array(
'uid' => $subscription->uid,
'type' => $subscription->type,
'event_type' => $subscription->event_type,
'module' => $subscription->module,
'send_method' => $subscription->send_method,
'send_interval' => $subscription->send_interval,
), $subscription->fields, TRUE)) {
// We've found duplicates, resolve conflict updating first, deleting the rest
// It is possible that we had a disabled one, this updating will fix it
$update = array_shift($duplicate);
unset($subscription->sid);
// It may be 0
foreach ($subscription as $key => $value) {
if (isset($value)) {
$update->{$key} = $value;
}
}
$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';
$result = drupal_write_record('notifications', $subscription);
}
// If the operation has worked so far, update fields and inform other modules
if ($result !== FALSE) {
if ($op == 'update') {
db_query("DELETE FROM {notifications_fields} WHERE sid = %d", $subscription->sid);
}
// There may be subscriptions with no fields, some people are coding such plug-ins.
if (!empty($subscription->fields)) {
foreach ($subscription->fields as $name => $value) {
if (is_array($value)) {
db_query("INSERT INTO {notifications_fields} (sid, field, value, intval) VALUES(%d, '%s', '%s', %d)", $subscription->sid, $value['type'], $value['value'], (int) $value['value']);
}
else {
db_query("INSERT INTO {notifications_fields} (sid, field, value, intval) VALUES(%d, '%s', '%s', %d)", $subscription->sid, $name, $value, (int) $value);
}
}
}
notifications_module_invoke($op, $subscription);
}
return $result;
}
/**
* 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: If !$limit, 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
* @param $limit
* Whether to limit the result to subscriptions with exactly that condition fields
*/
function notifications_delete_subscriptions($params, $conditions = array(), $limit = FALSE) {
// Build query conditions using the query builder
$query = notifications_subscriptions_query_build($params, $conditions, $limit);
$query['select'][] = 'n.sid';
list($sql, $args) = notifications_query_sql($query);
// Query notifications that meet these conditions and build an array
//$result = db_query('SELECT n.sid FROM {notifications} n '. implode(' ', $query['join']) .' WHERE '. implode(' AND ', $query['where']), $query['args']);
$result = db_query($sql, $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})");
}
}
}
/**
* Query builder for subscriptions
*
* Builds queries for 'notifications' and 'notifications_fields' tables using schema
* and fields (subscription fields) information.
*
* @param array $params
* Array of multiple conditions in the notifications table.
* @param array $conditions
* Array of multiple conditions in the notifications_fields table. The array elements may be
* - single field => value pairs
* - or key => array('type' => field, 'value' => value)
* If value is null, it just checks that a condition for the given field type exists
* @param $limit
* Whether to limit the result to subscriptions with exactly that condition fields
*
* @return array()
* Structured array with 'join', 'where', 'args' elements
*/
function notifications_subscriptions_query_build($params, $conditions = array(), $limit = FALSE) {
$join = $where = $args = array();
$schema = drupal_get_schema('notifications');
// If we limit this query to the number of conditions add a new param
if ($limit && $conditions) {
$params += array(
'conditions' => count($conditions),
);
}
// Add conditions for main notifications table
foreach ($params as $field => $value) {
if (in_array($schema['fields'][$field]['type'], array(
'serial',
'int',
))) {
$where[] = 'n.' . $field . " = %d";
}
else {
$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 $key => $data) {
if (is_array($data)) {
$field = $data['type'];
$value = $data['value'];
}
else {
$field = $key;
$value = $data;
}
$alias = 'nf' . $index++;
$join[] = "INNER JOIN {notifications_fields} {$alias} ON n.sid = {$alias}.sid";
$where[] = "{$alias}.field = '%s'";
$args[] = $field;
// If null value, do not check value, we just check that a condition for this field type exists
if (!is_null($value)) {
if (notifications_subscription_fields($field, 'type') == 'int') {
$where[] = "{$alias}.intval = %d";
}
else {
$where[] = "{$alias}.value = '%s'";
}
$args[] = $value;
}
}
}
// Return query array
return array(
'from' => array(
'{notifications} n',
),
'join' => $join,
'where' => $where,
'args' => $args,
);
}
/**
* 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 exactly that condition fields
* @param $key
* Optional key field to use as the array index. Will default to sid
* @param $pager
* Whether to throw a pager query
* @return
* Array of subscriptions indexed by uid, module, field, value, author
*
* @todo Check field types for building the query
*/
function notifications_get_subscriptions($params, $conditions = array(), $limit = TRUE, $key = 'sid', $pager = NULL) {
// Build query conditions using the query builder
$query = notifications_subscriptions_query_build($params, $conditions, $limit);
$query['select'][] = 'n.*';
list($sql, $args) = notifications_query_sql($query);
if ($pager) {
$sql .= ' ORDER BY n.sid';
$result = pager_query($sql, $pager, 0, NULL, $args);
}
else {
$result = db_query($sql, $args);
}
$subscriptions = array();
while ($subs = db_fetch_object($result)) {
$subscriptions[$subs->{$key}] = notifications_load_subscription($subs);
}
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');
drupal_alter('notifications_subscription_types', $types);
}
return notifications_info($types, $type, $field);
}
/**
* Get information about subscriptions fields
*/
function notifications_subscription_fields($type = NULL, $property = NULL) {
static $fields;
if (!isset($fields)) {
$fields = notifications_module_information('subscription fields');
drupal_alter('notifications_subscription_fields', $fields);
}
return notifications_info($fields, $type, $property);
}
/**
* Get information from an array of data
*/
function notifications_info(&$data, $type = NULL, $field = NULL) {
if ($field && $type) {
return isset($data[$type][$field]) ? $data[$type][$field] : NULL;
}
elseif ($field) {
$return = array();
foreach ($data as $id => $info) {
$return[$id] = $info[$field];
}
return $return;
}
elseif ($type) {
return isset($data[$type]) ? $data[$type] : array();
}
else {
return $data;
}
}
/**
* 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) {
static $digest_methods, $intervals;
if (!isset($digest_methods)) {
// 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;
}
}
/**
* 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 types':
$info['notifications'] = array(
'name' => t('Notifications'),
'description' => t('Messages coming from user subscriptions and system events'),
);
return $info;
case 'message groups':
// Generic notifications event
$info['notifications-event'] = array(
'module' => 'notifications',
'name' => t('Notifications event'),
'description' => t('Common parts for all Notifications messages for a single event. This is useful for defining a common header and/or footer for all these messages.'),
);
$info['notifications-digest'] = array(
'module' => 'notifications',
'name' => t('Notifications digest'),
'description' => t('Depending on your settings for each Send interval, Notifications may be digested, this is grouped and summarized in a single message. These are the common parts for Notifications digests.'),
);
return $info;
case 'message keys':
$type = $arg1;
switch ($type) {
case 'notifications-event':
// Event notifications
return array(
'subject' => t('Subject'),
'header' => t('Header'),
'main' => t('Content'),
'footer' => t('Footer'),
);
case 'notifications-digest':
return array(
'subject' => t('Subject'),
'header' => t('Header'),
'main' => t('Line for digested events'),
'footer' => t('Footer'),
);
}
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(
'subscription',
'user',
);
if ($type[1] == 'event') {
$tokens[] = 'event';
}
}
return $tokens;
case 'method update':
// A messaging method has been disabled ($arg1) and replaced by the new one ($arg2)
// Update subscriptions
db_query("UPDATE {notifications} SET send_method = '%s' WHERE send_method = '%s'", $arg2, $arg1);
// Purge notifications queue, we may lost some notifications but it's the safest option.
db_query("DELETE FROM {notifications_queue} WHERE send_method = '%s'", $arg1);
break;
}
}
/**
* 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,
'absolute' => TRUE,
));
$values['unsubscribe-url'] = url($link['href'], $link['options']);
}
return $values;
case 'user':
if ($account = $object) {
$values['subscriptions-manage'] = $account->uid ? url("user/{$account->uid}/notifications", array(
'absolute' => TRUE,
)) : '';
$link = notifications_get_link('unsubscribe', array(
'uid' => $account->uid,
'signed' => TRUE,
'destination' => FALSE,
'absolute' => TRUE,
));
$values['unsubscribe-url-global'] = url($link['href'], $link['options']);
return $values;
}
}
}
/**
* 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.');
$tokens['user']['unsubscribe-url-global'] = t('The url to allow a user to delete all their 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;
}
drupal_alter('notifications_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 link array for subscriptions
*
* @param $type
* Link type: 'subscribe' | 'unsubscribe'
* @param $params
* Aditional parameters for the subscription, may be
* - uid, the user for which the link is generated
* - confirm, whether to show confirmation page or not
* - signed, to produce a signed link that can be used by anonymous users (Example: unsubscribe link in emails)
* - Other subscription parameters: type, fields...
*/
function notifications_get_link($type, $params) {
global $user;
$params += array(
'uid' => $user->uid,
'confirm' => TRUE,
'signed' => FALSE,
'destination' => $_GET['q'],
'query' => array(),
);
switch ($type) {
case 'subscribe':
$elements = array(
'subscribe',
$params['uid'],
$params['type'],
implode(',', array_keys($params['fields'])),
implode(',', $params['fields']),
);
break;
case 'unsubscribe':
// The unsubscribe link can be for a single subscription or all subscriptions for a user
if (!empty($params['sid'])) {
$elements = array(
'unsubscribe',
'sid',
$params['sid'],
);
}
elseif (!empty($params['uid'])) {
$elements = array(
'unsubscribe',
'uid',
$params['uid'],
);
}
break;
}
// Build query string using named parameters
$query = $params['query'];
if ($params['destination']) {
$query['destination'] = $params['destination'];
}
// To skip the confirmation form, the link must be signed
// Note tat the query string will be 'confirm=1' to skip confirmation form
if (!$params['confirm']) {
$query['confirm'] = 1;
$params['signed'] = 1;
}
if ($params['signed']) {
$query['signature'] = _notifications_signature($elements, !$params['confirm']);
}
// Build final link parameters
$options['query'] = $query;
foreach (array(
'absolute',
'html',
) as $name) {
if (isset($params[$name])) {
$options[$name] = $params[$name];
}
}
return array(
'href' => 'notifications/' . implode('/', $elements),
'options' => $options,
);
}
/**
* 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'),
));
}
/**
* List of send methods
*/
function _notifications_send_methods($account = NULL) {
return variable_get('notifications_send_methods', messaging_method_list($account));
}
/**
* Signature for url parameters
*
* @param $params
* Subscription parameters
* @param $skip_confirm
* TRUE to skip confirmation form
*/
function _notifications_signature($params, $skip_confirm = FALSE) {
return md5('notifications:' . drupal_get_private_key() . ':' . ($skip_confirm ? 1 : 0) . ':' . 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' => NOTIFICATIONS_SUBSCRIPTION_ACTIVE,
'destination' => '',
'cron' => 1,
);
}
/**
* Status list
*/
function _notifications_subscription_status() {
return array(
NOTIFICATIONS_SUBSCRIPTION_ACTIVE => t('active'),
NOTIFICATIONS_SUBSCRIPTION_BLOCKED => t('blocked'),
NOTIFICATIONS_SUBSCRIPTION_INACTIVE => t('inactive'),
);
}
/**
* Build list of subscription types
*
* Note: some custom types may have user defined strings, that's why the check_plain() everywhere
*/
function _notifications_subscription_types($format = 'short', $filter = NULL) {
$options = array();
foreach (notifications_subscription_types() as $type => $info) {
if (!$filter || count(array_intersect_assoc($filter, $info)) == count($filter)) {
switch ($format) {
case 'short':
$options[$type] = check_plain($info['title']);
break;
case 'long':
$options[$type] = '<strong>' . check_plain($info['title']) . '</strong>.';
if (!empty($info['description'])) {
$options[$type] .= ' ' . check_plain($info['description']);
}
break;
}
}
}
return $options;
}
/**
* 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);
}
/**
* Generic subscriptions content form
*
* Builds a form for a user to manage its own subscriptions with
* some generic parameters
*
* Currently it only manages simple condition fields
* @param $account
* User account
* @param $type
* Subscription type
* @param $subscriptions
* Current subscriptions of this type. If null they'll be loaded
* @param $list
* Array with available subscriptions indexed by field value
* @param $defaults
* Default value for subscriptions
* @param $options
* Optional, array of aditional options for the form
*/
function notifications_user_form($form_state, $account, $type, $subscriptions, $list, $defaults, $options = array()) {
// Complete defaults
$info = notifications_subscription_types($type);
$field = $info['fields'][0];
$field_title = !empty($options['title']) ? $options['title'] : '';
if (is_null($subscriptions)) {
// Fetch subscriptions with given parameters
$subscriptions = notifications_get_subscriptions(array(
'type' => $type,
'event_type' => $info['event_type'],
'uid' => $account->uid,
), TRUE, 'value');
}
$defaults += array(
'sid' => 0,
'type' => $type,
'event_type' => $info['event_type'],
);
$defaults += _notifications_subscription_defaults($account);
// Hide Send method column if only one
$send_methods = _notifications_send_methods();
$header = array(
theme('table_select_header_cell'),
$field_title,
t('Send interval'),
);
if (count($send_methods) > 1) {
$header[] = t('Send method');
}
$form['defaults'] = array(
'#type' => 'value',
'#value' => $defaults,
);
$form['account'] = array(
'#type' => 'value',
'#value' => $account,
);
$form['current'] = array(
'#type' => 'value',
'#value' => $subscriptions,
);
$form['subscription_fields'] = array(
'#type' => 'value',
'#value' => array(),
);
$form['subscriptions'] = array(
'#tree' => TRUE,
'#theme' => 'notifications_form_table',
'#header' => $header,
);
foreach ($list as $key => $title) {
$rowdefaults = isset($subscriptions[$key]) ? (array) $subscriptions[$key] : $defaults;
$rowdefaults += $rowdefaults;
$form['subscriptions']['checkbox'][$key] = array(
'#type' => 'checkbox',
'#default_value' => $rowdefaults['sid'],
);
$form['subscriptions']['title'][$key] = array(
'#value' => $title,
);
$form['subscriptions']['send_interval'][$key] = array(
'#type' => 'select',
'#options' => _notifications_send_intervals(),
'#default_value' => $rowdefaults['send_interval'],
);
// Hide send methods if only one available
if (count($send_methods) > 1) {
$form['subscriptions']['send_method'][$key] = array(
'#type' => 'select',
'#options' => _notifications_send_methods(),
'#default_value' => $rowdefaults['send_method'],
);
}
else {
$form['subscriptions']['send_method'][$key] = array(
'#type' => 'value',
'#value' => $rowdefaults['send_method'],
);
}
// Pass on the fields for processing
$form['subscription_fields']['#value'][$key] = array(
$field => $key,
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Process generic form submission
*/
function notifications_user_form_submit($form, &$form_state) {
$form_values = $form_state['values'];
$account = $form_values['account'];
$current = $form_values['current'];
$defaults = $form_values['defaults'];
$defaults += array(
'uid' => $account->uid,
);
$fields = $form_values['subscription_fields'];
$values = $form_values['subscriptions'];
$check = 'checkbox';
foreach ($values[$check] as $index => $value) {
$subscription = NULL;
if ($value) {
// Checked, save only if new or changed
if (!isset($current[$index])) {
$subscription = $defaults;
}
elseif ($current[$index]->send_interval != $values['send_interval'][$index] || $current[$index]->send_method != $values['send_method'][$index]) {
$subscription = (array) $current[$index];
}
// Complete and save
if ($subscription) {
$subscription['send_interval'] = $values['send_interval'][$index];
$subscription['send_method'] = $values['send_method'][$index];
$subscription['fields'] = $fields[$index];
notifications_save_subscription($subscription);
}
}
elseif (isset($current[$index])) {
notifications_delete_subscription($current[$index]->sid);
}
}
}
/**
* Display a form field for a notifications_field
*/
function notifications_subscription_form_field($type, $value = NULL) {
$info = notifications_subscription_fields($type);
if (!empty($info['options callback'])) {
$element['#type'] = 'select';
$element['#options'] = call_user_func($info['options callback']);
}
elseif (!empty($info['autocomplete path'])) {
$element['#type'] = 'textfield';
$element['#autocomplete_path'] = $info['autocomplete path'];
if ($value) {
if (!empty($info['autocomplete callback'])) {
$value = call_user_func($info['autocomplete callback'], $value);
}
elseif (!empty($info['format callback'])) {
$value = call_user_func($info['format callback'], $value, FALSE);
}
}
}
else {
$element['#type'] = 'textfield';
if ($value) {
$value = check_plain($value);
}
}
if ($value) {
$element['#default_value'] = $value;
}
return $element;
}
/**
* Format subscription for display
*
* @return array()
* Array with type_name, field_names (array), field_values (array)
*/
function notifications_format_subscription($subscription, $format = 'short', $html = TRUE) {
// Build array and add subscription type name
$type_name = notifications_subscription_types($subscription->type, 'title');
$names = $values = array();
// Format every field separately
foreach ($subscription->fields as $key => $data) {
if (is_array($data)) {
$item = notifications_format_subscription_field($data['type'], $data['value'], $html);
}
else {
$item = notifications_format_subscription_field($key, $data, $html);
}
$names[$key] = $item['name'];
$values[$key] = $item['value'];
}
// Now do the formatting
switch ($format) {
case 'array':
return array(
'type' => $type_name,
'names' => $names,
'values' => $values,
);
case 'short':
return t('@type: !values', array(
'@type' => $type_name,
'!values' => implode(', ', $values),
));
case 'long':
return t('Subscription %id of type %type to: !values', array(
'%id' => $subscription->sid,
'%type' => $type_name,
'!values' => implode(', ', $values),
));
}
}
/**
* Format subscriptions field for display and get some more information
*
* @return array()
* Array with 'name' and 'value' elements
*/
function notifications_format_subscription_field($type, $value, $html = TRUE) {
$format_name = $format_value = t('Unknown');
if ($info = notifications_subscription_fields($type)) {
$format_name = $info['name'];
if (!empty($info['format callback'])) {
$format_value = call_user_func($info['format callback'], $value, $html);
}
elseif (!empty($info['options callback'])) {
$options = call_user_func($info['options callback']);
$format_value = isset($options[$value]) ? $options[$value] : t('Not available');
}
else {
$format_value = check_plain($value);
}
}
return array(
'name' => $format_name,
'value' => $format_value,
);
}
/**
* Implementation of hook_theme()
*/
function notifications_theme() {
return array(
'notifications_form_table' => array(
'arguments' => array(
'element' => NULL,
),
'file' => 'notifications.admin.inc',
),
'notifications_send_intervals' => array(
'arguments' => array(
'element' => NULL,
),
'file' => 'notifications.admin.inc',
),
'notifications_digest_short_body' => array(
'arguments' => array(
'text' => NULL,
'list' => NULL,
),
'file' => 'notifications.cron.inc',
),
'notifications_digest_short_line' => array(
'arguments' => array(
'line' => NULL,
'group' => NULL,
),
'file' => 'notifications.cron.inc',
),
'notifications_digest_long_body' => array(
'arguments' => array(
'header' => NULL,
'content' => NULL,
'footer' => NULL,
),
'file' => 'notifications.cron.inc',
),
'notifications_manage_subscriptions' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'notifications.manage.inc',
),
'notifications_subscriptions_filter_form' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'notifications.manage.inc',
),
'notifications_table_form' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'notifications.admin.inc',
),
'notifications_subscription_fields' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'notifications.pages.inc',
),
);
}
/**
* Short hand for info logs
*/
function notifications_log($message = NULL, $variables = NULL) {
return _notifications_log('info', $message, $variables);
dsm($message);
}
/**
* Quick logging for debugging and manual queue processing
*/
function _notifications_log($type, $message = NULL, $variables = NULL, $severity = WATCHDOG_NOTICE) {
static $logs;
if ($message) {
$logs[] = array(
$type,
$message,
$variables,
$severity,
);
}
else {
return $logs ? array_map('_notifications_log_format', $logs) : '';
}
}
/**
* Format notifications log
*/
function _notifications_log_format($log) {
list($type, $string, $args, $severity) = $log;
if ($args) {
// Transform arguments before inserting them.
$append = array();
foreach ($args as $key => $value) {
if (is_array($value) || is_object($value)) {
$value = print_r($value, TRUE);
}
switch ($key[0]) {
case '@':
// Escaped only.
$args[$key] = check_plain($value);
break;
case '%':
$args[$key] = theme('placeholder', $value);
break;
case '!':
// Pass-through.
$args[$key] = $value;
break;
default:
// Append to string a key value pair, different from watchdog format
$append[] = ' <strong>' . $key . '</strong>= ' . check_plain($value);
break;
}
}
$string = strtr($string, $args);
$append ? $string .= ' ' . implode(', ', $append) : NULL;
}
return '<strong> ' . $type . '</strong>: ' . $string;
}
// For PHP4 compatibility
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;
}
}
Functions
Name | Description |
---|---|
notifications_access_subscribe | Menu access callback for subscribe links |
notifications_access_user | Menu access callback for user subscriptions |
notifications_access_user_add | Menu access callback, add a given subscription type |
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_format_subscription | Format subscription for display |
notifications_format_subscription_field | Format subscriptions field for display and get some more information |
notifications_form_alter | Implementation of hook_form_alter() |
notifications_get_link | Return link array for subscriptions |
notifications_get_subscriptions | Get subscriptions that fit a set of conditions. |
notifications_info | Get information from an array of data |
notifications_load_subscription | Get an individual subscription. |
notifications_log | Short hand for info logs |
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_query_sql | Build the SQL statement from query elements |
notifications_queue | Queue events for notifications adding query conditions from plug-ins |
notifications_save_subscription | Update or create subscription |
notifications_subscriptions_query_build | Query builder for subscriptions |
notifications_subscription_access | Menu access callback |
notifications_subscription_fields | Get information about subscriptions fields |
notifications_subscription_form_field | Display a form field for a notifications_field |
notifications_subscription_load | Menu loading, subscription |
notifications_subscription_types | Get info about subscription types |
notifications_theme | Implementation of hook_theme() |
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_form | Generic subscriptions content form |
notifications_user_form_submit | Process generic form submission |
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_log | Quick logging for debugging and manual queue processing |
_notifications_log_format | Format notifications log |
_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 |
_notifications_subscription_status | Status list |
_notifications_subscription_types | Build list of subscription types |