notifications.module in Notifications 7
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
Hidden variables (can be set programatically, have no UI):
- 'notifications_event_log', number of seconds logs will be kept (defaults to 7 days) To keep logs longer than a week, define the (hidden) variable
- 'notifications_event_dispatch', dispatch events, defaults to 1 Set to 0 to disable event dispatching (no new notifications will be queued nor sent)
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
*
* Hidden variables (can be set programatically, have no UI):
*
* - 'notifications_event_log', number of seconds logs will be kept (defaults to 7 days)
* To keep logs longer than a week, define the (hidden) variable
*
* - 'notifications_event_dispatch', dispatch events, defaults to 1
* Set to 0 to disable event dispatching (no new notifications will be queued nor sent)
*
* This is based on the previous subscriptions module
*
* Development Seed, http://www.developmentseed.org, 2007
*/
// Format as plaintext. Note it evaluates to false.
define('NOTIFICATIONS_FORMAT_PLAIN', 0);
// Format as html. Note it evaluates to true
define('NOTIFICATIONS_FORMAT_HTML', 1);
// Format inline, as a string of csv
define('NOTIFICATIONS_FORMAT_INLINE', 2);
// Format as HTML table (4 +1)
define('NOTIFICATIONS_FORMAT_TABLE', 5);
// Format as item list (8 + 2(inline) + 1 (html))
define('NOTIFICATIONS_FORMAT_LIST', 10);
// Default time for logs, 7 days
define('NOTIFICATIONS_EVENT_LOG', 7 * 24 * 3600);
/**
* Implements hook_help().
*
* This file will be included only for Notifications admin pages
*/
function notifications_help($path, $arg) {
$pages = array(
'@admin-events' => url('admin/config/messaging/notifications/events'),
'@admin-subscriptions' => url('admin/config/messaging/subscriptions'),
'@admin-messaging' => url('admin/config/messaging/settings'),
'@admin-methods' => url('admin/config/messaging/settings/methods'),
'@admin-triggers' => url('admin/structure/trigger'),
);
switch ($path) {
case 'admin/config/messaging/notifications/events':
$output = '<p>' . t('To set up event actions, go to the <a href="@admin-triggers">Triggers page</a>.', $pages) . '</p>';
return $output;
case 'admin/config/messaging/notifications/settings':
// Try to clarify some concepts, tell the long story short.
$output = '<p>' . t('Users can subscribe to different objects (nodes, users, tags) by creating <strong><a href="@admin-subscriptions">Subscriptions</a></strong> for that objects. The subscription types available and the options to display will depend on additional modules enabled.', $pages) . '</p>';
$output .= '<p>' . t('When an <a href="@admin-events">Event</a> happens (node update, comment) and it matches an existing subscription, that triggers a <strong>Notification</strong> which is a <strong>Message</strong> that will be sent to the user through one of the available <a href="@admin-methods">Sending Methods</a>.', $pages) . '</p>';
$output .= '<p>' . t('These <strong>Notifications</strong> can be sent right away or queued to be processed on <i>cron</i>. Optionally Notifications can be digested and sent out every some <a href="@admin-intervals">time interval</a>.', $pages) . '</p>';
return $output;
case 'admin/messaging/notifications/subscriptions':
$output = '<p>' . t('On this page you can define which subscription types are enabled or disabled. <strong>Disabled subscription types will not be available for users</strong>.') . '</p>';
$output .= '<p>' . t('<strong>Existing subscriptions will be updated accordingly</strong>. They\'ll be disabled when their type is disabled or re-enabled when they were disabled and the type is enabled.') . '</p>';
return $output;
}
}
/**
* Format items according to predefined formats
*/
function notifications_format_items($items, $format = NOTIFICATIONS_FORMAT_LIST) {
if (!($format & NOTIFICATIONS_FORMAT_HTML)) {
$items = array_map('check_plain', $items);
}
if ($format & NOTIFICATIONS_FORMAT_LIST) {
return theme('item_list', $items);
}
elseif ($format & NOTIFICATIONS_FORMAT_TABLE) {
// @todo Return as table row ?
}
elseif ($format & NOTIFICATIONS_FORMAT_INLINE) {
return implode(', ', $items);
}
else {
return $items;
}
}
/**
* Implementation of hook_menu().
*/
function notifications_menu() {
$items['admin/config/messaging/notifications'] = array(
'title' => 'Notifications settings',
'description' => 'Configure notifications.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_settings_form',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'notifications.admin.inc',
);
$items['admin/config/messaging/notifications/settings'] = array(
'title' => 'Options',
'description' => 'Configure notifications',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/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',
);
$items['admin/config/messaging/subscriptions'] = array(
'title' => 'Subscriptions settings',
'description' => 'Configure subscription types and options.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_admin_subscriptions_settings',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'notifications.admin.inc',
);
$items['admin/config/messaging/subscriptions/types'] = array(
'title' => 'Options',
'description' => 'Subscription options.',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
// Manage existing subscriptions
$items['admin/notifications'] = array(
'title' => 'Subscriptions',
'description' => 'Manage existing subscriptions.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_admin_manage_subscriptions',
),
'access arguments' => array(
'administer notifications',
),
'file' => 'notifications.admin.inc',
);
$items['admin/notifications/admin'] = array(
'title' => 'Administer subscriptions',
'description' => 'Administer subscriptions.',
'type' => MENU_DEFAULT_LOCAL_TASK,
'access arguments' => array(
'administer notifications',
),
);
/* @todo d7update
$items['admin/notifications/status'] = array(
'title' => 'Status',
'description' => 'Summary of existing subscriptions.',
'page callback' => 'notifications_admin_status_page',
'access arguments' => array('administer notifications'),
'file' => 'notifications.admin.inc',
'type' => MENU_LOCAL_TASK,
);
*/
// Subscribe links. For this items access will be checked later in the page
$items['notifications/subscribe/%notifications_subscription_type'] = array(
'title' => 'Subscribe',
'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
// The parameter will be a list of sids, separated by commas
$items['notifications/unsubscribe'] = array(
'title' => 'Unsubscribe',
'type' => MENU_CALLBACK,
'page callback' => 'notifications_page_unsubscribe_overview',
'access callback' => 'notifications_access_unsubscribe',
'file' => 'notifications.pages.inc',
);
// Unsubscribe links This page will need to work with anonymous users
// The parameter will be a list of sids, separated by commas
$items['notifications/unsubscribe/%notifications_subscription'] = array(
'title' => 'Unsubscribe',
'type' => MENU_CALLBACK,
'page callback' => 'notifications_page_unsubscribe_subscription',
'page arguments' => array(
2,
),
'access callback' => 'notifications_access_unsubscribe',
'access arguments' => array(
2,
),
'file' => 'notifications.pages.inc',
);
// Delete all subscriptions for user
$items['notifications/unsubscribe/user/%user'] = array(
'title' => 'Unsubscribe',
'type' => MENU_CALLBACK,
'page callback' => 'notifications_page_unsubscribe_user',
'page arguments' => array(
3,
),
'access callback' => 'notifications_access_unsubscribe',
'access arguments' => array(
NULL,
3,
),
'file' => 'notifications.pages.inc',
);
// Edit subscription, stand alone page
$items['notifications/subscription/%notifications_subscription'] = array(
'type' => MENU_CALLBACK,
'title' => 'Subscription',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_subscription_form',
'view',
2,
),
'access callback' => 'notifications_access_subscription',
'access arguments' => array(
2,
'view',
),
'file' => 'notifications.pages.inc',
);
$items['notifications/subscription/%notifications_subscription/view'] = array(
'title' => 'Subscription',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
// Edit subscription, stand alone page
$items['notifications/subscription/%notifications_subscription/edit'] = array(
'type' => MENU_LOCAL_TASK,
'title' => 'Edit',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_subscription_form',
'edit',
2,
),
'access callback' => 'notifications_access_subscription',
'access arguments' => array(
2,
'edit',
),
'file' => 'notifications.pages.inc',
);
$items['notifications/subscription/%notifications_subscription/delete'] = array(
'type' => MENU_LOCAL_TASK,
'title' => 'Delete',
'weight' => 100,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'notifications_subscription_form',
'delete',
2,
),
'access callback' => 'notifications_access_subscription',
'access arguments' => array(
2,
'delete',
),
'file' => 'notifications.pages.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' => 'includes/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' => 'includes/node.inc',
);
return $items;
}
/**
* Menu access callback for subscribe links.
*
* More access checking depending on subscription type will be done at the destination page
*/
function notifications_access_subscribe($substype, $account = NULL) {
if ($substype && notifications_subscription_type_enabled($substype->type)) {
$account = $account ? $account : $GLOBALS['user'];
if ($account->uid) {
return user_access('create subscriptions', $account);
}
elseif (notifications_check_signature()) {
return TRUE;
// Signed link
}
}
}
/**
* Menu access callback for unsubscribe links.
*/
function notifications_access_unsubscribe($subscription = NULL, $account = NULL) {
if (notifications_check_signature()) {
return TRUE;
// Signed link
}
elseif ($subscription && $GLOBALS['user']->uid && $subscription->uid == $GLOBALS['user']->uid || $account && $GLOBALS['user']->uid == $account->uid) {
return user_access('maintain own subscriptions');
}
elseif (!$subscription && !$account) {
return user_access('maintain own subscriptions');
}
}
/**
* Menu access callback. Access subscription forms for given subscription
*/
function notifications_access_subscription($subscription, $op = 'view', $account = NULL) {
$account = $account ? $account : $GLOBALS['user'];
if (user_access('administer notifications') || user_access('manage all subscriptions')) {
return TRUE;
}
switch ($op) {
case 'view':
return $subscription->uid && $subscription->uid == $account->uid;
case 'edit':
case 'delete':
case 'unsubscribe':
return $subscription->uid && $subscription->uid == $account->uid && user_access('maintain own subscriptions');
}
return FALSE;
}
/**
* 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) {
return notifications_subscription($type)
->user_access($account);
}
else {
return TRUE;
}
}
}
/**
* Check signature from URL and query string
*/
function notifications_check_signature($option = 'result') {
$page_checked =& drupal_static(__FUNCTION__);
if (!isset($page_checked)) {
$page_checked = array(
'signed' => NULL,
'result' => NULL,
'timestamp' => 0,
'skip' => FALSE,
);
if (!empty($_GET['signature'])) {
$page_checked['signed'] = FALSE;
$query = $_GET;
$signature = $query['signature'];
unset($query['signature']);
unset($query['q']);
// Trim out the path element
$path = current_path();
if ($signature === notifications_url_signature($path, $query)) {
$paget_checked['signed'] = TRUE;
// Now check timestamp, it should be < 7 days
if (!empty($query['timestamp']) && time() - 24 * 7 * 3600 > (int) $query['timestamp']) {
drupal_set_message(t('This link has expired. Please get a new one or contact the site administrator.'), 'error');
$page_checked['result'] = FALSE;
}
else {
// Signature is ok and timestamp is ok or we don't have one.
// (If you sign links that never expire, that's your problem.)
$page_checked['timestamp'] = isset($query['timestamp']) ? (int) $query['timestamp'] : 0;
$page_checked['result'] = TRUE;
$page_checked['skip'] = !empty($query['skip']);
}
}
else {
drupal_set_message(t('This link is not valid anymore. Please get a new one or contact the site administrator.'), 'error');
return $page_checked['result'] = FALSE;
}
}
}
// Return nothing, we didn't have any signature
return $option ? $page_checked[$option] : $page_checked;
}
/**
* Check access for anonymous subscriptions
*/
function notifications_access_anonymous() {
static $access;
if (!isset($access)) {
$access = module_exists('notifications_anonymous') && notifications_anonymous_send_methods() && notifications_anonymous_send_intervals();
}
return $access;
}
/**
* Menu loading, subscription
*/
function notifications_subscription_load($sid) {
return Notifications_Subscription::load($sid);
}
/**
* Menu access callback for destinations
*/
function notifications_destination_access($op, $destination) {
// Access will be granted only to administrator for now
return user_access('administer notifications');
}
/**
* Implements hook_entity_info().
*/
function notifications_entity_info() {
// Notifications_Event
$info['notifications_event'] = array(
'label' => t('Event'),
'controller class' => 'MessagingEntityController',
'base class' => 'Notifications_Event',
'base table' => 'notifications_event',
'entity keys' => array(
'id' => 'eid',
),
);
// Notifications_Subscription
$info['notifications_subscription'] = array(
'label' => t('Subscription'),
'controller class' => 'MessagingEntityController',
'base class' => 'Notifications_Subscription',
'base table' => 'notifications_subscription',
'uri callback' => 'notifications_subscription_uri',
'entity keys' => array(
'id' => 'sid',
),
'bundle keys' => array(
'bundle' => 'sid',
),
'bundles' => array(),
'view modes' => array(
// @todo View mode for display as a field (when attached to nodes etc).
'full' => array(
'label' => t('Subscriptions page'),
'custom settings' => FALSE,
),
),
);
return $info;
}
/**
* Implementation of hook_cron_queue_info()
*/
/*
function notifications_cron_queue_info() {
$queues['notifications_queue'] = array(
'worker callback' => 'notifications_queue_cron_run',
'time' => 60,
);
return $queues;
}
*/
/**
* Implementation of hook_notifications()
*/
function notifications_notifications($op) {
switch ($op) {
case 'object types':
$types['node'] = array(
'title' => t('Node'),
'class' => 'Notifications_Node',
);
$types['user'] = array(
'title' => t('User'),
'class' => 'Notifications_User',
);
return $types;
case 'field types':
// Information about available fields for subscriptions
$fields['node:nid'] = array(
'title' => t('Node'),
'class' => 'Notifications_Node_Field',
);
$fields['user:uid'] = array(
'title' => t('User'),
'class' => 'Notifications_User_Field',
);
return $fields;
}
}
/**
* Get information about event types. Invoking this function will also load the event API
*
* @param $typekey
* Event type key
* @param $property
* Property to return
*/
function notifications_event_type($typekey = NULL, $property = NULL, $default = NULL) {
return notifications_info('event types', $typekey, $property, $default);
}
/**
* Get info about object types
*
* @param $type
* String, the subscriptions type OPTIONAL
* @param $field
* String, a specific field to retrieve info from OPTIONAL
*
* Information for a given field and type
* or information for a given field for all types
*/
function notifications_object_type($type = NULL, $field = NULL, $default = NULL) {
return notifications_info('object types', $type, $field, $default);
}
/**
* Get info about subscription types
*
* @param $type
* String, the subscriptions type OPTIONAL
* @param $property
* String, a specific property to retrieve info from OPTIONAL
*/
function notifications_subscription_type($type = NULL, $property = NULL, $default = NULL) {
return notifications_info('subscription types', $type, $property, $default);
}
/**
* Load subscription type for menu operations
*/
function notifications_subscription_type_load($type) {
return notifications_subscription($type);
}
/**
* Get subscription type objects available for a user or current user
*/
function notifications_subscription_user_types($account = NULL) {
$account = $account ? $account : $GLOBALS['user'];
$types = array();
foreach (notifications_subscription_enabled_types() as $type => $substype) {
if ($substype
->user_access($account, 'subscribe')) {
$types[$type] = $substype;
}
}
return $types;
}
/**
* Build subscription type object. We keep an object for each type so we can quickly clone it
*/
function notifications_subscription($type) {
$subscription_types =& drupal_static(__FUNCTION__);
if (!isset($subscription_types[$type])) {
$subscription_types[$type] = Notifications_Subscription::build_type($type);
}
return clone $subscription_types[$type];
}
/**
* Build a subscriptions list collection with this name
*
* A new Notifications_Subscription_List will be created if not cached before,
* and we'll invoke hook_notifications_subscription_list($name, $list) to collect subscriptions for this list.
*/
function notifications_subscription_list($name) {
$types =& drupal_static(__FUNCTION__);
if (!isset($types[$name])) {
$list = new Notifications_Subscription_List($name);
// Modules can add or remove subscriptions from the list
module_invoke_all('notifications_subscription_list', $name, $list);
$types[$name] = $list;
}
return clone $types[$name];
}
/**
* Implementation of hook_notifications_subscription_list().
*/
function notifications_notifications_subscription_list($name, $list = NULL) {
switch ($name) {
case 'page subscriptions':
// Get all subscriptions for the objects available in the current page for the current user
$account = $GLOBALS['user'];
if ($objects = module_invoke_all('notifications_subscription', 'page objects')) {
if ($add = Notifications_Subscription::object_subscriptions($objects, $account)) {
$list
->add($add);
}
$list
->set_user($account);
}
break;
}
}
/**
* Create notifications template object
*/
function notifications_template($name) {
$class = notifications_info('message templates', $name, 'class', 'Notifications_Message_Template');
$info = notifications_info('message templates', $name);
return new $class($info);
}
/**
* Get info about templates
*
* @param $type
* String, the subscriptions type OPTIONAL
* @param $field
* String, a specific field to retrieve info from OPTIONAL
*/
function notifications_template_info($type = NULL, $field = NULL) {
$types = notifications_info('notifications templates');
return messaging_array_info($types, $type, $field);
}
/*** Old code ****/
/**
* Implementation of hook_permission()
*/
function notifications_permission() {
return array(
'administer notifications' => array(
'title' => t('Administer notifications'),
'description' => t('Administer all notifications options.'),
),
'create subscriptions' => array(
'title' => t('Create subscriptions'),
'description' => t('Create own subscriptions.'),
),
'maintain own subscriptions' => array(
'title' => t('Maintain own subscriptions'),
'description' => t('Create, delete or edit own subscriptions.'),
),
'manage all subscriptions' => array(
'title' => t('Administer subscriptions'),
'description' => t('Administer other subscriptions for other users.'),
),
'skip notifications' => array(
'title' => t('Skip notifications'),
'description' => t('Make changes with an option to skip notifications when available.'),
),
);
}
/**
* Implementation of hook_user().
*/
function notifications_user_delete($user) {
// Delete related data on tables
Notifications_Subscription::delete_multiple(array(
'uid' => $user->uid,
));
}
/**
* Implementation of hook_user().
*/
function notifications_user($type, $edit, $user, $category = NULL) {
switch ($type) {
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('UPDATE {notifications_subscription} SET status = %d WHERE status = %d AND uid = %d', Notifications_Subscription::STATUS_BLOCKED, Notifications_Subscription::STATUS_ACTIVE, $user->uid);
notifications_queue()
->queue_clean(array(
'uid' => $user->uid,
));
}
else {
// User may be being unblocked, unblock subscriptions if any
db_query('UPDATE {notifications_subscription} SET status = %d WHERE status = %d AND uid = %d', Notifications_Subscription::STATUS_ACTIVE, Notifications_Subscription::STATUS_BLOCKED, $user->uid);
}
}
break;
case 'after_update':
// Update language for all existing subscriptions
if ($language = user_preferred_language($user)) {
db_query("UPDATE {notifications_subscription} SET language = '%s' WHERE uid = %d", $language->language, $user->uid);
}
break;
}
}
/**
* 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);
}
}
/**
* Create event object of given type
*
* Usage:
* notifications_event('node', 'insert');
*
* @param $type
* Event type key
* @param $action
* Event action
*/
function notifications_event($type, $action = NULL) {
$event = Notifications_Event::build_type($type, $action);
$event->queue = variable_get('notifications_event_queue', 0);
return $event;
}
/**
* Check whether we have enabled events of this type
*
* @param $key
* Event type key
* @param $default
* Default value to return if not set
*/
function notifications_event_enabled($key, $default = TRUE) {
$info = variable_get('notifications_event_enabled', array());
$status = isset($info[$key]) ? $info[$key] : $default;
// If this has a parent type, will be enabled just if parent is
if ($status && ($parent = notifications_event_type($key, 'parent'))) {
return notifications_event_enabled($parent, FALSE);
}
else {
return $status;
}
}
/**
* Build subscription object properly
*
* @param $subscription
* Subscription object, or array of properties or subscription type
*/
function notifications_subscription_build($subscription) {
return Notifications_Subscription::build_object($subscription);
}
/**
* 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
* @param $check
* Whether to check parameters, can be skipped if they've been previously checked
* @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, $check = TRUE) {
// Build object if not built previously
$subscription = notifications_subscription_build($subscription);
// Check all the parameters are ok, add error message and return if not
if ($check && !$subscription
->check_all()) {
return FALSE;
}
// Parameters are checked, now proceed
if (!empty($subscription->sid)) {
$op = 'update';
$result = $subscription
->save();
}
else {
if ($duplicate = notifications_get_subscriptions(array(
'uid' => $subscription->uid,
'mdid' => $subscription->mdid,
'type' => $subscription->type,
'event_type' => $subscription->event_type,
'send_interval' => $subscription->send_interval,
'module' => $subscription->module,
), $subscription
->get_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->sid = $update->sid;
// If there are more, delete, keep the table clean
while ($dupe = array_shift($duplicate)) {
Notifications_Subscription::delete_subscription($dupe->sid);
}
return notifications_save_subscription($subscription, $check);
}
else {
$op = 'insert';
$result = $subscription
->save();
}
}
// If the operation has worked so far, update fields and inform other modules
if ($result !== FALSE) {
$subscription
->invoke_all($op);
}
return $result;
}
/**
* Shorthand function for deleting everything related to a destination
*/
function notifications_delete_destination($mdid) {
Notifications_Subscription::delete_multiple(array(
'mdid' => $mdid,
));
Messaging_Destination::delete_multiple(array(
'mdid' => $mdid,
));
}
/**
* Load multiple subscriptions.
*/
function notifications_get_subscriptions($conditions = array(), $fields = array(), $limit = FALSE) {
// all fields in {notifications_subscription} are NOT NULL, so NULL is an undefined condition.
// remove NULL from coditions, preseve 0 and FALSE.
$conditions = array_filter($conditions, 'notifications_get_subscriptions_not_null');
return Notifications_Subscription::load_multiple($conditions, $fields, $limit);
}
/**
* Filter callback.
*/
function notifications_get_subscriptions_not_null($value) {
return !is_null($value);
}
/**
* Create a wrapped object and keep a static cache of created objects.
*
* @param $type
* Object type
* @parma $value
* Object or object key
*/
function notifications_object($type, $value) {
$cache =& drupal_static(__FUNCTION__);
$class = notifications_object_type($type, 'class', 'Notifications_Drupal_Object');
if (is_object($value) && is_a($value, $class)) {
// Already an instance of the right class, just return
return $object;
}
elseif (is_numeric($value) || is_string($value)) {
$key = $value;
}
if (isset($key) && isset($cache[$type][$key])) {
return $cache[$type][$key];
}
else {
$object = Notifications_Object::build($type, $value);
// Not all objects are cacheable, only if they have a value
if ($key = $object
->get_value()) {
$cache[$type][$key] = $object;
}
return $object;
}
}
/**
* Get enabled subscription types
*/
function notifications_subscription_enabled_types($type = NULL, $reset = FALSE) {
$types =& drupal_static(__FUNCTION__, NULL, $reset);
if (!isset($types)) {
$types = array();
foreach (notifications_subscription_type() as $key => $info) {
if (empty($info['disabled']) && notifications_subscription_type_enabled($key)) {
$types[$key] = notifications_subscription($key);
}
}
}
return $type ? $types[$type] : $types;
}
/**
* Check if this type is enabled or get array with all enabled types
*/
function notifications_subscription_type_enabled($type = NULL) {
if ($type) {
$info = notifications_subscription_type($type);
return empty($info['disabled']) && (!isset($info['enabled']) || $info['enabled']);
}
else {
$types = array_keys(notifications_subscription_type());
return array_filter($types, 'notifications_subscription_type_enabled');
//array_combine($types, $types)
}
}
/**
* Get information about subscriptions fields
*
* Replaces notifications_field_type()
*/
function notifications_field_type($type = NULL, $property = NULL, $default = NULL) {
$fields = notifications_info('field types');
return messaging_array_info($fields, $type, $property, $default);
}
/**
* Filter elements in an array of arrays/objects that match some condition
*
* @param $array
* Data array to be filtered.
* @param $filter
* Array of field => value pairs
* @param $reverse
* Reverse filter, return elements that don't match
*/
function notifications_array_filter($array, $filter, $reverse = FALSE) {
if (!$filter) {
return $array;
}
foreach ($array as $key => $data) {
$compare = is_object($data) ? (array) $data : $data;
$match = count(array_intersect_assoc($filter, $compare)) == count($filter);
if ($match && $reverse || !$match && !$reverse) {
unset($array[$key]);
}
}
return $array;
}
/**
* Array item callback to format title and description
*/
function notifications_format_title_description($item) {
if (is_object($item)) {
$title = check_plain($item
->get_title());
$description = check_plain($item
->get_description());
}
elseif (is_array($item)) {
$title = isset($item['title']) ? check_plain($item['title']) : t('no title');
$description = isset($item['description']) ? check_plain($item['description']) : '';
}
return '<strong>' . $title . '</strong> ' . $description;
}
/**
* Serialize an array ordering the keys before.
*
* This is useful for cache indexes and signatures.
*/
function notifications_array_serialize($array) {
if (!$array) {
return '';
}
// First we sort and serialize multiple conditions
foreach ($array as $key => $value) {
if (is_array($value)) {
asort($value);
$array[$key] = implode(':', $value);
}
}
// Now we sort the whole condtions array maintaining index association
ksort($array, SORT_STRING);
return implode(',', array_keys($array)) . '/' . implode(',', $array);
}
/**
* 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) {
switch ($op) {
case 'message types':
$info['notifications'] = array(
'name' => t('Notifications'),
'description' => t('Messages coming from user subscriptions and system events'),
);
return $info;
}
}
/**
* Implements hook_messaging_method()
*/
function notifications_messaging_method($op, $method, $param = NULL) {
switch ($op) {
case 'replace':
// Replace old method $method by new method $param
db_update('notifications_subscription')
->fields(array(
'send_method' => $param,
))
->condition('send_method', $method)
->execute();
break;
case 'disable':
// Disable all subscriptions for disabled method
db_update('notifications_subscription')
->fields(array(
'status' => Notifications_Subscription::STATUS_NOSEND,
))
->condition('send_method', $method)
->execute();
break;
}
}
/**
* Get event types. Invoking this function will also load the event API
*
* Was: notifications_event_type_enabled
*/
function notifications_event_enabled_types($typekey = NULL, $property = NULL, $default = NULL) {
$enabled =& drupal_static(__FUNCTION__);
if (!isset($enabled)) {
$enabled = array();
foreach (notifications_event_type() as $type => $event_type) {
if (empty($event['disabled']) && notifications_event_enabled($type)) {
$enabled[$type] = $event_type;
}
}
}
return messaging_array_info($info, $typekey, $property, $default);
}
/**
* Information about digesting method for a send interval.
*
* @return array()
* Ditest information for that interval, or all the information if no interval
*/
function notifications_build_method($send_interval = NULL, $refresh = FALSE) {
$build_methods = notifications_info('build methods', NULL, $refresh);
$intervals = variable_get('notifications_digest_methods', array());
if (is_null($send_interval)) {
return $build_methods;
}
elseif (!empty($intervals[$send_interval]) && isset($build_methods[$intervals[$send_interval]])) {
return $build_methods[$intervals[$send_interval]];
}
else {
// Default, that will be always the simple one
return $build_methods['simple'];
}
}
/**
* Invoke hook_notifications($name) on all modules and get a property from the array
*
* Properties will be overridden by the value of 'notifications_option_[$name]
*/
function notifications_info($name, $type = NULL, $property = NULL, $default = NULL) {
$_name = strtr($name, ' ', '_');
$info =& drupal_static('notifications_info_' . $_name);
if (!isset($info)) {
$info = module_invoke_all('notifications', $name);
// Override with variable values
foreach ($info as $key => &$data) {
if ($options = notifications_option_get($_name . '_' . $key)) {
$data = array_merge($data, $options);
}
}
// Provide alter hook: notifications_name
drupal_alter('notifications_' . $_name, $info);
}
return messaging_array_info($info, $type, $property, $default);
}
/**
* Get value from notifications_option_* variables
*
* These variables may be overridden for a page request without affecting the stored variables
*
* @param $name
* Variable name, will be prefixed with 'notifications_option_'
* @param $index
* Optional index if it s an array variable
*/
function notifications_option_get($name, $index = NULL, $default = NULL) {
$options =& drupal_static('notifications_options', array());
if (!array_key_exists($name, $options)) {
$options[$name] = variable_get('notifications_option_' . $name, NULL);
}
if (isset($index)) {
return isset($options[$name][$index]) ? $options[$name][$index] : $default;
}
else {
return $options[$name];
}
}
/**
* Set value from notifications_option_* variables
*
* @param $name
* Variable name, will be prefixed with 'notifications_option_'
* @param $value
* New variable value
* @param $index
* Optional index if it s an array variable
* @param $request
* Set only for this page request (do not save to variables table)
*/
function notifications_option_set($name, $value, $index = NULL, $request = FALSE) {
// Make sure the variable is loaded into the static variable
notifications_option_get($name);
$options =& drupal_static('notifications_options', array());
if (isset($index)) {
$options[$name][$index] = $value;
}
else {
$options[$name] = $value;
}
if (!$request) {
variable_set('notifications_option_' . $name, $options[$name]);
}
}
/**
* Get an array of options indexed by type
*
* Example: If we want to get the 'enabled' property for all subscription types
* - $name = 'subscription_types'
* - $types = array('type1', 'type2'...)
* We will iterate over all the 'notifications_option_subscription_type_$type' array variables
* and get the $property we want for them
*/
function notifications_option_array_get($name, $type_list, $property, $default = NULL) {
$type_values = array();
foreach ($type_list as $type) {
// Example 'notifications_option_subscription_types_thread' => array()
$value = notifications_option_get($name . '_' . $type, array());
if (isset($value[$property])) {
$type_values[$type] = $value[$property];
}
elseif (isset($default)) {
$type_values[$type] = $default;
}
}
return $type_values;
}
/**
* Set the same property for a family of array variables
*/
function notifications_option_array_set($name, $property, $values) {
$options =& drupal_static('notifications_options');
foreach ($values as $type => $value) {
$array = notifications_option_get($name . '_' . $type);
$array[$property] = $value;
notifications_option_set($name . '_' . $type, $array);
}
}
/**
* List of send intervals, only Immediately if no advanced queue enabled
*/
function notifications_send_intervals($account = NULL) {
if (function_exists('notifications_queue_send_intervals')) {
return notifications_queue_send_intervals($account);
}
else {
return array(
0 => t('Immediately'),
);
}
}
/**
* Get list of send methods for user or anonymous account
*/
function notifications_send_methods($account) {
// We restrict send methods for anonymous accounts when edited by regular users
if (empty($account->uid) && function_exists('notifications_anonymous_send_methods')) {
return notifications_anonymous_send_methods();
}
else {
return _notifications_send_methods($account);
}
}
/**
* List of send methods
*
* @param $account
* Optional user account, for checking permissions against this account
*/
function _notifications_send_methods($account = NULL) {
return variable_get('notifications_send_methods', messaging_method_list($account));
}
/**
* Implementation of hook_theme()
*/
function notifications_theme() {
return array(
'notifications_table_form' => array(
'render element' => 'form',
'file' => 'notifications.pages.inc',
),
'notifications_subscription_fields' => array(
'render element' => 'element',
'file' => 'notifications.pages.inc',
),
'notifications_admin_table_form' => array(
'render element' => 'form',
'file' => 'notifications.admin.inc',
),
'notifications_admin_subscription_list' => array(
'variables' => array(
'sids' => NULL,
'limit' => 10,
),
'file' => 'notifications.admin.inc',
),
);
}
/**
* Implementation of hook_forms()
*/
function notifications_forms($form_id, $args) {
if (strpos($form_id, 'notifications_subscription') === 0) {
foreach (array(
'view',
'edit',
'delete',
'add',
'subscribe',
'unsubscribe',
) as $op) {
$forms['notifications_subscription_' . $op . '_form'] = array(
'callback' => 'notifications_subscription_form',
'callback arguments' => array(
$op,
),
);
}
return $forms;
}
}
/**
* Subscription form ('add', 'edit', 'confirm')
*/
function notifications_subscription_form($form, &$form_state, $operation, $subscription) {
return $subscription
->get_form($operation, $form, $form_state);
}
/**
* Validate form submission
*/
function notifications_subscription_form_validate($form, &$form_state) {
return Notifications_Subscription::build_from_submission($form, $form_state)
->form_validate($form, $form_state);
}
/**
* Process form submission
*/
function notifications_subscription_form_submit($form, &$form_state) {
return Notifications_Subscription::build_from_submission($form, $form_state)
->form_submit($form, $form_state);
}
/**
* Form with subscription list
*/
function notifications_subscription_list_form($form, &$form_state, $type, $subscriptions) {
$form = Notifications_Subscription_List::build_list($subscriptions)
->get_form($type, $form, $form_state);
$form['#submit'][] = 'notifications_subscription_list_form_validate';
$form['#validate'][] = 'notifications_subscription_list_form_submit';
return $form;
}
/**
* Validate list form submission
*/
function notifications_subscription_list_form_validate($form, &$form_state) {
return Notifications_Subscription_List::build_from_submission($form, $form_state)
->form_validate($form, $form_state);
}
/**
* Process list form submission
*/
function notifications_subscription_list_form_submit($form, &$form_state) {
return Notifications_Subscription_List::build_from_submission($form, $form_state)
->form_submit($form, $form_state);
}
/**
* Implements hook_token_info().
*/
function notifications_token_info() {
$info['tokens']['user']['unsubscribe-url'] = array(
'name' => t("Unsubscribe URL"),
'description' => t("Signed URL for cancelling all user subscriptions."),
);
return $info;
}
/**
* Implements hook_tokens().
*/
function notifications_tokens($type, $tokens, array $data = array(), array $options = array()) {
if ($type == 'user' && !empty($data['user'])) {
$user = $data['user'];
$replacements = array();
foreach ($tokens as $name => $original) {
switch ($name) {
// Signed unsubscribe url
case 'unsubscribe-url':
$url_options = array(
'absolute' => TRUE,
);
if ($user->uid) {
$url_options['signed'] = TRUE;
$path = 'notifications/unsubscribe/user/' . $user->uid;
$url_options = notifications_url_options($path, $url_options);
}
else {
$path = 'notifications/unsubscribe';
}
$replacements[$original] = url($path, $url_options);
break;
}
}
return $replacements;
}
}
/**
* Fill some url options
*/
/**
* Wrapper for url() function with some more options
*/
function notifications_url_options($path, $options = array()) {
$options += array(
'skip_confirmation' => FALSE,
'query' => array(),
);
// If skip confirmation, links need to be signed
$options += array(
'signed' => $options['skip_confirmation'],
);
// If signed, add timestamp and signature, and maybe skip confirmation
if ($options['signed']) {
$options['query'] += array(
'timestamp' => REQUEST_TIME,
);
if ($options['skip_confirmation']) {
$options['query']['skip'] = 1;
}
$options['query']['signature'] = notifications_url_signature($path, $options['query']);
}
return $options;
}
/**
* Signature for url parameters
*
* @param $path
* Path or array with path elements
* @param $query
* Query string elements
*/
function notifications_url_signature($path, $query = array()) {
$path = is_array($path) ? implode('/', $path) : $path;
if (isset($query['signature'])) {
unset($query['signature']);
}
$string = $query ? notifications_array_serialize($query) : 'none';
return md5('notifications' . drupal_get_private_key() . ':' . $path . ':' . $string);
}
/**
* Implementation of hook_cron()
*
* Keep logs clean
*/
function notifications_cron() {
if ($log_time = variable_get('notifications_event_log', NOTIFICATIONS_EVENT_LOG)) {
db_delete('notifications_event')
->condition('log', 1)
->condition('created', REQUEST_TIME - $log_time, '<')
->execute();
}
}
/**
* Implementation of hook_cron_queue_info()
*/
function notifications_cron_queue_info() {
return array(
'notifications_event' => array(
'worker callback' => 'notifications_cron_queue_event_worker',
'time' => 60,
),
);
}
/**
* Worker callback. Check whether the message has expired before sending out.
*/
function notifications_cron_queue_event_worker($event) {
$event
->send_all();
$event
->done();
}
Functions
Name | Description |
---|---|
notifications_access_anonymous | Check access for anonymous subscriptions |
notifications_access_subscribe | Menu access callback for subscribe links. |
notifications_access_subscription | Menu access callback. Access subscription forms for given subscription |
notifications_access_unsubscribe | Menu access callback for unsubscribe links. |
notifications_access_user | Menu access callback for user subscriptions |
notifications_access_user_add | Menu access callback, add a given subscription type |
notifications_array_filter | Filter elements in an array of arrays/objects that match some condition |
notifications_array_serialize | Serialize an array ordering the keys before. |
notifications_build_method | Information about digesting method for a send interval. |
notifications_check_signature | Check signature from URL and query string |
notifications_cron | Implementation of hook_cron() |
notifications_cron_queue_event_worker | Worker callback. Check whether the message has expired before sending out. |
notifications_cron_queue_info | Implementation of hook_cron_queue_info() |
notifications_delete_destination | Shorthand function for deleting everything related to a destination |
notifications_destination_access | Menu access callback for destinations |
notifications_entity_info | Implements hook_entity_info(). |
notifications_event | Create event object of given type |
notifications_event_enabled | Check whether we have enabled events of this type |
notifications_event_enabled_types | Get event types. Invoking this function will also load the event API |
notifications_event_type | Get information about event types. Invoking this function will also load the event API |
notifications_field_type | Get information about subscriptions fields |
notifications_format_items | Format items according to predefined formats |
notifications_format_title_description | Array item callback to format title and description |
notifications_forms | Implementation of hook_forms() |
notifications_get_subscriptions | Load multiple subscriptions. |
notifications_get_subscriptions_not_null | Filter callback. |
notifications_help | Implements hook_help(). |
notifications_info | Invoke hook_notifications($name) on all modules and get a property from the array |
notifications_menu | Implementation of hook_menu(). |
notifications_messaging | Implementation of hook_messaging() |
notifications_messaging_method | Implements hook_messaging_method() |
notifications_notifications | Implementation of hook_notifications() |
notifications_notifications_subscription_list | Implementation of hook_notifications_subscription_list(). |
notifications_object | Create a wrapped object and keep a static cache of created objects. |
notifications_object_type | Get info about object types |
notifications_option_array_get | Get an array of options indexed by type |
notifications_option_array_set | Set the same property for a family of array variables |
notifications_option_get | Get value from notifications_option_* variables |
notifications_option_set | Set value from notifications_option_* variables |
notifications_permission | Implementation of hook_permission() |
notifications_save_subscription | Update or create subscription |
notifications_send_intervals | List of send intervals, only Immediately if no advanced queue enabled |
notifications_send_methods | Get list of send methods for user or anonymous account |
notifications_subscription | Build subscription type object. We keep an object for each type so we can quickly clone it |
notifications_subscription_build | Build subscription object properly |
notifications_subscription_enabled_types | Get enabled subscription types |
notifications_subscription_form | Subscription form ('add', 'edit', 'confirm') |
notifications_subscription_form_submit | Process form submission |
notifications_subscription_form_validate | Validate form submission |
notifications_subscription_list | Build a subscriptions list collection with this name |
notifications_subscription_list_form | Form with subscription list |
notifications_subscription_list_form_submit | Process list form submission |
notifications_subscription_list_form_validate | Validate list form submission |
notifications_subscription_load | Menu loading, subscription |
notifications_subscription_type | Get info about subscription types |
notifications_subscription_type_enabled | Check if this type is enabled or get array with all enabled types |
notifications_subscription_type_load | Load subscription type for menu operations |
notifications_subscription_user_types | Get subscription type objects available for a user or current user |
notifications_template | Create notifications template object |
notifications_template_info | Get info about templates |
notifications_theme | Implementation of hook_theme() |
notifications_tokens | Implements hook_tokens(). |
notifications_token_info | Implements hook_token_info(). |
notifications_url_options | Wrapper for url() function with some more options |
notifications_url_signature | Signature for url parameters |
notifications_user | Implementation of hook_user(). |
notifications_user_delete | Implementation of hook_user(). |
notifications_user_setting | Gets a user setting, defaults to default system setting for each |
_notifications_send_methods | List of send methods |