Subscriptions API

The Subscriptions module provides a range of subscription types. Users can subscribe to individual content items, to all items of a particular content type, to all posts in a particular blog, or to all items tagged with a particular taxonomy term. In addition the subscriptions module provides an API that allows other modules to implement additional subscription types. There is a single hook hook_subscriptions() that the subscriptions module uses to obtain information about the subscription types provided by your module.

The Subscriptions module uses the mail_edit module for generating the notification emails. Therefore your module should also implement its hook hook_mailkeys() to provide the template variables that you want to use in the email templates for your subscription types.

This guide illustrates the use of the subscriptions API with an example. The example we have chosen implements a subscription type that allows users to subscribe to all posts authored by a particular user. We will call our example module 'subscriptions_author' and the subscription type it implements we will call 'author'.

hook_subscriptions

The first argument of hook_subscriptions($op, $arg0 = NULL, $arg1 = NULL, $arg2 = NULL) specifies what information the hook should return. The possible values for $op are 'types', 'node_options', 'stype', 'queue', 'fields' and 'access'. The hook could be implemented as follows:

<?php
function hook_subscriptions($op$arg0 NULL$arg1 NULL$arg2 NULL) {
  switch (
$op) {
    case 
'types':
      ... 
// return information about the subscription types implemented by your module
    
case 'node_options'// called when subscription controls are to be rendered
      
... // return information about how to render the subscription controls
    
case 'queue'// is called when events are added to the subscriptions queue
      
... 
    case 
'fields'// is called when notification emails are generated
      
...  
    case 
'stype'// is called when a subscription is created by the user
      
... 
    case 
'access'// is called when notification emails are generated
      
... // makes sure users do not get notifications to information they do not have 
          // access to the subscriptions table
  
}
}
?>

We will now discuss each of the cases in turn.

$op = 'types'

This is a key operation for providing useful information about your subscription types. The returned information is used by the subscriptions module to:

- Construct a sub-tab for each new subscription type on the Subscriptions settings pages.
- Create an access permission for each new subscription type.

Example:

<?php
    
case 'types':
      
$types = array();
      
$types['author'] = array(
        
'title' => t('Authors'),
        
'access' => 'subscribe to authors',
        
'page' => 'subscriptions_author_page_author',
        
'fields' => array('node''uid'),
        
'weight' => 0,
      );
      return 
$types;
      break;
?>

The operation returns an array with one entry for each subscription type that your module creates, indexed by the machine-readable name of the subscription type. The value of each entry is itself an array with the following fields:

- 'title' The user-friendly name for the subscription type. It is used for example as the label of the sub-tab that each subscription type gets on the user and admin subscription pages.
- 'access' The name of the permission for creating subscriptions of this type.
- 'page' The function in charge of displaying the list of subscriptions of this type.
- 'fields' An array of the form array('modulename', 'fieldname'). The first entry is the name of the module that owns the data that is being subscribed to. In our case we subscribe to nodes and these are managed by the node module. The second entry is a string identifying the particular piece of data being subscribed to. In our case we are subscribing to nodes with a particular user as author, so we use 'uid'. This happens to be the field in the 'node' table that holds the author information, but such a close correspondence between the entries of this array and the table and field names is unusual and not necessary. The only important thing is that no two subscription types share the same value for this array. Other examples are array('node', 'type') for the subscription type allowing users to subscribe to all nodes of a particular type, array('node', 'tid') for subsriptions to all nodes associated to a particular taxonomy term and array('user', 'uid') for subscribing to notifications when a user profile changes.
- 'weight' This determines the order in which subscription types are displayed in the user interface.

$op = 'node_options'

The subscriptions_ui module provides a user interface for users to manage subscriptions displaying a form in every node (a.k.a subscriptions controls or node form). Implement this operation if you want to add options for your module. You can of course also implement hook_form_alter(), in order to customize the node form before render.

For this operation the arguments are used as follows:

- $arg0 holds the user object for the currently logged in user.
- $arg1 holds the current node object.

The operation should return an array with a row for every subscription type. Each row should be an array with the fields:

- 'name': The label of this option
- 'params': An array of parameters for the subscription. It must contain the key 'value' whose value should allow you to identify the nodes (or other objects) being subscribed to. It may optionally contain the key 'author_uid' in case the subscription should be restricted to nodes with a particular author. In our example we do not use this second parameter.
- 'link' => A link to a page relevant to the subscription option. In our case, where we subscribe by author, this will be a link to the author's profile page.

Example:

<?php
    
case 'node_options':
      
$options = array();
      
$options['uid'] = array(
        array(
          
'name' => t('To posts by this author'),
          
'params' => array('value' => $arg1->uid),
          
'link' => 'user/'$arg1->uid,
        ),
      );
      return 
$options;
      break;
?>

$op = queue

Occurs when a notification event is going to be queued into table subscriptions_queue.
$arg0 holds an $event array with the following structure:

array(
  'module' => 'node',
  'node' => (A node object),
  'type' => 'node',
  'action' => 'update',
);

Note the following details about the array:
- 'type' can be 'node' or 'comment'. If 'comment', it is interpreted as 'send_comments'. See example below.
- 'action' is used only when 'type' => 'node' and then 'action' => 'update' it is interpreted as 'send_updates'. See example below.

$arg1 is not used.
$arg2 is not used.

The return value should be an array of parameters to be concatenated to main subscriptions queue query:

- 'join' concatenated to JOIN part of queue query.
- 'where' additional conditions for query.
- 'args' values corresponding to where part of query.

Example:

<?php
    
case 'queue':
      
// $arg0 is $event array.
      
if ($arg0['module'] == 'node') {
        
$node $arg0['node'];
        
$params['node']['uid'] = array(
          
'join' => 'INNER JOIN {users} a ON s.value = a.uid',
          
'where' => 'a.uid = %d',
          
'args' => array($node->uid),
        );
        if (
$arg0['type'] == 'comment') {
          
$where ' AND s.send_comments = 1';
        }
        elseif (
$arg0['type'] == 'node' && $arg0['action'] == 'update') {
          
$where  ' AND s.send_updates = 1';
        }
        if (isset(
$where)) {
          
$params['node']['uid']['where'] .= $where;
        }
        return 
$params;
      }
      break;
?>

$op = 'fields'

Occurs when notification emails are generated.
$arg0 can be 'node' or 'comment'.
$arg1 is not used.
$arg2 is not used.

The return value should be an array with a row for every subscription type your module provides. Each row is an array with the following fields:
- 'mailvars_function' is a callback to populate the mailvars in the out-going messages. You can use the function_subscriptions_content_node_mailvars() provided by the subscriptions_content module.
- '!subs_type' is a mailvar giving the subscription type.
The following mailvars are already known and your module does not have to specify their value: !site, !sender_name, !sender_page, !sender_contact_page, !sender_has_contact_page, !recipient_name, !recipient_page, !edit_uri, !username, !login_url, !mailto, !manage_url, !name, !unsubscribe_url.
Note: You can use any other name for your mailvars. It just has to start with "!" and have a valid string value.

Example:

<?php
    
case 'fields'// Parameter is module
      
if ($arg0 == 'node' || $arg0 == 'comment') {
        return array(
          
'uid' => array(
            
'mailvars_function' => '_subscriptions_content_node_mailvars',
            
'!subs_type' => t('author'),
            ... 
// add more mailvars here
          
),
        );
      }
      break;
?>

$op = 'stype'

Occurs when a subscription is added.
$arg0 is the name of the requested subscription type.
$arg1 subscription ID
$arg2 author UID

This operation returns an array list as follows

array(
  'modulename',
  'fieldname',
  $arg1,
  $arg2,
);

Example:

<?php
    
case 'stype':
      return 
$arg0 == 'author' ? array('node''uid'$arg1$arg2) : NULL;
?>

$op = 'access'

Occurs when notification emails are generated. It makes sure users do not get notifications to information they do not have access in the subscriptions table.

$arg0 Custom function for loading the content to be included in the notification (commonly node_load or _comment_load)
$arg1 Arguments for custom load function (commonly nid or cid)
$arg2 A node object loaded by custom function ($arg0)

This operation returns TRUE or FALSE according to validation. FALSE will ever win upon TRUE and cancel sending of given node ($arg2) or its comments.

Example:

<?php
    
