subscriptions.module

Tracking 6.x-1.x branch
  1. drupal
    1. 5 contributions/subscriptions/subscriptions.module
    2. 6 contributions/subscriptions/subscriptions.module
    3. 7 contributions/subscriptions/subscriptions.module

Subscriptions module.

Functions & methods

NameDescription
subscriptions_argReturn arg($index) in the proper way.
subscriptions_autosubscribeSubscribe users to content they post, if not already subscribed (context: on_post, on_update, on_comment).
subscriptions_coder_ignoreImplements hook_coder_ignore().
subscriptions_delete_formProvide the form definition for deleting subscriptions via s/del/... (aka subscriptions/rem/...) link.
subscriptions_delete_form_submitDelete Subscription form submit handler.
subscriptions_form_user_profile_form_alterImplementation of hook_form_alter() for the user/uid/edit form.
subscriptions_getGet subscriptions.
subscriptions_get_full_subscriptionGet all subscription fields for the given parameters.
subscriptions_get_subscriptionGet subscription sid for the given parameters.
subscriptions_initImplementation of hook_init().
subscriptions_menuImplementation of hook_menu().
subscriptions_node_is_blockedReturns TRUE if the given $nid is blocked.
subscriptions_permImplementation of hook_perm().
subscriptions_queueQueue events for notifications.
subscriptions_suspendedReturns whether notifications are suspended for the given user, and optionally alerts the user if delivery is suspended.
subscriptions_themeImplementation of hook_theme().
subscriptions_typesHook subscription_types(). Get info about subscription types.
subscriptions_userImplementation of hook_user().
subscriptions_user_loadLoad (and cache) a user.
subscriptions_user_operationsImplementation of hook_user_operations().
subscriptions_writeHelper function to do access checking and create a subscription.
subscriptions_write_subscriptionCreate a subscription.
_subscriptions_access
_subscriptions_bulk_access
_subscriptions_bulk_operationCallback for bulk subscriptions.
_subscriptions_cmp_by_weightHelper function for uasort()ing arrays with elements that have a 'weight'
_subscriptions_get_settingHelper function to retrieve send_self/autosub_on_post/autosub_on_update/autosub_on_comment/ | 1, 0, digest/send_interval/send_updates/send_comments/ | -1 = use…
_subscriptions_rem_access
_subscriptions_validate_hook_resultCheck return values of hook_subscriptions().

File

