subscriptions_content.module

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

Subscriptions to content events

Subscriptions_content extends the Subscriptions module to allow users to subscribe by content type. If a user subscribes to a content he will receive notifications each time a node is published. Subscriptions_content also provides the ability to subscribe to comments and updates of nodes by content type or by other kinds of subscriptions (defined by other Subscriptions submodules).

Functions & methods

NameDescription
subscriptions_content_commentImplementation of hook_comment().
subscriptions_content_comment_loadCustom function for loading comments.
subscriptions_content_cronImplementation of hook_cron().
subscriptions_content_disableImplementation of hook_disable().
subscriptions_content_form_alterImplementation of hook_form_alter().
subscriptions_content_form_comment_form_alterImplementation of hook_form_alter().
subscriptions_content_form_mail_edit_trans_alterImplementation of hook_form_alter().
subscriptions_content_form_mail_edit_trans_deleteDelete handler for enhanced mail_edit page.
subscriptions_content_form_mail_edit_trans_saveSave handler for enhanced mail_edit page.
subscriptions_content_form_node_admin_content_alterImplementation of hook_form_alter().
subscriptions_content_form_node_admin_nodes_alterImplementation of hook_form_alter().
subscriptions_content_form_node_type_form_alterImplementation of hook_form_alter().
subscriptions_content_form_subscriptions_settings_form_alterImplementation of hook_form_alter().
subscriptions_content_get_default_workflow
subscriptions_content_mailkeysImplementation of hook_mailkeys().
subscriptions_content_mail_edit_textImplementation of hook_mail_edit_text().
subscriptions_content_mail_edit_tokens_listImplementation of hook_mail_edit_tokens_list().
subscriptions_content_nodeapiImplementation of hook_nodeapi().
subscriptions_content_node_admin_nodes_submitHandle bulk publishing.
subscriptions_content_node_formBuild the Thread subscriptions form at user/UID/subscriptions/node.
subscriptions_content_node_loadCustom function for loading nodes. Loads not only the node but also any attached comments that are in the queue.
subscriptions_content_node_typeImplementation of hook_node_type().
subscriptions_content_node_type_form_submit
subscriptions_content_page_nodeSubscriptions page callback: List thread subscriptions.
subscriptions_content_page_typeSubscriptions page callback: List content types subscriptions.
subscriptions_content_subscriptionsImplementation of hook_subscriptions().
subscriptions_content_suppress_notificationsProvide a static flag to suppress notifications.
subscriptions_content_type_formBuild the Content Types subscriptions form at user/UID/subscriptions/type.
subscriptions_content_type_is_blockedReturn TRUE if the content type is blocked.
_subscriptions_content_accessImplementation of hook_access(), subhook of hook_subscriptions().
_subscriptions_content_autosubscribeAuto-subscribe, if the content type is not blocked.
_subscriptions_content_format_commentsGiven a comment template returns a formatted text of comments for given node.
_subscriptions_content_format_textConvert text with formatting into plain text.
_subscriptions_content_loadReturns a node if published, including any comments that are still queued, but limited by the given subscriptions queue ID.
_subscriptions_content_node_mailvarsFill given array of mailvars with given node values.
_subscriptions_content_node_optionsImplementation of hook_node_options(), subhook of hook_subscriptions().
_subscriptions_content_typesImplementation of hook_types(), subhook of hook_subscriptions().
_subscriptions_content_validate_blocked_nodesValidate the 'subscriptions_blocked_nodes' input.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Subscriptions to content events
  5. *
  6. * Subscriptions_content extends the Subscriptions module to allow users to
  7. * subscribe by content type. If a user subscribes to a content he will receive
  8. * notifications each time a node is published.
  9. * Subscriptions_content also provides the ability to subscribe to comments and
  10. * updates of nodes by content type or by other kinds of subscriptions (defined
  11. * by other Subscriptions submodules).
  12. */
  13. /**
  14. * Implementation of hook_subscriptions().
  15. *
  16. * @ingroup hooks
  17. */
  18. function subscriptions_content_subscriptions($op, $arg0 = NULL, $arg1 = NULL, $arg2 = NULL) {
  19. static $stypes = array(
  20. 'node' => array('node', 'nid'),
  21. 'type' => array('node', 'type'),
  22. );
  23. $function = '_subscriptions_content_'. $op;
  24. if (function_exists($function)) {
  25. return $function($arg0, $arg1, $arg2);
  26. }
  27. switch ($op) {
  28. case 'queue':
  29. // $arg0 is $event array.
  30. if ($arg0['module'] == 'node') {
  31. $node = $arg0['node'];
  32. $params['node']['nid']['value'] = $node->nid;
  33. $params['node']['type']['value'] = $node->type;
  34. if ($arg0['type'] == 'comment') {
  35. $where = 's.send_comments = 1';
  36. }
  37. elseif ($arg0['type'] == 'node' && $arg0['action'] == 'update') {
  38. $where = 's.send_updates = 1';
  39. }
  40. if (isset($where)) {
  41. $params['node']['nid']['where'] = $where;
  42. $params['node']['type']['where'] = $where;
  43. }
  44. return $params;
  45. }
  46. break;
  47. case 'fields': // Parameter is module
  48. if ($arg0 == 'node' || $arg0 == 'comment') {
  49. return array(
  50. 'nid' => array(
  51. 'mailvars_function' => '_subscriptions_content_node_mailvars',
  52. 'mailkey' => 'subscriptions_content_node-nid',
  53. '!subs_type' => t('thread'),
  54. ),
  55. 'type' => array(
  56. 'mailvars_function' => '_subscriptions_content_node_mailvars',
  57. 'mailkey' => 'subscriptions_content_node-type',
  58. '!subs_type' => t('content type'),
  59. ),
  60. );
  61. }
  62. break;
  63. case 'stypes':
  64. return $stypes;
  65. case 'stype':
  66. return (isset($stypes[$arg0]) ? array_merge( $stypes[$arg0], array($arg1, $arg2)) : NULL);
  67. }
  68. }
  69. /**
  70. * Implementation of hook_node_options(), subhook of hook_subscriptions().
  71. *
  72. * This is called by subscriptions_ui_node_form() in subscriptions_ui.module.
  73. *
  74. * @ingroup form
  75. * @ingroup hooks
  76. *
  77. * @see subscriptions_ui_node_form()
  78. */
  79. function _subscriptions_content_node_options($account, $node) {
  80. // Default node, field are the first three indexes, but they can be overridden in params
  81. // Thread
  82. $options = array();
  83. $statics = variable_get('subscriptions_static_content_types', array());
  84. if (!in_array($node->type, $statics)) {
  85. $options['nid'][] = array(
  86. 'name' => t('Subscribe to this page'),
  87. 'params' => array('module' => 'node', 'field' => 'nid', 'value' => $node->nid),
  88. 'link' => 'node/'. $node->nid,
  89. );
  90. $options['nid']['weight'] = -4;
  91. }
  92. $unlisteds = variable_get('subscriptions_unlisted_content_types', array());
  93. if (user_access('subscribe to content types', $account)) {
  94. $unlisted_tag = '';
  95. if (in_array($node->type, $unlisteds)) {
  96. if (user_access('subscribe to all content types', $account)) {
  97. $unlisted_tag = '&nbsp;'. SUBSCRIPTIONS_UNAVAILABLE;
  98. }
  99. else {
  100. return $options;
  101. }
  102. }
  103. // Content type
  104. $type_name = node_get_types('name', $node->type);
  105. if ($type_name) {
  106. $options['type'][] = array(
  107. 'name' => t('To %type content', array('%type' => $type_name)) . $unlisted_tag,
  108. 'params' => array('module' => 'node', 'field' => 'type', 'value' => $node->type),
  109. 'link' => 'type/'. $node->type,
  110. );
  111. // Content type and author
  112. $options['type'][] = array(
  113. 'name' => t('To %type content by %name', array('%type' => $type_name, '%name' => ($node->uid ? check_plain($node->name) : variable_get('anonymous', '???')))) . $unlisted_tag,
  114. 'params' => array('module' => 'node', 'field' => 'type', 'value' => $node->type, 'author_uid' => $node->uid),
  115. 'link' => 'type/'. $node->type .'/'. $node->uid,
  116. );
  117. $options['type']['weight'] = -2;
  118. }
  119. }
  120. return $options;
  121. }
  122. /**
  123. * Implementation of hook_access(), subhook of hook_subscriptions().
  124. *
  125. * @ingroup hooks
  126. */
  127. function _subscriptions_content_access($load_function, $load_args, $node) {
  128. ///global $user; /// keep this for remote debugging
  129. if (($load_function == 'subscriptions_content_node_load' || ($load_function == 'subscriptions_content_comment_load' && $node->_subscriptions_comments)) &&
  130. ($node->status || user_access('administer nodes')) && node_access('view', $node)) {
  131. if (!empty($node->type) && subscriptions_content_type_is_blocked($node->type) && !user_access('subscribe to all content types')) {
  132. return FALSE;
  133. }
  134. // We vote 'yes'. Other modules might vote 'no' and then that wins.
  135. ///watchdog('subs debug', "_sca returns TRUE for node $node->nid, user $user->uid."); ///
  136. return TRUE;
  137. }
  138. ///watchdog('subs debug', "_sca: node_access('view', $node->nid) returns ". node_access('view', $node) ." for user $user->uid."); ///
  139. }
  140. /**
  141. * Implementation of hook_types(), subhook of hook_subscriptions().
  142. *
  143. * This is called by subscriptions_types() in subscriptions.module.
  144. *
  145. * @return
  146. * Returns information about types for Subscriptions module interface.
  147. *
  148. * @ingroup form
  149. * @ingroup hooks
  150. *
  151. * @see subscriptions_types()
  152. */
  153. function _subscriptions_content_types() {
  154. $tr = 't';
  155. $types['node'] = array(
  156. 'title' => 'Pages/Threads',
  157. 'access' => 'subscribe to content',
  158. 'page' => 'subscriptions_content_page_node',
  159. 'fields' => array('node', 'nid'),
  160. 'weight' => -4,
  161. );
  162. $types['type'] = array(
  163. 'title' => 'Content types',
  164. 'access' => 'subscribe to content types',
  165. 'page' => 'subscriptions_content_page_type',
  166. 'fields' => array('node', 'type'),
  167. 'weight' => -2,
  168. );
  169. return $types;
  170. // help potx to pick up these strings:
  171. t('Pages/Threads');
  172. t('subscribe to content');
  173. t('subscribe to content types');
  174. }
  175. /**
  176. * Implementation of hook_nodeapi().
  177. *
  178. * Catch node inserts and updates and send them to the subscriptions queue;
  179. * catch node deletes and remove any associated thread subscriptions.
  180. */
  181. function subscriptions_content_nodeapi(&$node, $op, $arg = 0) {
  182. global $user;
  183. static $unpublished_nid;
  184. switch ($op) {
  185. case 'load':
  186. case 'prepare':
  187. $unpublished_nid = ($node->status ? NULL : $node->nid);
  188. $node->subscriptions_notify = in_array((isset($node->nid) ? ($node->status ? 'n_pub' : 'n_unpub') : 'n_new'), subscriptions_content_get_default_workflow($node->type));
  189. break;
  190. case 'update':
  191. if ($unpublished_nid == $node->nid && $node->status) {
  192. // An unpublished node just became published -- treat this as an 'insert'!
  193. $op = 'insert';
  194. }
  195. // fall through
  196. case 'insert':
  197. $event = array(
  198. 'module' => 'node',
  199. 'uid' => $node->uid,
  200. 'load_function' => 'subscriptions_content_node_load',
  201. 'load_args' => $node->nid,
  202. 'type' => 'node',
  203. 'action' => $op,
  204. 'is_new' => ($op == 'insert'),
  205. 'node' => $node,
  206. );
  207. $unpublished_nid = NULL;
  208. // Check direct subscription and auto subscription
  209. //if (isset($node->subscriptions_subscribe)) {
  210. // if ($node->subscriptions_subscribe) {
  211. // subscriptions_write_subscription('node', 'nid', $node->nid, -1, $user->uid);
  212. // }
  213. //} else
  214. if ($node->uid > 0 && !$arg) {
  215. _subscriptions_content_autosubscribe($node->type, 'node', 'nid', $node->nid, ($op == 'insert' ? 'on_post' : 'on_update'));
  216. }
  217. if ((!isset($node->subscriptions_notify) || $node->subscriptions_notify) && !subscriptions_content_suppress_notifications()) {
  218. subscriptions_queue($event);
  219. }
  220. break;
  221. case 'delete':
  222. db_query("DELETE FROM {subscriptions} WHERE module = 'node' AND field = 'nid' AND value = '%s'", $node->nid);
  223. break;
  224. }
  225. }
  226. /**
  227. * Implementation of hook_comment().
  228. *
  229. * Catch comment inserts and updates and send them to the subscriptions queue.
  230. */
  231. function subscriptions_content_comment($comment, $op) {
  232. global $user;
  233. static $is_unpublished;
  234. static $inserted_cid = 0;
  235. $comment = (array)$comment; // $comment can be an object or an array.
  236. if (!isset($comment['nomail']) && ($op == 'insert' || $op == 'update' || ($op == 'publish' && $comment['cid'] != $inserted_cid))) {
  237. $node = node_load($comment['nid']);
  238. if (!isset($comment['subscriptions_notify']) || $comment['subscriptions_notify']) {
  239. $event = array(
  240. 'module' => 'node',
  241. 'load_function' => 'subscriptions_content_comment_load',
  242. 'load_args' => $comment['cid'],
  243. 'uid' => $comment['uid'],
  244. 'type' => 'comment',
  245. 'action' => $op,
  246. 'is_new' => ($op != 'update' || $is_unpublished && $comment['status'] == COMMENT_PUBLISHED),
  247. 'node' => $node,
  248. 'comment' => (object)$comment,
  249. );
  250. subscriptions_queue($event);
  251. $inserted_cid = $comment['cid'];
  252. }
  253. _subscriptions_content_autosubscribe($node->type, 'node', 'nid', $comment['nid'], 'on_comment');
  254. }
  255. }
  256. /**
  257. * Implementation of hook_form_alter().
  258. *
  259. * Add the Content Settings part to admin/settings/subscriptions,
  260. *
  261. * @ingroup hooks
  262. * @ingroup form
  263. */
  264. function subscriptions_content_form_subscriptions_settings_form_alter(&$form, &$form_state) {
  265. $tr = 't';
  266. // General content settings
  267. $select = array();
  268. $select[0] = '<'. t('none') .'>';
  269. $nodetypes = node_get_types();
  270. foreach ($nodetypes as $ntype => $nname) {
  271. $select[$ntype] = $nname->name;
  272. }
  273. $form['content'] = array(
  274. '#type' => 'fieldset',
  275. '#title' => t('Content settings'),
  276. '#collapsible' => TRUE,
  277. '#weight' => -10,
  278. );
  279. $form['content']['subscriptions_unlisted_content_types'] = array(
  280. '#type' => 'select',
  281. '#title' => t('Unlisted content types'),
  282. '#default_value' => variable_get('subscriptions_unlisted_content_types', array()),
  283. '#options' => $select,
  284. '#description' => t('Select content types which should be <strong>removed from subscription listings</strong>.<br />The content may still be available for subscribing via different kinds of subscriptions, but subscribing by content type will be unavailable for the selected types.'),
  285. '#multiple' => TRUE,
  286. );
  287. $form['content']['subscriptions_blocked_content_types'] = array(
  288. '#type' => 'select',
  289. '#title' => t('Blocked content types'),
  290. '#default_value' => variable_get('subscriptions_blocked_content_types', array()),
  291. '#options' => $select,
  292. '#description' => t('Select content types which should be <strong>completely unavailable for subscribing</strong>, i.e. content of the selected types will never trigger notifications for regular users.'),
  293. '#multiple' => TRUE,
  294. );
  295. $form['content']['subscriptions_blocked_content_types_note'] = array(
  296. '#type' => 'item',
  297. '#title' => t('Note'),
  298. '#value' => t("The %permission permission grants normal access to unlisted and blocked content types; this is intended as an administrative function, and the content types and links will be marked with a '!symbol' symbol (and appear !red_ON like this !red_OFF in the case of blocked types).", array('%permission' => t('subscribe to all content types'), '!symbol' => SUBSCRIPTIONS_UNAVAILABLE, '!red_ON' => '<span class="error">', '!red_OFF' => '</span>')),
  299. );
  300. $form['content']['subscriptions_blocked_nodes'] = array(
  301. '#type' => 'textfield',
  302. '#title' => t('Blocked nodes'),
  303. '#size' => 100,
  304. '#maxlength' => 1000,
  305. '#default_value' => variable_get('subscriptions_blocked_nodes', ''),
  306. '#description' => t('Enter the IDs of nodes that should be <strong>completely unavailable for subscribing</strong>, separated by spaces.'),
  307. );
  308. $form['#validate'][] = '_subscriptions_content_validate_blocked_nodes';
  309. $statics = variable_get('subscriptions_static_content_types', array());
  310. $avoid_empty_subscribe_links = variable_get('subscriptions_avoid_empty_subscribe_links', 0);
  311. $form['content']['static_content'] = array(
  312. '#type' => 'fieldset',
  313. '#title' => t('Static content'),
  314. '#collapsible' => TRUE,
  315. '#collapsed' => (empty($statics) || (count($statics) == 1 && isset($statics[0]))) && !$avoid_empty_subscribe_links,
  316. );
  317. $form['content']['static_content']['subscriptions_static_content_types'] = array(
  318. '#type' => 'select',
  319. '#title' => t('Static content types'),
  320. '#default_value' => $statics,
  321. '#options' => $select,
  322. '#description' => t('Select content types which do not change nor receive comments and thus should not have the %option option.', array('%option' => t('Subscribe to this page'))),
  323. '#multiple' => TRUE,
  324. );
  325. $form['content']['static_content']['subscriptions_avoid_empty_subscribe_links'] = array(
  326. '#type' => 'checkbox',
  327. '#title' => t('Avoid empty %Subscribe links', array('%Subscribe' => t('Subscribe'))),
  328. '#default_value' => $avoid_empty_subscribe_links,
  329. '#description' => t('Nodes of %Static_content_types may end up with no %Subscribe options at all. Turn this option on to avoid displaying %Subscribe links in this case. The default is OFF, because this option causes processing overhead for each node view operation.', array('%Static_content_types' => t('Static content types'), '%Subscribe' => t('Subscribe'))),
  330. );
  331. $form['content']['subscriptions_allow_html_node_output'] = array(
  332. '#type' => 'checkbox',
  333. '#title' => t('Allow HTML node body output'),
  334. '#default_value' => variable_get('subscriptions_allow_html_node_output', 0),
  335. '#description' => t("Allow HTML from the node body to pass through to the email output. This, by itself, is not sufficient for sending HTML email, but it's what @Subscriptions can do to accommodate other modules that attempt to do that.<br />Default is OFF, to turn HTML formatting into plain text and to resolve the HTML entities.", array('@Subscriptions' => 'Subscriptions')),
  336. );
  337. $form['content']['subscriptions_generate_full_node'] = array(
  338. '#type' => 'checkbox',
  339. '#title' => t('Generate the %full_node variable', array('%full_node' => '!full_node')),
  340. '#default_value' => variable_get('subscriptions_generate_full_node', 0),
  341. '#description' => t("Generating this variable causes considerable overhead even if it's not used, and <strong>it may even cause errors</strong>, depending on the !content_type! Default is OFF.", array('!content_type' => $tr('content type'))),
  342. );
  343. }
  344. /**
  345. * Validate the 'subscriptions_blocked_nodes' input.
  346. */
  347. function _subscriptions_content_validate_blocked_nodes($form, $form_state) {
  348. $form_values = $form_state['values'];
  349. $values = $form_values['subscriptions_blocked_nodes'];
  350. if (!empty($values)) {
  351. $values = explode(' ', $values);
  352. foreach ($values as $v) {
  353. if (!empty($v) && !is_numeric($v)) {
  354. form_set_error('subscriptions_blocked_nodes', t('Enter a series of numbers, separated by spaces.'));
  355. break;
  356. }
  357. }
  358. }
  359. }
  360. /**
  361. * Implementation of hook_form_alter().
  362. *
  363. * Add the 'Send subscriptions notifications' to the Workflow settings on
  364. * admin/content/types/CONTENT_TYPE,
  365. *
  366. * @ingroup hooks
  367. * @ingroup form
  368. */
  369. function subscriptions_content_form_node_type_form_alter(&$form, &$form_state) {
  370. $tr = 't';
  371. $form['workflow']['subscriptions_workflow'] = array(
  372. '#type' => 'select',
  373. '#title' => t('Default %Send_subscriptions_notifications options for', array('%Send_subscriptions_notifications' => t('Send subscriptions notifications'))),
  374. '#default_value' => subscriptions_content_get_default_workflow($form['#node_type']->type),
  375. '#options' => array(
  376. 'n_new' => t('New nodes'),
  377. 'n_unpub' => t('Unpublished nodes (as new)'),
  378. 'n_pub' => t('Published nodes (as update)'),
  379. 'c_new' => t('New comments'),
  380. 'c_unpub' => t('Unpublished comments (as new)'),
  381. 'c_pub' => t('Published comments (as update)'),
  382. ),
  383. '#multiple' => TRUE,
  384. '#description' => t('Select all situations where generation of notifications should be ON.') .'<br />'.
  385. t('Items that are created in unpublished state create notifications to administrators only; when they are later published, "new" notifications are generated.') .'<br />'.
  386. t('Users with the %administer_nodes / %administer_comments permissions will be able to override these options.', array(
  387. '%administer_nodes' => $tr('administer nodes'),
  388. '%administer_comments' => $tr('administer comments'),
  389. )),
  390. );
  391. $form['#submit'][] = 'subscriptions_content_node_type_form_submit';
  392. }
  393. function subscriptions_content_get_default_workflow($content_type) {
  394. return variable_get('subscriptions_default_workflow_'. $content_type, array('n_new', 'n_unpub', 'n_pub', 'c_new', 'c_unpub', 'c_pub'));
  395. }
  396. function subscriptions_content_node_type_form_submit($form, &$form_state) {
  397. variable_set('subscriptions_default_workflow_'. $form_state['values']['type'], $form_state['values']['subscriptions_workflow']);
  398. }
  399. /**
  400. * Implementation of hook_form_alter().
  401. *
  402. * Add the Send Subscriptions Notifications checkbox to the Publishing Options
  403. * fieldset on the node edit form.
  404. *
  405. * @ingroup hooks
  406. * @ingroup form
  407. */
  408. function subscriptions_content_form_alter(&$form, &$form_state, $form_id) {
  409. global $user;
  410. if (isset($form['type']['#value']) && $form['type']['#value'] .'_node_form' == $form_id) {
  411. if (isset($form['options'])) {
  412. $tr = 't';
  413. $form['options']['subscriptions_notify'] = array(
  414. '#type' => 'checkbox',
  415. '#title' => t('Send subscriptions notifications'),
  416. '#description' => t('You may want to turn this OFF when you only change %Promoted_to_front_page or %Sticky_at_top_of_lists, otherwise Subscriptions will send out "update" notifications; this option is not saved.<br />Subscriptions does not send notifications for unpublished nodes (except to users who have the %administer_nodes permission), but when you set %Published to ON, Subscriptions will send out "new" notifications, unless you turn this off here.',
  417. array('%Promoted_to_front_page' => $tr('Promoted to front page'),
  418. '%Sticky_at_top_of_lists' => $tr('Sticky at top of lists'),
  419. '%administer_nodes' => $tr('administer nodes'),
  420. '%Published' => $tr('Published'))),
  421. '#weight' => 5,
  422. '#default_value' => (isset($form['#node']->subscriptions_notify) ? $form['#node']->subscriptions_notify : TRUE),
  423. );
  424. }
  425. }
  426. }
  427. /**
  428. * Implementation of hook_form_alter().
  429. *
  430. * Add submit handler to catch bulk content operations and suppress
  431. * notifications by default.
  432. *
  433. * @ingroup hooks
  434. * @ingroup form
  435. */
  436. function subscriptions_content_form_node_admin_content_alter(&$form, &$form_state) {
  437. if (!$form_state['submitted']) {
  438. $form['admin']['options']['subscriptions_notify'] = array(
  439. '#type' => 'checkbox',
  440. '#title' => t('Send subscriptions notifications on %Publish', array('%Publish' => t('Publish'))),
  441. '#description' => '<br />'. t('When publishing unpublished nodes, you may want to turn this ON.'),
  442. '#prefix' => '<br />',
  443. );
  444. array_unshift($form['admin']['options']['submit']['#submit'], 'subscriptions_content_node_admin_nodes_submit');
  445. }
  446. }
  447. /**
  448. * Implementation of hook_form_alter().
  449. *
  450. * Add submit handler to catch bulk content moderation submissions.
  451. *
  452. * @ingroup hooks
  453. * @ingroup form
  454. */
  455. function subscriptions_content_form_node_admin_nodes_alter(&$form, &$form_state) {
  456. array_unshift($form['#submit'], 'subscriptions_content_node_admin_nodes_submit');
  457. }
  458. /**
  459. * Implementation of hook_form_alter().
  460. *
  461. * Add the Send Subscriptions Notifications checkbox to the Administration Options
  462. * fieldset on the comment edit form.
  463. *
  464. * @ingroup hooks
  465. * @ingroup form
  466. */
  467. function subscriptions_content_form_comment_form_alter(&$form, &$form_state) {
  468. $is_update = isset($form['admin']);
  469. $is_unpublished = ($is_update ? $form['admin']['status']['#default_value'] : !user_access('post comments without approval'));
  470. if (user_access('administer comments')) {
  471. $tr = 't';
  472. // create the Administration fieldset if it doesn't exist:
  473. if (!isset($form['admin'])) {
  474. $form['admin'] = array(
  475. '#type' => 'fieldset',
  476. '#title' => $tr('Administration'),
  477. '#collapsible' => TRUE,
  478. '#collapsed' => TRUE,
  479. '#weight' => -2,
  480. );
  481. }
  482. $form['admin']['subscriptions_notify'] = array(
  483. '#type' => 'checkbox',
  484. '#title' => t('Send subscriptions notifications'),
  485. '#weight' => -1,
  486. );
  487. $node = node_load($form['nid']['#value']);
  488. $default_workflow = subscriptions_content_get_default_workflow($node->type);
  489. if ($is_update && $is_unpublished) {
  490. $form['admin']['subscriptions_notify']['#description'] = t('Subscriptions notifications are not sent for unpublished comments (except to users who have the %administer_comments permission), but when you change !Status to %Published, Subscriptions will send out "new" notifications, unless you suppress this here.', array(
  491. '%administer_comments' => $tr('administer comments'),
  492. '!Status' => $tr('Status'),
  493. '%Published' => $tr('Published'),
  494. ));
  495. $form['admin']['subscriptions_notify']['#default_value'] = in_array('c_unpub', $default_workflow);
  496. }
  497. elseif ($is_unpublished) {
  498. $form['admin']['subscriptions_notify']['#description'] = t('Subscriptions notifications are not sent for unpublished comments, except to users who have the %administer_comments permission, and you can even suppress the latter here.', array(
  499. '%administer_comments' => $tr('administer comments'),
  500. ));
  501. $form['admin']['subscriptions_notify']['#default_value'] = in_array('c_new', $default_workflow);
  502. }
  503. else { // published, new or update
  504. $form['admin']['subscriptions_notify']['#description'] = t('You can suppress sending subscriptions notifications here; this option is not saved.');
  505. $form['admin']['subscriptions_notify']['#default_value'] = in_array(($is_update ? 'c_pub' : 'c_new'), $default_workflow);
  506. }
  507. }
  508. }
  509. /**
  510. * Handle bulk publishing.
  511. */
  512. function subscriptions_content_node_admin_nodes_submit($form, &$form_state) {
  513. if ($form_state['values']['operation'] == 'publish') {
  514. if (!$form_state['values']['subscriptions_notify']) {
  515. subscriptions_content_suppress_notifications(TRUE);
  516. return;
  517. }
  518. foreach ($form_state['values']['nodes'] as $nid) {
  519. if ($nid != 0 && ($node = node_load($nid)) && !$node->status) {
  520. subscriptions_content_nodeapi($node, 'prepare');
  521. subscriptions_content_nodeapi($node, 'update', TRUE); // (avoid autosubscribe)
  522. }
  523. }
  524. }
  525. }
  526. /**
  527. * Provide a static flag to suppress notifications.
  528. */
  529. function subscriptions_content_suppress_notifications($set = NULL) {
  530. static $suppress_notifications = FALSE; // not drupal_static!
  531. if ($set !== NULL) {
  532. $suppress_notifications = $set;
  533. }
  534. return $suppress_notifications;
  535. }
  536. /**
  537. * Auto-subscribe, if the content type is not blocked.
  538. *
  539. * @param $content_type
  540. * Content type of the node to subscribe to.
  541. * @param $module
  542. * Parameter for subscriptions_autosubscribe().
  543. * @param $field
  544. * Parameter for subscriptions_autosubscribe().
  545. * @param $value
  546. * Parameter for subscriptions_autosubscribe().
  547. * @param $context
  548. * Parameter for subscriptions_autosubscribe().
  549. */
  550. function _subscriptions_content_autosubscribe($content_type, $module, $field, $value, $context)
  551. {
  552. if (!user_access('subscribe to content') || subscriptions_content_type_is_blocked($content_type)) {
  553. return;
  554. }
  555. subscriptions_autosubscribe($module, $field, $value, $context);
  556. }
  557. /**
  558. * Return TRUE if the content type is blocked.
  559. *
  560. * @param $content_type
  561. */
  562. function subscriptions_content_type_is_blocked($content_type)
  563. {
  564. $blockeds = variable_get('subscriptions_blocked_content_types', array());
  565. return in_array($content_type, $blockeds);
  566. }
  567. /**
  568. * Fill given array of mailvars with given node values.
  569. *
  570. * Callback of function _subscriptions_content_node_mailvars().
  571. *
  572. * @param $mailvars
  573. * Array of mailvars to be full-filled.
  574. * @param $node
  575. * Node object used to fill $mailvars.
  576. * @param $field
  577. * Internal use for filling variable !term_name.
  578. * @param $s
  579. * Subscription object.
  580. */
  581. function _subscriptions_content_node_mailvars(&$mailvars, $node, $field, $s) {
  582. global $language;
  583. include_once drupal_get_path('module', 'subscriptions_mail') .'/subscriptions_mail.templates.inc';
  584. $mailvars['!url'] = url('node/'. $node->nid, array('absolute' => TRUE));
  585. $mailvars['!node_type'] = $node->type;
  586. $mailvars['!title'] = trim($node->title);
  587. $mailvars['!teaser'] = _subscriptions_content_format_text($node->teaser, $node->format);
  588. $mailvars['!body'] = _subscriptions_content_format_text($node->body, $node->format);
  589. $mailvars['!nid'] = $node->nid;
  590. $mailvars['!full_node'] = (variable_get('subscriptions_generate_full_node', 0) ? _subscriptions_content_format_text(node_view($node, FALSE, TRUE, FALSE)) : '!full_node');
  591. $mailvars['!is_new'] = (integer) !empty($node->_subscriptions_is_new);
  592. $mailvars['!is_updated'] = (integer) !empty($node->_subscriptions_is_updated);
  593. $mailvars['!is_old'] = (integer)(empty($node->_subscriptions_is_new) && empty($node->_subscriptions_is_updated));
  594. $mailvars['!is_published'] = $node->status;
  595. $mailvars['!has_new_comments'] = (integer)!empty($node->_subscriptions_comments);
  596. if ($field == 'tid') {
  597. $mailvars['!term_name'] = db_result(db_query('SELECT name FROM {term_data} WHERE tid = %d', $s['value']));
  598. }
  599. elseif (!empty($node->tid)) {
  600. $mailvars['!term_name'] = db_result(db_query('SELECT name FROM {term_data} WHERE tid = %d', $node->tid));
  601. }
  602. else {
  603. unset($mailvars['!term_name']);
  604. }
  605. if ($s['load_function'] == 'subscriptions_content_comment_load' || ($s['load_function'] == 'subscriptions_content_node_load' && isset($node->_subscriptions_comments))) {
  606. $comment_template = subscriptions_mail_template_load(SUBSCRIPTIONS_COMMENT_MAILKEY, $language->language, 'body', 'CITEM');
  607. $separator = subscriptions_mail_template_load(SUBSCRIPTIONS_COMMENT_MAILKEY, $language->language, 'subject', 'SEP');
  608. $mailvars['!comments'] = _subscriptions_content_format_comments($node, $comment_template, $separator);
  609. }
  610. else {
  611. $mailvars['!comments'] = '';
  612. }
  613. $files = '';
  614. if (isset($node->files) && user_access('view uploaded files')) {
  615. foreach($node->files as $file) {
  616. $files .= file_create_url($file->filepath) ."\n";
  617. }
  618. }
  619. $mailvars['!has_files'] = empty($files) ? 0 : 1;
  620. $mailvars['!files'] = $files;
  621. $account = subscriptions_user_load($node->revision_uid);
  622. $mailvars['!revision_name'] = $account->name;
  623. $mailvars['!revision_log'] = _subscriptions_content_format_text($node->log);
  624. }
  625. /**
  626. * Convert text with formatting into plain text.
  627. */
  628. function _subscriptions_content_format_text($text, $format = NULL) {
  629. static $have_img_assist;
  630. if (!isset($have_img_assist)) {
  631. $have_img_assist = module_exists('img_assist');
  632. }
  633. if (!empty($have_img_assist)) {
  634. foreach (img_assist_get_macros($text) as $unexpanded_macro => $macro) {
  635. $expanded_macro = img_assist_render_image($macro);
  636. if (preg_match('/<img src="([^"]*)".*title="([^"]*)"/', $expanded_macro, $matches)) {
  637. $text = str_replace($unexpanded_macro, " [<a href=\"$matches[1]\">$matches[2]</a>] ", $text);
  638. }
  639. }
  640. }
  641. if (isset($format)) {
  642. $text = check_markup($text, $format, FALSE);
  643. }
  644. if (!variable_get('subscriptions_allow_html_node_output', 0)) {
  645. $text = drupal_html_to_text($text);
  646. }
  647. return trim($text);
  648. }
  649. /**
  650. * Given a comment template returns a formatted text of comments for given
  651. * node.
  652. */
  653. function _subscriptions_content_format_comments($node, $comment_template, $separator) {
  654. $comments = array();
  655. foreach ($node->_subscriptions_comments as $comment) {
  656. $mailvars = array(
  657. '!comment_name' => ($comment->uid == 0 ? variable_get('anonymous', '!comment_name') : $comment->name),
  658. '!comment_uid' => $comment->uid,
  659. '!comment_title' => trim($comment->subject),
  660. '!comment_text' => _subscriptions_content_format_text($comment->comment, $comment->format),
  661. '!comment_cid' => $comment->cid,
  662. '!comment_nid' => $comment->nid,
  663. '!comment_url' => url('node/'. $comment->nid, array('fragment' => 'comment-'. $comment->cid, 'absolute' => TRUE)),
  664. '!comment_is_new' => (integer)$comment->_subscriptions_is_new,
  665. '!comment_is_published' => (integer)($comment->status == COMMENT_PUBLISHED),
  666. );
  667. if (function_exists('realname_make_name')) {
  668. $mailvars += array(
  669. '!comment_realname' => realname_make_name($comment),
  670. );
  671. }
  672. $template = module_invoke('subscriptions_mail', 'template_preprocess', $comment_template, $mailvars);
  673. $comments[] = strtr($template ? $template : $comment_template, $mailvars);
  674. }
  675. return implode($separator, $comments);
  676. }
  677. /**
  678. * Custom function for loading nodes.
  679. * Loads not only the node but also any attached comments that are in the queue.
  680. *
  681. * Function name stored in {subscriptions_queue}.load_func and called by
  682. * subscriptions_mail().
  683. *
  684. * @param $nid
  685. * Node ID.
  686. * @param $sqid
  687. * Subscriptions queue ID.
  688. * @param $is_new
  689. * TRUE if this is a new-node notification.
  690. *
  691. * @return node as array().
  692. */
  693. function subscriptions_content_node_load($nid, $sqid, $is_new) {
  694. // Do not cache because for different users the node can be different,
  695. // subscriptions_mail_cron caches per uid.
  696. $node = _subscriptions_content_load($nid, 0);
  697. if (empty($node)) {
  698. return;
  699. }
  700. if ($is_new) {
  701. $node->_subscriptions_is_new = TRUE;
  702. }
  703. else {
  704. $node->_subscriptions_is_updated = TRUE;
  705. }
  706. return $node;
  707. }
  708. /**
  709. * Custom function for loading comments.
  710. *
  711. * Function name stored in {subscriptions_queue}.load_func and called by
  712. * subscriptions_mail().
  713. *
  714. * @param $cid
  715. * Comment ID.
  716. * @param $sqid
  717. * Subscriptions queue ID.
  718. *
  719. * @return node as array().
  720. */
  721. function subscriptions_content_comment_load($cid, $sqid) {
  722. $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $cid));
  723. if (empty($nid)) {
  724. return;
  725. }
  726. $item = db_fetch_array(db_query('SELECT sq.module, sq.field FROM {subscriptions_queue} sq WHERE sqid = %d', $sqid));
  727. if ($item['module'] != 'node' || $item['field'] != 'nid') {
  728. // Only if we're processing a node/nid queue item should we cut off the comments at an update item, otherwise not:
  729. $sqid = NULL;
  730. }
  731. $node = _subscriptions_content_load($nid, $sqid);
  732. if (empty($node->_subscriptions_comments)) {
  733. return;
  734. }
  735. return $node;
  736. }
  737. /**
  738. * Returns a node if published, including any comments that are still queued, but
  739. * limited by the given subscriptions queue ID.
  740. */
  741. function _subscriptions_content_load($nid, $comment_load_sqid) {
  742. global $user;
  743. $node = node_load($nid, NULL, TRUE);
  744. // Note: we must not cache across users (access checking), and we take care
  745. // not to process the same node more than once (except for multiple batches
  746. // of comments), so we don't gain from caching nodes; on the contrary: we
  747. // might run out of memory!
  748. if (!empty($node) && module_exists('comment')) {
  749. $published_comments_only = $limit_sqids = '';
  750. if (!user_access('administer comments')) {
  751. $published_comments_only = 'AND c.status = '. COMMENT_PUBLISHED;
  752. }
  753. if (!empty($comment_load_sqid)) {
  754. // check for a later queued update notification (don't send comments past that one because it will go out as node/type with its own comments later!)
  755. if ($cutoff_sqid = db_result(db_query_range("SELECT sqid FROM {subscriptions_queue} WHERE module = 'node' AND field = 'nid' AND value = '%s' AND uid = %d AND load_function = 'subscriptions_content_node_load' AND sqid > %d", array($nid, $user->uid, $comment_load_sqid), 0, 1))) {
  756. $limit_sqids = 'AND q.sqid < '. (integer) $cutoff_sqid;
  757. }
  758. }
  759. $sql = "
  760. SELECT q.sqid AS _subscriptions_sqid, q.is_new AS _subscriptions_is_new, c.*
  761. FROM {comments} c
  762. INNER JOIN {subscriptions_queue} q ON ". ($GLOBALS['db_type'] == 'pgsql' ? 'CAST(' : '')
  763. ."c.cid". ($GLOBALS['db_type'] == 'pgsql' ? ' AS VARCHAR)' : '')
  764. ." = q.load_args AND q.uid = %d AND q.load_function = '%s'
  765. WHERE c.nid = %d ". $published_comments_only .' '. $limit_sqids;
  766. $sql = db_rewrite_sql($sql, 'c', 'cid');
  767. $result = db_query($sql, $user->uid, 'subscriptions_content_comment_load', $nid);
  768. $sqids = array();
  769. while ($comment = db_fetch_object($result)) {
  770. comment_invoke_comment($comment, 'view');
  771. if ($comment && user_access('access comments') && !isset($node->_subscriptions_comments[$comment->cid])) {
  772. $node->_subscriptions_comments[$comment->cid] = $comment;
  773. }
  774. $sqids[] = $comment->_subscriptions_sqid;
  775. }
  776. if ($sqids) {
  777. db_query('DELETE FROM {subscriptions_queue} WHERE sqid IN ('. db_placeholders($sqids) .')', $sqids);
  778. }
  779. }
  780. return (empty($node) ? NULL : $node);
  781. }
  782. /**
  783. * Subscriptions page callback: List thread subscriptions.
  784. */
  785. function subscriptions_content_page_node($account, $form) {
  786. return drupal_get_form('subscriptions_content_node_form', $account, $form);
  787. }
  788. /**
  789. * Build the Thread subscriptions form at user/UID/subscriptions/node.
  790. *
  791. * @ingroup form
  792. */
  793. function subscriptions_content_node_form(&$form_state, $account, $form) {
  794. $uid = $account->uid;
  795. $tr = 't';
  796. $subscriptions = array();
  797. $sql = db_rewrite_sql("
  798. SELECT n.nid, n.uid, s.send_interval, s.author_uid, s.send_comments, s.send_updates, n.title, n.status, n.changed, n.comment AS comment_count, ncs.last_comment_timestamp,
  799. CASE WHEN s.send_comments + s.send_updates = 0 THEN n.created
  800. WHEN s.send_comments + s.send_updates = 2 THEN
  801. CASE WHEN n.changed > ncs.last_comment_timestamp THEN n.changed ELSE ncs.last_comment_timestamp END
  802. WHEN s.send_comments = 1 THEN ncs.last_comment_timestamp
  803. ELSE n.changed END
  804. AS latest_activity
  805. FROM {node} n
  806. INNER JOIN {subscriptions} s ON ". ($GLOBALS['db_type'] == 'pgsql' ? 'CAST(' : '')
  807. ."n.nid". ($GLOBALS['db_type'] == 'pgsql' ? ' AS VARCHAR)' : '')
  808. ." = s.value
  809. LEFT JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid
  810. WHERE s.module = 'node' AND s.field = 'nid' AND s.recipient_uid = %d
  811. ORDER BY latest_activity DESC");
  812. $sql_count = db_rewrite_sql("
  813. SELECT COUNT(n.nid)
  814. FROM {node} n
  815. INNER JOIN {subscriptions} s ON ". ($GLOBALS['db_type'] == 'pgsql' ? 'CAST(' : '')
  816. ."n.nid". ($GLOBALS['db_type'] == 'pgsql' ? ' AS VARCHAR)' : '')
  817. ." = s.value
  818. WHERE s.module = 'node' AND s.field = 'nid' AND s.recipient_uid = %d");
  819. $result = pager_query($sql, 50, 0, $sql_count, $uid);
  820. while ($s = db_fetch_array($result)) {
  821. $subscriptions[$s['nid']][$s['author_uid']] = $s;
  822. }
  823. // check whether we've commented:
  824. $nids = array_keys($subscriptions);
  825. $result = db_query("
  826. SELECT nid FROM {comments}
  827. WHERE
  828. nid IN (
  829. SELECT ". ($GLOBALS['db_type'] == 'pgsql' ? 'CAST(' : '')
  830. ."value". ($GLOBALS['db_type'] == 'pgsql' ? ' AS INTEGER)' : '')
  831. ." FROM {subscriptions} WHERE module = 'node' AND field = 'nid' AND recipient_uid = %d
  832. )
  833. AND uid = %d GROUP BY nid", $uid, $uid);
  834. while ($c = db_fetch_array($result)) {
  835. if (isset($subscriptions[$c['nid']])) {
  836. foreach ($subscriptions[$c['nid']] as $author_uid => $subscription) {
  837. $subscriptions[$c['nid']][$author_uid]['commented'] = TRUE;
  838. }
  839. }
  840. }
  841. $form[0] = array(
  842. '#type' => 'item',
  843. '#title' => '',
  844. '#tree' => TRUE,
  845. '#theme' => 'subscriptions_form_table',
  846. );
  847. $defaults = array();
  848. foreach ($subscriptions as $nid => $bundle) {
  849. foreach ($bundle as $author_uid => $subscription) {
  850. $title = truncate_utf8($subscription['title'], 40);
  851. if ($title != $subscription['title']) {
  852. $title .= '...';
  853. }
  854. $title = l($title, 'node/'. $subscription['nid']);
  855. if (!$subscription['status']) {
  856. if (user_access('administer nodes')) {
  857. $title = SUBSCRIPTIONS_UNAVAILABLE .'&nbsp;'. $title;
  858. }
  859. else {
  860. continue;
  861. }
  862. }
  863. $subscription['extra_info'] = t('@latest_activity, @authored, @commented', array(
  864. '@latest_activity' => format_interval(time() - $subscription['latest_activity']),
  865. '@authored' => ($subscription['uid'] == $uid ? $tr('Yes') : $tr('No')),
  866. '@commented' => (!empty($subscription['commented']) ? $tr('Yes') : $tr('No')),
  867. ));
  868. subscriptions_form_helper($form[0], $defaults, $author_uid, $subscription['nid'], $title, $subscription);
  869. }
  870. }
  871. unset($form[0]['author']);
  872. if (count(element_children($form[0]))) {
  873. $form[0]['extra_info']['#title'] = t('Latest activity, authored, commented');
  874. $form[0]['defaults'] = array(
  875. '#type' => 'value',
  876. '#value' => $defaults,
  877. );
  878. subscriptions_form_column_filter($form[0], $uid);
  879. $form['#tree'] = TRUE;
  880. $form['uid'] = array('#type' => 'value', '#value' => $uid);
  881. $form['access_key'] = array('#type' => 'value', '#value' => 'node');
  882. $form['module'] = array('#type' => 'value', '#value' => 'node');
  883. $form['field'] = array('#type' => 'value', '#value' => 'nid');
  884. $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 10);
  885. $form['#submit'][] = 'subscriptions_page_form_submit';
  886. $form['note'] = array('#type' => 'item', '#description' => '<div>'. t('Note: Deactivated subscriptions will be removed from the list.') .'</div>' );
  887. $form['pager'] = array('#value' => theme('pager', NULL, 50, 0));
  888. }
  889. else {
  890. unset($form['header']);
  891. unset($form['footer']);
  892. $form['notice'] = array(
  893. '#type' => 'item',
  894. '#value' => t('There are no subscribed pages.'),
  895. '#weight' => 10,
  896. );
  897. }
  898. return $form;
  899. }
  900. /**
  901. * Subscriptions page callback: List content types subscriptions.
  902. */
  903. function subscriptions_content_page_type($account, $form) {
  904. return drupal_get_form('subscriptions_content_type_form', $account, $form);
  905. }
  906. /**
  907. * Build the Content Types subscriptions form at user/UID/subscriptions/type.
  908. *
  909. * @ingroup form
  910. */
  911. function subscriptions_content_type_form(&$form_state, $account, $form) {
  912. $subscriptions = array();
  913. $bulk_op = (empty($_SESSION['subscriptions']['bulk_op']) ? '' : $_SESSION['subscriptions']['bulk_op']);
  914. if ($bulk_op) {
  915. // No initialization for bulk subscription.
  916. $uid = -DRUPAL_AUTHENTICATED_RID;
  917. }
  918. else {
  919. $arg5 = subscriptions_arg(5);
  920. $uid = (isset($account) ? $account->uid : (is_numeric($arg5) ? -$arg5 : -DRUPAL_AUTHENTICATED_RID));
  921. $result = db_query(db_rewrite_sql("
  922. SELECT s.value, s.send_interval, s.author_uid, s.send_comments, s.send_updates, nt.type, nt.name
  923. FROM {subscriptions} s
  924. INNER JOIN {node_type} nt ON s.value = nt.type
  925. WHERE s.module = 'node' AND s.field = 'type' AND s.recipient_uid = %d
  926. ORDER BY s.author_uid", 'nt', 'type'), $uid);
  927. while ($s = db_fetch_array($result)) {
  928. $subscriptions[$s['value']][$s['author_uid']] = $s;
  929. }
  930. }
  931. $unlisteds = variable_get('subscriptions_unlisted_content_types', array());
  932. $blockeds = variable_get('subscriptions_blocked_content_types', array());
  933. $omits = array_merge($unlisteds, $blockeds);
  934. $form[0] = array(
  935. '#type' => 'item',
  936. '#title' => '',
  937. '#tree' => TRUE,
  938. '#theme' => 'subscriptions_form_table',
  939. );
  940. $intervals = _subscriptions_send_intervals();
  941. foreach (node_get_types() as $type) {
  942. // add the active subscriptions
  943. $type_name = check_plain($type->name);
  944. if (in_array($type->type, $omits)) {
  945. if (user_access('subscribe to all content types') || user_access('administer site configuration')) {
  946. if (in_array($type->type, $blockeds)) {
  947. $type_name = '<span class="error" title="'. t('This !content_type is blocked.', array('!content_type' => t('content type'))) .'">'. $type_name .'</span>&nbsp;'. SUBSCRIPTIONS_UNAVAILABLE;
  948. }
  949. else {
  950. $type_name = $type_name .'&nbsp;'. SUBSCRIPTIONS_UNAVAILABLE;
  951. }
  952. }
  953. else {
  954. continue;
  955. }
  956. }
  957. if (!isset($subscriptions[$type->type][-1])) {
  958. // author-less item is missing -- add it here:
  959. $subscriptions[$type->type][-1] = array(
  960. 'send_interval' => _subscriptions_get_setting('send_interval', ($uid < 0 ? $uid : $account)),
  961. 'send_comments' => _subscriptions_get_setting('send_comments', ($uid < 0 ? $uid : $account)),
  962. 'send_updates' => _subscriptions_get_setting('send_updates', ($uid < 0 ? $uid : $account)),
  963. 'author_uid' => FALSE,
  964. );
  965. }
  966. foreach ($subscriptions[$type->type] as $author_uid => $subscription) {
  967. subscriptions_form_helper($form[0], $defaults, $author_uid, $type->type, $type_name, $subscription);
  968. }
  969. }
  970. if (isset($form[0]['checkboxes'])) {
  971. $form[0]['defaults'] = array(
  972. '#type' => 'value',
  973. '#value' => $defaults,
  974. );
  975. subscriptions_form_column_filter($form[0], $uid);
  976. $form['#tree'] = TRUE;
  977. $form['uid'] = array('#type' => 'value', '#value' => ($bulk_op ? $_SESSION['subscriptions']['uids'] : $uid));
  978. $form['access_key'] = array('#type' => 'value', '#value' => 'type');
  979. $form['module'] = array('#type' => 'value', '#value' => 'node');
  980. $form['field'] = array('#type' => 'value', '#value' => 'type');
  981. $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 10);
  982. $form['#submit'][] = 'subscriptions_page_form_submit';
  983. }
  984. else {
  985. $form['header']['#value'] = t('There are no available !subs_types.', array('!subs_types' => t('content types')));
  986. unset($form['footer']);
  987. }
  988. return $form;
  989. }
  990. /**
  991. * Implementation of hook_mailkeys().
  992. *
  993. * Provide mailkeys for mail_edit.
  994. *
  995. * @ingroup hooks
  996. */
  997. function subscriptions_content_mailkeys() {
  998. $mailkeys['node-nid'] = t('Notifications for %Pages (update/comment) subscriptions', array('%Pages' => t('Pages/Threads')));
  999. foreach (node_get_types() as $key => $type) {
  1000. $mailkeys['node-type-'. $key] = t('Notifications for %type !content_type subscriptions', array('%type' => $type->name, '!content_type' => t('content type')));
  1001. }
  1002. return $mailkeys;
  1003. }
  1004. /**
  1005. * Implementation of hook_mail_edit_tokens_list().
  1006. *
  1007. * Provide replacable tokens for mail_edit.
  1008. *
  1009. * @ingroup hooks
  1010. */
  1011. function subscriptions_content_mail_edit_tokens_list($mailkey, $options = array()) {
  1012. $tr = 't';
  1013. $tokens = array();
  1014. switch ($mailkey) {
  1015. case 'comments':
  1016. $tokens += array(
  1017. '!comment_name' => t('The name of the comment author.'),
  1018. );
  1019. if (module_exists('realname')) {
  1020. $tokens += array(
  1021. '!comment_realname' => t('The real name of the comment author (provided by Realname module).'),
  1022. );
  1023. }
  1024. $tokens += array(
  1025. '!comment_uid' => t('The ID of the comment author (0 = %anonymous).', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
  1026. '!comment_title' => t('The title of the comment.'),
  1027. '!comment_text' => t('The body text of the comment.'),
  1028. '!comment_cid' => t('The ID of the comment.'),
  1029. '!comment_nid' => t("The ID of the comment's node."),
  1030. '!comment_url' => t('The direct URL of the comment.'),
  1031. '!comment_is_new' => t('The type of comment notification: 1 = new comment, 0 = updated comment.'),
  1032. '!comment_is_published' => t('The comment publication state: 1 = published, 0 = unpublished.<br />(Unpublished comments are sent to users with the %administer_comments permission only.)', array('%administer_comments' => $tr('administer comments'))),
  1033. );
  1034. return $tokens;
  1035. default:
  1036. $tokens += array(
  1037. '!unsubscribe_url' => t('The user can unsubscribe by clicking this link.'),
  1038. '!sender_name' => t('The name of the sender (if the sender is visible).'),
  1039. );
  1040. if (module_exists('realname')) {
  1041. $tokens += array(
  1042. '!sender_realname' => t('The real name of the sender (provided by RealName module).'),
  1043. );
  1044. }
  1045. $tokens += array(
  1046. '!sender_page' => t('The user page of the sender (if the sender is visible).'),
  1047. '!sender_has_contact_page' => t("The sender's contact setting: 1 = contact form enabled, 0 = disabled."),
  1048. '!sender_contact_page' => t('The contact page of the sender.'),
  1049. '!sender_uid' => t('The ID of the sender (even if the sender is not visible!) (0 = %anonymous).', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
  1050. '!subs_type' => t("The type of the subscription, like '!thread' or '!category'.", array('!thread' => t('thread'), '!category' => $tr('category'))),
  1051. '!node_type' => t("The type of the node, like '!forum' or '!story'.", array('!forum' => 'forum', '!story' => 'story')),
  1052. '!revision_name' => t('The name of the user who made the last change to the node.'),
  1053. '!revision_log' => t('The revision log entry of the node.'),
  1054. '!nid' => t('The ID of the node.'),
  1055. '!title' => t('The title of the subscriptions item.'),
  1056. '!teaser' => t('An excerpt of the subscriptions item.'),
  1057. '!body' => t('The body of the subscriptions item.'),
  1058. );
  1059. if (isset($options['tokens'])) {
  1060. $tokens += $options['tokens'];
  1061. }
  1062. $tokens += module_invoke_all('subscriptions_tokens_list', $mailkey, array('tokens' => $tokens));
  1063. if ($mailkey != 'digest') {
  1064. $tokens['!full_node'] = t('The full node as it appears on the website (must be specifically enabled !here).', array('!here' => l(t('here'), 'admin/settings/subscriptions', array('fragment' => 'edit-subscriptions-generate-full-node'))));
  1065. }
  1066. $tokens += array(
  1067. '!url' => t('The URL of the item.'),
  1068. '!is_new' => t('The type of notification: 1 = new item, 0 = otherwise.'),
  1069. '!is_updated' => t('The type of notification: 1 = updated (possibly new and already updated) item, 0 = otherwise.'),
  1070. '!is_old' => t('The type of notification: 1 = neither new nor updated item, 0 = otherwise.'),
  1071. '!is_published' => t('The publication state: 1 = published, 0 = unpublished.<br />(Unpublished nodes are sent to users with the %administer_nodes permission only.)', array('%administer_nodes' => $tr('administer nodes'))),
  1072. '!has_files' => t('The presence of attached files or img_assist images: 1 = files are available in !files, 0 = no files.'),
  1073. '!files' => t('The list of attached files, one per line, if any, otherwise empty.'),
  1074. '!has_new_comments' => t('The comments state: 1 = comments are available in !comments, 0 = no comments.'),
  1075. '!comments' => t('One or more comments if available, otherwise empty.') .'<br />',
  1076. );
  1077. if ($mailkey == 'node-nid' || $mailkey == 'digest') {
  1078. $tokens['!comments'] = $tokens['!comments'] . t('The rendering of the comments is defined by the template below.');
  1079. }
  1080. else {
  1081. $tokens['!comments'] = $tokens['!comments'] . t('The rendering of the comments is defined by the !link.', array(
  1082. '!link' => '<a href="../subscriptions_content_node-nid/'. check_plain(subscriptions_arg(4)) .'#edit-comment-body">'. t('comment templates') .'</a>'));
  1083. }
  1084. }
  1085. return $tokens;
  1086. }
  1087. /**
  1088. * Implementation of hook_mail_edit_text().
  1089. *
  1090. * Provide default template strings for mail_edit.
  1091. *
  1092. * @ingroup hooks
  1093. */
  1094. function subscriptions_content_mail_edit_text($mailkey, $language) {
  1095. if (!($return = module_invoke('subscriptions_mail', 'subscriptions_mail_text', $mailkey, $language))) {
  1096. $return = module_invoke_all('subscriptions_mail_text', $mailkey, $language);
  1097. }
  1098. return $return;
  1099. }
  1100. /**
  1101. * Implementation of hook_form_alter().
  1102. *
  1103. * Add the comment parts to the subscriptions_content_node-nid mail_edit page.
  1104. *
  1105. * @ingroup hooks
  1106. * @ingroup form
  1107. */
  1108. function subscriptions_content_form_mail_edit_trans_alter(&$form, &$form_state) {
  1109. $mailkey = 'subscriptions_content_node-nid';
  1110. if ($form['id']['#value'] == $mailkey) {
  1111. $tr = 't';
  1112. $langcode = $form['language']['#value'];
  1113. $form['mail']['note'] = array(
  1114. '#value' => '<div>'. t("Note: 'new' and 'update' notifications for %Pages subscriptions use the matching !content_type subscriptions template rather than this one, if allowed; this does not apply to 'comment'-only notifications.", array('%Pages' => t('Pages/Threads'), '!content_type' => t('content type'))) .'</div>',
  1115. '#weight' => -1,
  1116. );
  1117. $comment_body = subscriptions_mail_template_load(SUBSCRIPTIONS_COMMENT_MAILKEY, $langcode, 'body', 'CITEM');
  1118. $separator = subscriptions_mail_template_load(SUBSCRIPTIONS_COMMENT_MAILKEY, $langcode, 'subject', 'SEP');
  1119. $form['mail']['comment_body'] = array(
  1120. '#title' => t('Comment body'),
  1121. '#type' => 'textarea',
  1122. '#default_value' => $comment_body,
  1123. '#rows' => 7,
  1124. '#description' => t('The body of each comment.'),
  1125. );
  1126. $placeholders = subscriptions_content_mail_edit_tokens_list('comments');
  1127. // Display the user documentation of placeholders supported by this module mailkey
  1128. $doc = "<dl>\n";
  1129. foreach ($placeholders as $name => $description) {
  1130. $doc .= '<dt>'. $name .'</dt>';
  1131. $doc .= '<dd>'. $description .'</dd>';
  1132. }
  1133. $doc .= "</dl>\n";
  1134. $form['mail']['comment_token_help'] = array(
  1135. '#title' => $tr('Replacement patterns'),
  1136. '#type' => 'fieldset',
  1137. '#collapsible' => TRUE,
  1138. '#collapsed' => TRUE,
  1139. );
  1140. $form['mail']['comment_token_help']['help'] = array(
  1141. '#value' => $doc,
  1142. );
  1143. $form['mail']['comment_separator'] = array(
  1144. '#title' => t('Comment separator'),
  1145. '#type' => 'textarea',
  1146. '#default_value' => $separator,
  1147. '#rows' => 2,
  1148. '#description' => t('The separator between comments (if needed).'),
  1149. );
  1150. $form['op']['#submit'][] = 'subscriptions_content_form_mail_edit_trans_save';
  1151. if (isset($form['delete'])) {
  1152. $form['delete']['#submit'][] = 'subscriptions_content_form_mail_edit_trans_delete';
  1153. }
  1154. }
  1155. }
  1156. /**
  1157. * Save handler for enhanced mail_edit page.
  1158. */
  1159. function subscriptions_content_form_mail_edit_trans_save($form, &$form_state) {
  1160. $form_state['values']['description'] = '';
  1161. $form_state['values']['subject'] = $form_state['values']['comment_separator'];
  1162. $form_state['values']['body'] = $form_state['values']['comment_body'];
  1163. $form_state['values']['id'] .= '+comment';
  1164. mail_edit_trans_save($form, $form_state);
  1165. }
  1166. /**
  1167. * Delete handler for enhanced mail_edit page.
  1168. */
  1169. function subscriptions_content_form_mail_edit_trans_delete($form, &$form_state) {
  1170. db_query("DELETE FROM {mail_edit} WHERE id = '%s' AND language = '%s'", $form_state['values']['id'] .'+comment', $form_state['values']['language']);
  1171. }
  1172. /**
  1173. * Implementation of hook_node_type().
  1174. *
  1175. * Remove node type subscriptions when the underlying node type is removed.
  1176. *
  1177. * @ingroup hooks
  1178. */
  1179. function subscriptions_content_node_type($op, $info) {
  1180. switch ($op) {
  1181. case 'delete':
  1182. $type = $info->type;
  1183. db_query("DELETE FROM {subscriptions_queue} WHERE module = 'node' AND field = 'type' AND value = '%s'", $type);
  1184. db_query("DELETE FROM {subscriptions} WHERE module = 'node' AND field = 'type' AND value = '%s'", $type);
  1185. foreach (array('blocked', 'unlisted') as $key) {
  1186. $array = variable_get('subscriptions_'. $key .'_content_types', array());
  1187. unset($array[$type]);
  1188. variable_set('subscriptions_'. $key .'_content_types', $array);
  1189. }
  1190. break;
  1191. }
  1192. }
  1193. /**
  1194. * Implementation of hook_cron().
  1195. *
  1196. * Ensure that we're heavier than the taxonomy.module.
  1197. *
  1198. * @ingroup hooks
  1199. */
  1200. function subscriptions_content_cron() {
  1201. $result = db_query("SELECT name, weight FROM {system} WHERE name IN ('taxonomy', 'subscriptions_content') AND type = 'module'");
  1202. while ($module = db_fetch_array($result)) {
  1203. $weights[$module['name']] = $module['weight'];
  1204. }
  1205. if ($weights['subscriptions_content'] <= $weights['taxonomy']) {
  1206. db_query("UPDATE {system} SET weight = %d WHERE name = 'subscriptions_content' AND type = 'module'", $weights['taxonomy'] + 1);
  1207. }
  1208. }
  1209. /**
  1210. * Implementation of hook_disable().
  1211. *
  1212. * Remove our queue items.
  1213. *
  1214. * @ingroup hooks
  1215. */
  1216. function subscriptions_content_disable() {
  1217. db_query("DELETE FROM {subscriptions_queue} WHERE load_function LIKE 'subscriptions_content_%'");
  1218. }