case 'access':
      if ((
$load_function == 'subscriptions_content_node_load' 
            
|| ($load_function == 'subscriptions_content_comment_load' && $node->_comments)
          ) && (
$node->status || user_access('administer nodes')) && node_access('view'$node)) {
        return 
TRUE;
      }
      break;
?>

Functions for creating subscription page

subscriptions_author_page_author()

Example:

<?php
function subscriptions_author_page_author($account$form) {
  
$query "SELECT * FROM {users} WHERE status = %d";
  
$rs pager_query($query100NULL1);
  
$authors = array();
  while (
$row db_fetch_object($rs)) {
    
$authors[] = $row;
  }
  
  return 
drupal_get_form('subscriptions_author_form'$authors$account$form) .
    
theme('pager'NULL10);
}
?>

subscriptions_og_author_form()

Example:

<?php
function subscriptions_author_form($account$form) {
  
// Load and prepare a list of 1) given account subscriptions or 2) defaults for authors
  
$uid = (isset($account) ? $account->uid : (is_numeric(arg(5)) ? -arg(5) : -DRUPAL_AUTHENTICATED_RID));
  
  
$sql db_rewrite_sql("
SELECT
  s.value, s.send_interval, s.author_uid,
  s.send_comments, s.send_updates, u.name
FROM
  {users} u JOIN
  {subscriptions} s ON
    u.uid = s.value
WHERE
  s.module = 'node' AND
  s.field = 'uid' AND
  s.recipient_uid = %d
ORDER BY
  s.author_uid
  "
'n''uid');
  
$result db_query($sql$uid);
  
$user_subscriptions = array();
  while (
$s db_fetch_array($result)) {
    
$subscriptions[$s['value']][$s['author_uid']] = $s;
    
$subscriptions_groups_by_type[$s['value']] = $s;
  }
  
  foreach (
$authors as $author) {
    
$form[$author->uid][0] = array(
      
'#tree' => TRUE,
      
'#theme' => 'subscriptions_form_table',
    );
    
//$author = user_load(array('uid' => $author->uid));
    
$defaults = array();
    
$title l($author->name'user/'$author->uid);
    
$subscriptions_uid[0] = $subscriptions[$author->uid];
    
    
// author-less item is missing -- add it here:
    
if (!isset($subscriptions_uid[0][-1])) {
      
$subscriptions_uid[0][-1] = array(
        
'send_interval' => _subscriptions_get_setting('send_interval', ($uid $uid $account)),
        
'send_comments' => _subscriptions_get_setting('send_comments', ($uid $uid $account)),
        
'send_updates' => _subscriptions_get_setting('send_updates', ($uid $uid $account)),
        
'author_uid' => FALSE,
      );
    }
    
// add the active subscriptions
    
foreach ($subscriptions_uid[0] as $author_uid => $subscription) {
      
// TODO: support multi-parent hierarchies (currently, the child is not repeated)
      
subscriptions_form_helper($form[$author->uid][0], $defaults$author_uid = -1$key $author->uid$title$subscription);
    }
        
//dsm($defaults);
        
    
$form[$author->uid][0]['defaults'] = array(
      
'#type' => 'value',
      
'#value' => $defaults,
    );
    
subscriptions_form_column_filter($form[$author->uid][0], $uid);
  }

  if (empty(
$form)) {
    
$form = array('#value' => t('There are no active authors.'));
  }
  else {
    
$form['#tree'] = TRUE;
    
$form['uid'] = array('#type' => 'value','#value' => $uid);
    
$form['access_key'] = array('#type' => 'value''#value' => 'author');
    
$form['module'] = array('#type' => 'value''#value' => 'node');
    
$form['field'] = array('#type' => 'value''#value' => 'uid');
    
$form['submit'] = array('#type' => 'submit''#value' => t('Save'), '#weight' => 10);
    
$form['#submit']['subscriptions_page_form_submit'] = array();
  }
  
  return 
$form;
}
?>

hook_mailkeys()

Used in order of let Mail Editor manage your module's mail templates, since Subscriptions_Mail module depends on mail_edit.

This hook should return an array of string, with a value for every template name.

Example:

<?php
function subscriptions_author_mailkeys() {
  return array(
'subscriptions-node-uid');
}
?>

hook_subscriptions_mail()

FPG: hook_cront????