View source
  1. <?php
  2. /**
  3. * @mainpage Subscriptions module
  4. *
  5. * This module enables users to subscribe to be notified of changes to nodes or
  6. * taxonomies, such as new comments in specific forums, or additions to some
  7. * category of blog. Once enabled, all nodes will have an additional link that
  8. * allows the user to change their subscriptions. Users get a tab on their user
  9. * page to manage their own subscriptions. Users can also set an auto-subscribe
  10. * function which notifies the user if anyone comments on posts they have made.
  11. * Admins can turn this on by default.
  12. */
  13. /**
  14. * @file
  15. * Subscriptions module.
  16. */
  17. /**
  18. * Implementation of hook_init().
  19. *
  20. * @ingroup hooks
  21. * @ingroup init
  22. */
  23. function subscriptions_init() {
  24. define('SUBSCRIPTIONS_UNAVAILABLE', '<span class="error" title="'. t('(unavailable to regular users)') .'">&curren;</span>');
  25. if (subscriptions_arg(0) == 'subscriptions' || subscriptions_arg(2) == 'subscriptions') {
  26. include_once drupal_get_path('module', 'subscriptions') .'/subscriptions.admin.inc'; // TODO: do we need this?
  27. }
  28. if (subscriptions_arg(0) == 's' && subscriptions_arg(1) == 'del') {
  29. $router_item = menu_get_item('subscriptions/rem/'. substr($_GET['q'], 6));
  30. if (isset($router_item) && $router_item['access']) {
  31. menu_set_item($_GET['q'], $router_item);
  32. }
  33. }
  34. }
  35. /**
  36. * Implementation of hook_menu().
  37. *
  38. * @ingroup hooks
  39. * @ingroup menu
  40. */
  41. function subscriptions_menu() {
  42. global $user; // we need the user to to build some urls
  43. $items['admin/settings/subscriptions'] = array(
  44. 'title' => 'Subscriptions',
  45. 'description' => 'Enables site settings for user subscriptions.',
  46. 'page callback' => 'drupal_get_form',
  47. 'page arguments' => array('subscriptions_settings_form'),
  48. 'access arguments' => array('administer site configuration'),
  49. );
  50. $items['admin/settings/subscriptions/settings'] = array(
  51. 'title' => 'Site settings',
  52. 'weight' => -10,
  53. 'type' => MENU_DEFAULT_LOCAL_TASK,
  54. );
  55. $items['admin/settings/subscriptions/userdefaults'] = array(
  56. 'title' => 'User defaults',
  57. 'weight' => -5,
  58. 'page callback' => 'subscriptions_page_user_overview',
  59. 'page arguments' => array(NULL),
  60. 'type' => MENU_LOCAL_TASK,
  61. 'access arguments' => array('administer site configuration'),
  62. );
  63. $items['admin/settings/subscriptions/userdefaults/settings'] = array(
  64. 'type' => MENU_DEFAULT_LOCAL_TASK,
  65. 'title' => 'Overview',
  66. 'weight' => -10,
  67. );
  68. $items['admin/settings/subscriptions/userdefaults/bulk'] = array(
  69. 'title' => 'Bulk operation',
  70. 'weight' => -8,
  71. 'page callback' => 'drupal_get_form',
  72. 'page arguments' => array('subscriptions_page_user_bulk'),
  73. 'type' => MENU_LOCAL_TASK,
  74. 'access callback' => '_subscriptions_bulk_access',
  75. );
  76. $items['admin/settings/subscriptions/intervals'] = array(
  77. 'title' => 'Interval',
  78. 'page callback' => 'drupal_get_form',
  79. 'page arguments' => array('subscriptions_intervals'),
  80. 'type' => MENU_LOCAL_TASK,
  81. 'access arguments' => array('administer site configuration'),
  82. );
  83. $items['user/%user/subscriptions'] = array(
  84. 'title' => 'Subscriptions',
  85. 'page callback' => 'subscriptions_page_user_overview',
  86. 'page arguments' => array(1),
  87. 'type' => MENU_LOCAL_TASK,
  88. 'access callback' => '_subscriptions_access',
  89. 'access arguments' => array(1),
  90. );
  91. $hide_overview_page = variable_get('subscriptions_hide_overview_page', 0);
  92. if (!$hide_overview_page) {
  93. $items['user/%user/subscriptions/overview'] = array(
  94. 'type' => MENU_DEFAULT_LOCAL_TASK,
  95. 'title' => 'Overview',
  96. 'weight' => -10,
  97. );
  98. }
  99. else {
  100. $minimum_weight = 0;
  101. foreach (subscriptions_types() as $stype => $data) {
  102. if (isset($data['weight']) && $data['weight'] < $minimum_weight) {
  103. $minimum_weight = $data['weight'];
  104. }
  105. }
  106. }
  107. foreach (subscriptions_types() as $stype => $data) {
  108. $weight = (isset($data['weight']) ? $data['weight'] : 0);
  109. $items['subscriptions/add/'. $stype] = array(
  110. 'title' => 'Add subscription',
  111. 'type' => MENU_CALLBACK,
  112. 'page callback' => 'drupal_get_form',
  113. 'page arguments' => array('subscriptions_add_form', $stype),
  114. 'access arguments' => array($data['access']),
  115. );
  116. $items['subscriptions/del/'. $stype] = array(
  117. 'type' => MENU_CALLBACK,
  118. 'page callback' => 'drupal_get_form',
  119. 'page arguments' => array('subscriptions_del_form', $stype),
  120. 'access arguments' => array($data['access']),
  121. );
  122. if (empty($data['page'])) {
  123. continue; // no page
  124. }
  125. $items['user/%user/subscriptions/'. $stype] = array(
  126. 'title' => 'N/A', // for l.d.o, overwritten below
  127. 'type' => MENU_LOCAL_TASK,
  128. 'file' => 'subscriptions.admin.inc',
  129. 'page callback' => 'subscriptions_page',
  130. 'page arguments' => array(1, $stype),
  131. 'access callback' => '_subscriptions_access',
  132. 'access arguments' => array(1, $data['access']),
  133. 'weight' => $weight,
  134. );
  135. $items['user/%user/subscriptions/'. $stype]['title'] = $data['title'];
  136. if ($hide_overview_page && $minimum_weight == $weight) {
  137. // Install the first subscription type page as the default task.
  138. $items['user/%user/subscriptions/'. $stype]['type'] = MENU_DEFAULT_LOCAL_TASK;
  139. $default_item = $items['user/%user/subscriptions/'. $stype];
  140. $items['user/%user/subscriptions'] = array_merge($items['user/%user/subscriptions'], array(
  141. 'file' => $default_item['file'],
  142. 'page callback' => $default_item['page callback'],
  143. 'page arguments' => $default_item['page arguments'],
  144. 'access callback' => $default_item['access callback'],
  145. 'access arguments' => $default_item['access arguments'],
  146. ));
  147. $hide_overview_page = FALSE;
  148. }
  149. if ($stype == 'node') {
  150. continue; // not in site settings
  151. }
  152. $items['admin/settings/subscriptions/userdefaults/'. $stype] = array(
  153. 'title' => 'N/A', // for l.d.o, overwritten below
  154. 'type' => MENU_LOCAL_TASK,
  155. 'file' => 'subscriptions.admin.inc',
  156. 'page callback' => 'subscriptions_page',
  157. 'page arguments' => array(NULL, $stype),
  158. 'access arguments' => array('administer site configuration'),
  159. 'weight' => $weight,
  160. );
  161. $items['admin/settings/subscriptions/userdefaults/'. $stype]['title'] = $data['title'];
  162. }
  163. // Unsubscribe links
  164. $items['subscriptions/rem/%'] = array(
  165. 'page callback' => 'drupal_get_form',
  166. 'page arguments' => array('subscriptions_delete_form', 2, 3, 4, 5, 6),
  167. 'type' => MENU_CALLBACK,
  168. 'access callback' => '_subscriptions_rem_access',
  169. 'access arguments' => array(2, 3, 4, 5, 6, 7),
  170. );
  171. return $items;
  172. }
  173. function _subscriptions_rem_access($a2, $a3, $a4, $a5, $a6, $md) {
  174. return $md == md5(drupal_get_private_key() . $a2 . $a3 . $a4 . $a5 . $a6);
  175. }
  176. function _subscriptions_access($account, $access = NULL) {
  177. global $user;
  178. if ($account && $account->uid) {
  179. if (isset($access)) {
  180. $has_access = user_access($access, $account);
  181. }
  182. else {
  183. foreach (subscriptions_types() as $stype => $data) {
  184. if (user_access($data['access'], $account)) {
  185. $has_access = TRUE;
  186. }
  187. }
  188. }
  189. return !empty($has_access) && ($account->uid == $user->uid || user_access('administer user subscriptions'));
  190. }
  191. return FALSE;
  192. }
  193. /*
  194. * Access callback for bulk subscribe operations.
  195. */
  196. function _subscriptions_bulk_access()
  197. {
  198. return user_access('bulk-administer user subscriptions') && !empty($_SESSION['subscriptions']['bulk_op']);
  199. }
  200. /**
  201. * Implementation of hook_perm().
  202. *
  203. * @ingroup hooks
  204. */
  205. function subscriptions_perm() {
  206. return array_merge(array(
  207. 'administer user subscriptions',
  208. 'bulk-administer user subscriptions',
  209. 'subscribe to all content types',
  210. 'suspend own subscriptions',
  211. ), subscriptions_types('access'));
  212. t('administer user subscriptions');
  213. t('bulk-administer user subscriptions');
  214. t('subscribe to all content types');
  215. t('suspend own subscriptions');
  216. }
  217. /**
  218. * Implementation of hook_user().
  219. *
  220. * @ingroup hooks
  221. */
  222. function subscriptions_user($type, $edit, &$account, $category = NULL) {
  223. static $new_uid = 0;
  224. switch ($type) {
  225. case 'insert':
  226. db_query("INSERT INTO {subscriptions_user} (uid) VALUES(%d)", $account->uid);
  227. // $account->roles isn't set yet, but we'll be called again with 'load'
  228. $new_uid = $account->uid;
  229. break;
  230. case 'load':
  231. if ($new_uid && $account->uid == $new_uid) {
  232. foreach (array_keys($account->roles) as $rid) {
  233. $rids[] = -$rid;
  234. }
  235. db_query("INSERT INTO {subscriptions} (module, field, value, recipient_uid, send_interval, author_uid, send_updates, send_comments)
  236. SELECT module, field, value, %d, send_interval, author_uid, send_updates, send_comments FROM {subscriptions}
  237. WHERE recipient_uid IN (%s)", $account->uid, implode(',', $rids));
  238. $new_uid = 0;
  239. }
  240. break;
  241. case 'delete':
  242. db_query("DELETE FROM {subscriptions_user} WHERE uid = %d", $account->uid);
  243. db_query("DELETE FROM {subscriptions} WHERE recipient_uid = %d", $account->uid);
  244. db_query("DELETE FROM {subscriptions_last_sent} WHERE uid = %d", $account->uid);
  245. break;
  246. }
  247. }
  248. /**
  249. * Helper function to do access checking and create a subscription.
  250. *
  251. * @param string $access_key
  252. * The key for checking access to the subscription item.
  253. * @param string $module
  254. * Module that implements the subscription.
  255. * @param string $field
  256. * Field that's being checked for the subscription.
  257. * @param mixed $value
  258. * Value that must be in the field in order to trigger the subscription.
  259. * @param int $author_uid
  260. * Optional ID of the author if the subscription is restricted to nodes by
  261. * on specific author.
  262. * @param object|null $recipient
  263. * Optional user object of the recipient.
  264. * @param int $send_interval
  265. * Optional send interval, must be >0.
  266. * @param int $send_updates
  267. * Optional flag (0 or 1) to indicate whether to trigger on node updates.
  268. * @param int $send_comments
  269. * Optional flag (0 or 1) to indicate whether to trigger on comment additions
  270. * or changes.
  271. */
  272. function subscriptions_write($access_key, $module, $field, $value, $author_uid = -1, $recipient = NULL, $send_interval = 1, $send_updates = 0, $send_comments = 0) {
  273. global $user;
  274. // Access checking
  275. $recipient_uid = isset($recipient) ? $recipient : $user->uid;
  276. $access = subscriptions_types('access', $access_key);
  277. if ($recipient_uid && $access && ($recipient_uid == $user->uid && user_access($access) || user_access('administer user subscriptions')) || $recipient_uid == 0 && user_access('administer site configuration')) {
  278. subscriptions_write_subscription($module, $field, $value, $author_uid, $recipient_uid, $send_interval, $send_updates, $send_comments);
  279. }
  280. }
  281. /**
  282. * Queue events for notifications.
  283. *
  284. * @param $event
  285. * Event array.
  286. */
  287. function subscriptions_queue($event) {
  288. global $user;
  289. if (isset($event['node']->nid) && strpos(' '. variable_get('subscriptions_blocked_nodes', '') .' ', ' '. $event['node']->nid .' ')) {
  290. return;
  291. }
  292. $event += array(
  293. 'uid' => $user->uid,
  294. 'load_args' => '',
  295. );
  296. foreach (module_implements('subscriptions_queue_alter') as $module) {
  297. $function = $module .'_subscriptions_queue_alter';
  298. $function($event);
  299. if (empty($event)) {
  300. return; // $event was cleared, forget it
  301. }
  302. }
  303. if (is_array($event['load_args'])) {
  304. $event['load_args'] = serialize($event['load_args']);
  305. }
  306. if (!empty($event['noqueue_uids'])) {
  307. // Allow hook_subscriptions_queue_alter() modules to set uids that won't get any notifications queued:
  308. $noqueue_uids_where = "s.recipient_uid NOT IN (". implode(', ', array_fill(0, count($event['noqueue_uids']), '%d')) .")";
  309. }
  310. foreach (module_implements('subscriptions') as $subs_module) {
  311. $subs_module_query = module_invoke($subs_module, 'subscriptions', 'queue', $event);
  312. // Allow other modules to alter the data.
  313. drupal_alter('subscriptions_queue_guery', $subs_module_query);
  314. if (!isset($subs_module_query)) {
  315. continue;
  316. }
  317. foreach ($subs_module_query as $module => $module_query) {
  318. foreach ($module_query as $field => $query) {
  319. $join = empty($query['join']) ? '' : $query['join'];
  320. $where = empty($query['where']) ? array() : array($query['where']);
  321. $args = array($event['load_function'], $event['load_args'], $event['is_new'], $module, $field);
  322. $groupby = empty($query['groupby']) ? '' : $query['groupby'];
  323. // author-specific subscriptions trigger on comments, when the node author is subscribed to:
  324. $args[] = ($module == 'node' && $event['type'] == 'comment' && isset($event['node']->uid) ? $event['node']->uid : $event['uid']);
  325. if (!empty($query['value'])) {
  326. $where[] = "s.value = '%s'";
  327. $args[] = $query['value'];
  328. }
  329. if (!empty($query['args'])) {
  330. $args = array_merge($args, $query['args']);
  331. }
  332. if ($user->uid && !_subscriptions_get_setting('send_self', $user)) {
  333. $where[] = "s.recipient_uid != %d";
  334. $args[] = $user->uid;
  335. }
  336. if (!empty($event['noqueue_uids'])) {
  337. $where[] = $noqueue_uids_where;
  338. $args = array_merge($args, $event['noqueue_uids']);
  339. }
  340. $conditions = implode(' AND ', $where);
  341. $sql = "
  342. INSERT INTO {subscriptions_queue} (uid, name, language, module, field, value, author_uid, send_interval, digest, last_sent, load_function, load_args, is_new, suspended)
  343. SELECT u.uid, u.name, u.language, s.module, s.field, s.value, s.author_uid, s.send_interval, su.digest, COALESCE(sls.last_sent, 0) last_sent, '%s', '%s', '%d', su.suspended
  344. FROM {subscriptions} s
  345. INNER JOIN {subscriptions_user} su ON s.recipient_uid = su.uid
  346. INNER JOIN {users} u ON su.uid = u.uid
  347. $join
  348. LEFT JOIN {subscriptions_last_sent} sls ON su.uid = sls.uid AND s.send_interval = sls.send_interval
  349. WHERE
  350. s.module = '%s' AND
  351. s.field = '%s' AND
  352. s.author_uid IN (%d, -1) AND $conditions
  353. $groupby";
  354. $result = db_query($sql, $args);
  355. $affected_rows = db_affected_rows();
  356. /* for debugging:
  357. $sql = db_prefix_tables($sql);
  358. _db_query_callback($args, TRUE);
  359. $sql = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $sql);
  360. drupal_set_message("$sql<br />". $affected_rows .' row(s) inserted.');
  361. /**/
  362. }
  363. }
  364. }
  365. }
  366. /**
  367. * Get subscription sid for the given parameters.
  368. */
  369. function subscriptions_get_subscription($uid, $module, $field, $value, $author_uid = -1) {
  370. static $subscriptions;
  371. if (!isset($subscriptions[$uid][$module][$field][$value][$author_uid])) {
  372. $sql = "SELECT sid FROM {subscriptions} WHERE module = '%s' AND field = '%s' AND value = '%s' AND author_uid = %d AND recipient_uid = %d";
  373. $subscriptions[$uid][$module][$field][$value][$author_uid] = db_result(db_query($sql, $module, $field, $value, $author_uid, $uid));
  374. }
  375. return $subscriptions[$uid][$module][$field][$value][$author_uid];
  376. }
  377. /**
  378. * Get all subscription fields for the given parameters.
  379. */
  380. function subscriptions_get_full_subscription($uid, $module, $field, $value, $author_uid = -1) {
  381. $sql = "SELECT * FROM {subscriptions} WHERE module = '%s' AND field = '%s' AND value = '%s' AND author_uid = %d AND recipient_uid = %d";
  382. return db_fetch_object(db_query($sql, $module, $field, $value, $author_uid, $uid));
  383. }
  384. /**
  385. * Create a subscription.
  386. */
  387. function subscriptions_write_subscription($module, $field, $value, $author_uid, $recipient_uid, $send_interval = 1, $send_updates = 0, $send_comments = 0) {
  388. db_query("UPDATE {subscriptions} SET send_interval = %d, send_updates = %d, send_comments = %d WHERE module = '%s' AND field ='%s' AND value='%s' AND recipient_uid = %d AND author_uid = %d", $send_interval, $send_updates, $send_comments, $module, $field, $value, $recipient_uid, $author_uid);
  389. if (!db_affected_rows()) {
  390. @db_query("INSERT INTO {subscriptions} (module, field, value, author_uid, recipient_uid, send_interval, send_updates, send_comments) VALUES ('%s', '%s', '%s', %d, %d, %d, %d, %d)", $module, $field, $value, $author_uid, $recipient_uid, $send_interval, $send_updates, $send_comments);
  391. }
  392. }
  393. /**
  394. * Provide the form definition for deleting subscriptions via
  395. * s/del/... (aka subscriptions/rem/...) link.
  396. *
  397. * Callback of _subscriptions_menu().
  398. *
  399. * @param $form_state
  400. * FAPI form state.
  401. * @param $module
  402. * Module that controls the subscription.
  403. * @param $field
  404. * Field that controls the subscription (subscription type).
  405. * @param $value
  406. * Subscription parameter (depends on type).
  407. * @param $author_uid
  408. * User ID for author-specific subscriptions or -1/NULL for all authors.
  409. * @param $recipient_uid
  410. * User ID of the subscriber.
  411. *
  412. * @ingroup forms
  413. * @see _subscriptions_menu()
  414. */
  415. function subscriptions_delete_form(&$form_state, $module, $field, $value, $author_uid, $recipient_uid) {
  416. $form['data'] = array('#type' => 'value', '#value' => array($module, $field, $value, $author_uid, $recipient_uid));
  417. // We might be called from subscriptions_del_form() and don't want to submit to subscriptions_del_form_submit():
  418. $form['#submit'][] = 'subscriptions_delete_form_submit';
  419. return confirm_form($form, t('Are you sure you want to unsubscribe?'), '<front>', NULL, t('Unsubscribe'));
  420. }
  421. /**
  422. * Delete Subscription form submit handler.
  423. */
  424. function subscriptions_delete_form_submit($form, &$form_state) {
  425. db_query("DELETE FROM {subscriptions} WHERE module = '%s' AND field = '%s' AND value = '%s' AND author_uid = %d AND recipient_uid = %d", $form_state['values']['data']);
  426. drupal_set_message(t('Your subscription was deactivated.'));
  427. $form_state['redirect'] = '<front>';
  428. }
  429. /**
  430. * Subscribe users to content they post, if not already subscribed
  431. * (context: on_post, on_update, on_comment).
  432. */
  433. function subscriptions_autosubscribe($module, $field, $value, $context) {
  434. global $user;
  435. // if user has auto subscribe enabled and he's not already subscribed
  436. if ($user->uid && _subscriptions_get_setting('autosub_'. $context, $user) && !subscriptions_get_subscription($user->uid, $module, $field, $value)) {
  437. subscriptions_write_subscription($module, $field, $value, -1, $user->uid, _subscriptions_get_setting('send_interval', $user), 1, 1);
  438. }
  439. }
  440. /**
  441. * Get subscriptions.
  442. *
  443. * @param $params
  444. * Array of parameters for the query.
  445. * @return
  446. * Array of subscriptions indexed by uid, module, field, value, author_uid.
  447. */
  448. function subscriptions_get($params) {
  449. // Build query
  450. foreach ($params as $field => $value) {
  451. if (is_numeric($value)) {
  452. $conditions[] = $field .' = %d';
  453. }
  454. else {
  455. $conditions[] = "$field = '%s'";
  456. }
  457. $args[] = $value;
  458. }
  459. $sql = "SELECT * FROM {subscriptions} WHERE ". implode(' AND ', $conditions);
  460. $result = db_query($sql, $args);
  461. $subscriptions = array();
  462. while ($s = db_fetch_object($result)) {
  463. $subscriptions[$s->recipient_uid][$s->module][$s->field][$s->value][$s->author_uid] = 1;
  464. }
  465. return $subscriptions;
  466. }
  467. /**
  468. * Hook subscription_types(). Get info about subscription types.
  469. *
  470. * @return
  471. * Information for a given field and type
  472. * or information for a given field for all types
  473. *
  474. * @ingroup hooks
  475. */
  476. function subscriptions_types($field = NULL, $type = NULL) {
  477. static $types, $list;
  478. if (!isset($types)) {
  479. $types = module_invoke_all('subscriptions', 'types');
  480. // Allow other modules to alter the data.
  481. drupal_alter('subscriptions_types', $types);
  482. foreach ($types as $stype => $data) {
  483. if (!_subscriptions_validate_hook_result($stype, $data)) {
  484. continue;
  485. }
  486. foreach ($data as $name => $value) {
  487. $list[$name][$stype] = $value;
  488. }
  489. }
  490. }
  491. if ($type) {
  492. return isset($types[$type][$field]) ? $types[$type][$field] : NULL;
  493. }
  494. else if ($field) {
  495. return isset($list[$field]) ? $list[$field] : array();
  496. }
  497. else {
  498. return $types;
  499. }
  500. }
  501. /**
  502. * Check return values of hook_subscriptions().
  503. */
  504. function _subscriptions_validate_hook_result($stype, $data)
  505. {
  506. if (isset($stype)) {
  507. if (!is_numeric($stype) && is_array($data) && isset($data['title']) && isset($data['access']) && isset($data['page']) && isset($data['fields']) && is_array($data['fields'])) {
  508. return TRUE;
  509. }
  510. }
  511. static $already_reported = FALSE;
  512. if (!$already_reported) {
  513. $already_reported = TRUE;
  514. foreach (module_implements('subscriptions') as $module) {
  515. $hook = $module .'_subscriptions';
  516. $types = $hook('types');
  517. foreach($types as $stype => $data) {
  518. if (!_subscriptions_validate_hook_result($stype, $data)) {
  519. $modules[$module] = $module;
  520. }
  521. }
  522. }
  523. drupal_set_message(t('The following modules return invalid data from %hook: !modules Either they are buggy !Subscriptions add-ons, or they are unrelated to !Subscriptions and should not define %hook!', array('%hook' => 'hook_subscriptions()', '!modules' => '<ul><li>'. implode($modules, '</li><li>') .'</li></ul>', '!Subscriptions' => 'Subscriptions')), 'error', FALSE);
  524. }
  525. return FALSE;
  526. }
  527. /**
  528. * Implementation of hook_theme().
  529. */
  530. function subscriptions_theme() {
  531. return array(
  532. 'subscriptions_form_table' => array(
  533. 'file' => 'subscriptions.admin.inc',
  534. 'arguments' => array('element' => NULL),
  535. )
  536. );
  537. }
  538. /**
  539. * Returns TRUE if the given $nid is blocked.
  540. */
  541. function subscriptions_node_is_blocked($nid) {
  542. return strpos(' '. variable_get('subscriptions_blocked_nodes', '') .' ', ' '. $nid .' ');
  543. }
  544. /**
  545. * Helper function for uasort()ing arrays with elements that have a 'weight'
  546. */
  547. function _subscriptions_cmp_by_weight($a, $b) {
  548. $a = (isset($a['weight']) ? $a['weight'] : 0);
  549. $b = (isset($b['weight']) ? $b['weight'] : 0);
  550. return ($a < $b ? -1 : ($a == $b ? 0 : +1));
  551. }
  552. /**
  553. * Helper function to retrieve
  554. * send_self/autosub_on_post/autosub_on_update/autosub_on_comment/ | 1, 0,
  555. * digest/send_interval/send_updates/send_comments/ | -1 = use default
  556. * send_interval_visible/send_updates_visible/send_comments_visible/ | 1, 0, -1 = only preference, -2 = always use site default
  557. * uses_defaults values;
  558. * $account can be NULL/0 (for site default), a user object, or a uid.
  559. */
  560. function _subscriptions_get_setting($name, $account) {
  561. global $user;
  562. if (!isset($account) || is_object($account) && empty($account->uid) || is_numeric($account) && $account <= 0 ) {
  563. $uid = -DRUPAL_AUTHENTICATED_RID;
  564. unset($account);
  565. }
  566. elseif (is_numeric($account)) {
  567. if ($account == $user->uid) {
  568. $account = $user;
  569. $uid = $user->uid;
  570. }
  571. else {
  572. $uid = $account;
  573. unset($account);
  574. }
  575. }
  576. if (isset($account)) {
  577. $uid = $account->uid;
  578. }
  579. static $defaults = array();
  580. if (!isset($defaults[$uid][$name])) {
  581. $result = db_query("SELECT uid, digest, send_interval, send_updates, send_comments, send_interval_visible, send_updates_visible, send_comments_visible, autosub_on_post, autosub_on_update, autosub_on_comment, send_self FROM {subscriptions_user} WHERE uid in (%d, %d) ORDER BY uid", -DRUPAL_AUTHENTICATED_RID, $uid);
  582. while ($s = db_fetch_array($result)) {
  583. $defaults[$s['uid']] = $s;
  584. }
  585. if (empty($defaults[$uid])) {
  586. // Note: This should not happen -- subscriptions_user() takes care of inserting/removing records as users are created/deleted.
  587. // If it does happen, then users were created without calling the proper hooks, or they may have been created on another multi-site (#351753).
  588. // Let's add the missing records, as if the user were being created just now, with the expected hook_user() invocations:
  589. $account = user_load($uid);
  590. subscriptions_user('insert', NULL, $account);
  591. subscriptions_user('load', NULL, $account);
  592. return _subscriptions_get_setting($name, $account);
  593. }
  594. $defaults[$uid]['uses_defaults'] = FALSE;
  595. foreach ($defaults[$uid] as $key => $value) {
  596. if ($value < 0) { // not set, use site dft
  597. $defaults[$uid][$key] = $defaults[-DRUPAL_AUTHENTICATED_RID][$key];
  598. $defaults[$uid]['uses_defaults'] = TRUE;
  599. }
  600. }
  601. foreach (array('interval', 'updates', 'comments') as $parm ) {
  602. // Site overrides user values.
  603. if ($defaults[-DRUPAL_AUTHENTICATED_RID]['send_'. $parm .'_visible'] == -2) {
  604. $defaults[$uid]['send_'. $parm] = $defaults[-DRUPAL_AUTHENTICATED_RID]['send_'. $parm];
  605. }
  606. }
  607. }
  608. return $defaults[$uid][$name];
  609. }
  610. /**
  611. * Load (and cache) a user.
  612. */
  613. function subscriptions_user_load($uid) {
  614. static $users = array();
  615. if (!isset($users[$uid])) {
  616. $users[$uid] = user_load($uid);
  617. }
  618. return $users[$uid];
  619. }
  620. /**
  621. * Returns whether notifications are suspended for the given user,
  622. * and optionally alerts the user if delivery is suspended.
  623. */
  624. function subscriptions_suspended($uid, $alert = FALSE) {
  625. $result = db_result(db_query("SELECT suspended FROM {subscriptions_user} WHERE uid = %d", $uid));
  626. if ($result && $alert && empty($_POST)) {
  627. include_once drupal_get_path('module', 'subscriptions') .'/subscriptions.admin.inc';
  628. _subscriptions_suspended_alert($uid, $result);
  629. }
  630. return $result;
  631. }
  632. /**
  633. * Implementation of hook_form_alter() for the user/uid/edit form.
  634. *
  635. * Display a message if subscriptions notifications are suspended.
  636. *
  637. * @ingroup hooks
  638. */
  639. function subscriptions_form_user_profile_form_alter(&$form, &$form_state) {
  640. subscriptions_suspended(subscriptions_arg(1, 'uid'), TRUE);
  641. }
  642. /**
  643. * Implementation of hook_user_operations().
  644. */
  645. function subscriptions_user_operations($form_state = NULL) {
  646. if (user_access('bulk-administer user subscriptions')) {
  647. return array(
  648. array(
  649. 'label' => t('Subscribe the selected users to...'),
  650. 'callback' => '_subscriptions_bulk_operation',
  651. 'callback arguments' => array('sub'),
  652. ),
  653. array(
  654. 'label' => t('Unsubscribe the selected users from...'),
  655. 'callback' => '_subscriptions_bulk_operation',
  656. 'callback arguments' => array('unsub'),
  657. ),
  658. );
  659. }
  660. }
  661. /**
  662. * Callback for bulk subscriptions.
  663. */
  664. function _subscriptions_bulk_operation($uids, $bulk_op) {
  665. $_SESSION['subscriptions']['bulk_op'] = $bulk_op;
  666. $_SESSION['subscriptions']['uids'] = serialize($uids);
  667. $_SESSION['subscriptions']['back_url'] = $_GET['q'];
  668. drupal_goto('admin/settings/subscriptions/userdefaults/bulk');
  669. }
  670. /**
  671. * Return arg($index) in the proper way.
  672. */
  673. function subscriptions_arg($index, $member_name = FALSE) {
  674. if (strpos($_GET['q'], 's/del') === 0) {
  675. return arg($index);
  676. }
  677. $mgi = menu_get_item();
  678. $arg = NULL;
  679. if (isset($mgi['map'][$index])) {
  680. $arg = $mgi['map'][$index];
  681. if ($member_name) {
  682. if (is_object($arg) && isset($arg->$member_name)) {
  683. $arg = $arg->$member_name;
  684. }
  685. else {
  686. $arg = NULL;
  687. }
  688. }
  689. }
  690. return $arg;
  691. }
  692. /**
  693. * Implements hook_coder_ignore().
  694. */
  695. function subscriptions_coder_ignore() {
  696. return array(
  697. 'path' => drupal_get_path('module', 'subscriptions'),
  698. 'line prefix' => drupal_get_path('module', 'subscriptions') .'/',
  699. );
  700. }