simplenews_scheduler.module 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. <?php
  2. /**
  3. * @file
  4. * Simplenews Scheduler module allows a schedule to be set
  5. * for sending (and resending) a Simplenews item.
  6. */
  7. /**
  8. * NEWSLETTER SEND COMMAND
  9. */
  10. define('SIMPLENEWS_COMMAND_SEND_SCHEDULE', 4);
  11. define('SIMPLENEWS_COMMAND_SEND_NONE', 5);
  12. /**
  13. * Implements hook_permission().
  14. */
  15. function simplenews_scheduler_permission() {
  16. return array(
  17. 'overview scheduled newsletters' => array(
  18. 'title' => t('View scheduled newsletters'),
  19. 'description' => t('Access overview page for scheduled newsletters.'),
  20. ),
  21. 'send scheduled newsletters' => array(
  22. 'title' => t('Send scheduled newsletters'),
  23. 'description' => t('Allows users to use scheduled newsletter sending option.'),
  24. ),
  25. );
  26. }
  27. /**
  28. * Implements hook_menu().
  29. */
  30. function simplenews_scheduler_menu() {
  31. $items = array();
  32. $items["node/%node/editions"] = array(
  33. 'title' => 'Newsletter Editions',
  34. 'type' => MENU_LOCAL_TASK,
  35. 'weight' => 2,
  36. 'page callback' => 'simplenews_scheduler_node_page',
  37. 'page arguments' => array(1),
  38. 'access callback' => '_simplenews_scheduler_tab_permission',
  39. 'access arguments' => array(1),
  40. );
  41. return $items;
  42. }
  43. /**
  44. * Implements hook_form_FORM_ID_alter().
  45. *
  46. * @todo replace the "This newsletter has been sent" checkbox of simplenews module
  47. * by a message like "Last edition of this newsletter was sent at 12.12.2012"
  48. */
  49. function simplenews_scheduler_form_simplenews_node_tab_send_form_alter(&$form, &$form_state) {
  50. global $user;
  51. // Add schedule settings to the send newsletter form.
  52. if (user_access('send scheduled newsletters')) {
  53. // Make sure that this is not an edition.
  54. $node = node_load($form['nid']['#value']);
  55. // Only add the schedule send options if the newsletter has not been sent,
  56. // in which case there is no send form element.
  57. if (isset($form['simplenews']['send']) && !isset($node->simplenews_scheduler_edition)) {
  58. // Set the default values.
  59. $form['#submit'][] = "simplenews_scheduler_submit";
  60. $scheduler = array();
  61. $record = db_select('simplenews_scheduler', 's')
  62. ->fields('s')
  63. ->condition('nid', $node->nid)
  64. ->execute()
  65. ->fetchAssoc();
  66. if (!empty($record)) {
  67. $scheduler = $record;
  68. }
  69. else {
  70. $scheduler['activated'] = 0;
  71. }
  72. $form_state['simplenews_scheduler'] = $scheduler;
  73. $form['simplenews']['send']['#options'] += array(
  74. SIMPLENEWS_COMMAND_SEND_SCHEDULE => t('Send newsletter according to schedule'),
  75. SIMPLENEWS_COMMAND_SEND_NONE => t("Stop newsletter schedule"),
  76. );
  77. $form['simplenews']['send']['#default_value'] = ($scheduler['activated'] == 1) ? SIMPLENEWS_COMMAND_SEND_SCHEDULE : variable_get('simplenews_send', SIMPLENEWS_COMMAND_SEND_NONE);
  78. $form['simplenews']['scheduler'] = array(
  79. '#type' => 'fieldset',
  80. '#title' => t('Schedule details'),
  81. '#attributes' => array('class' => array('schedule-info')),
  82. '#collapsible' => TRUE,
  83. '#collapsed' => FALSE,
  84. '#states' => array(
  85. 'visible' => array(':input[name="simplenews[send]"]' => array('value' => (string) SIMPLENEWS_COMMAND_SEND_SCHEDULE)),
  86. ),
  87. );
  88. // If there is no default value, use the current time for start.
  89. $start_date = !empty($scheduler['start_date']) ? $scheduler['start_date'] : REQUEST_TIME;
  90. // and Today + 2 years for stop, that should be enough.
  91. $stop_date = !empty($scheduler['stop_date']) ? $scheduler['stop_date'] : REQUEST_TIME + 2 * 365 * 24 * 60 * 60;
  92. $form['simplenews']['scheduler']['start_date'] = array(
  93. '#type' => 'date_select',
  94. '#title' => t('Start sending on'),
  95. '#default_value' => date('Y-m-d H:i', $start_date),
  96. '#required' => TRUE,
  97. '#date_format' => 'Y-m-d H:i',
  98. '#date_label_position' => 'within',
  99. '#date_year_range' => '-0:+3',
  100. '#description' => t('Intervals work by creating a new node at the
  101. desired time and marking this to be sent, ensure
  102. you have your <a href="@site">site timezones</a>
  103. configured and <a href="@user">user timezone</a>
  104. configured.', array('@site' => url('admin/config/date-time'), '@user' => url('user/' . $user->uid . '/edit'))),
  105. );
  106. $intervals = array(
  107. 'hour' => t('Hour'),
  108. 'day' => t('Day'),
  109. 'week' => t('Week'),
  110. 'month' => t('Month'),
  111. );
  112. $form['simplenews']['scheduler']['interval'] = array(
  113. '#type' => 'select',
  114. '#title' => t('Sending interval'),
  115. '#options' => $intervals,
  116. '#description' => t('Interval to send at'),
  117. '#default_value' => !empty($scheduler['send_interval']) ? $scheduler['send_interval'] : 'week',
  118. );
  119. $form['simplenews']['scheduler']['frequency'] = array(
  120. '#type' => 'textfield',
  121. '#title' => t('Interval frequency'),
  122. '#size' => 5,
  123. '#default_value' => !empty($scheduler['interval_frequency']) ? $scheduler['interval_frequency'] : 1,
  124. '#description' => t('Set the number of Intervals between newsletter transmission.'),
  125. );
  126. $stoptypes = array(
  127. t('Never'),
  128. t('On a given date'),
  129. t('After a maximum number of editions')
  130. );
  131. $form['simplenews']['scheduler']['stoptype'] = array(
  132. '#type' => 'radios',
  133. '#title' => t('Stop sending'),
  134. '#options' => $stoptypes,
  135. '#default_value' => !empty($scheduler['stop_type']) ? $scheduler['stop_type'] : 0,
  136. '#attributes' => array('class' => array('simplenews-command-stop')),
  137. );
  138. $form['simplenews']['scheduler']['stop_edition'] = array(
  139. '#type' => 'textfield',
  140. '#default_value' => isset($scheduler['stop_edition']) ? $scheduler['stop_edition'] : 0,
  141. '#size' => 5,
  142. '#maxlength' => 5,
  143. '#required' => TRUE,
  144. '#description' => t('The maximum number of editions which should be sent.'),
  145. '#states' => array(
  146. 'visible' => array(':input[name="simplenews[scheduler][stoptype]"]' => array('value' => (string) 2)),
  147. ),
  148. );
  149. $form['simplenews']['scheduler']['stop_date'] = array(
  150. '#type' => 'date_select',
  151. '#title' => t('Stop sending on'),
  152. '#default_value' => date('Y-m-d H:i', $stop_date),
  153. '#required' => TRUE,
  154. '#date_format' => 'Y-m-d H:i',
  155. '#date_label_position' => 'within',
  156. '#date_year_range' => '-0:+3',
  157. '#description' => t('The date when the last sent newsletter will be sent.'),
  158. '#states' => array(
  159. 'visible' => array(':input[name="simplenews[scheduler][stoptype]"]' => array('value' => (string) 1)),
  160. ),
  161. );
  162. $form['simplenews']['scheduler']['php_eval'] = array(
  163. '#type' => 'textarea',
  164. '#title' => t('Additionally only create newsletter edition if the following code returns true'),
  165. '#default_value' => isset($scheduler['php_eval']) ? $scheduler['php_eval'] : '',
  166. '#required' => FALSE,
  167. '#description' => t('Additionally evaluate the following PHP code and only issue the newsletter edition if it returns true. Do not include &lt;?php ?&gt; tags.'),
  168. '#access' => user_access('use PHP for settings'),
  169. );
  170. $form['simplenews']['scheduler']['title'] = array(
  171. '#type' => 'textfield',
  172. '#title' => t('Title pattern for new edition nodes'),
  173. '#description' => t('New edition nodes will have their title set to the above string, with tokens replaced.'),
  174. '#required' => TRUE,
  175. '#default_value' => isset($scheduler['title']) ? $scheduler['title'] : '[node:title]',
  176. );
  177. $form['simplenews']['scheduler']['token_help'] = array(
  178. '#title' => t('Replacement patterns'),
  179. '#type' => 'fieldset',
  180. '#collapsible' => TRUE,
  181. '#collapsed' => TRUE,
  182. );
  183. $form['simplenews']['scheduler']['token_help']['help'] = array(
  184. '#theme' => 'token_tree',
  185. '#token_types' => array('node'),
  186. );
  187. $form['simplenews']['scheduler']['activated'] = array(
  188. '#type' => 'value',
  189. '#value' => $scheduler['activated'],
  190. );
  191. }
  192. elseif (isset($node->simplenews_scheduler_edition)) {
  193. // This is a newsletter edition.
  194. $form['simplenews']['none']['#title'] = t('This node is part of a scheduled newsletter configuration. View the original newsletter <a href="@parent">here</a>.', array('@parent' => url('node/' . $node->simplenews_scheduler_edition->pid)));
  195. }
  196. }
  197. }
  198. /**
  199. * Additional submit handler for the node_tab_send_form of simplenews.
  200. */
  201. function simplenews_scheduler_submit($form, &$form_state) {
  202. $scheduler = $form_state['simplenews_scheduler'];
  203. $nid = $form_state['values']['nid'];
  204. $node = node_load($nid);
  205. // Get Scheduler values from Simplenews.
  206. $send = $form_state['values']['simplenews']['send'];
  207. $stoptype = $form_state['values']['simplenews']['scheduler']['stoptype'];
  208. $start_date = strtotime($form_state['values']['simplenews']['scheduler']['start_date']);
  209. $stop_date = ($stoptype == 1) ? strtotime($form_state['values']['simplenews']['scheduler']['stop_date']) : 0;
  210. $record = array(
  211. 'nid' => $nid,
  212. 'activated' => $send == SIMPLENEWS_COMMAND_SEND_SCHEDULE ? 1 : 0,
  213. 'send_interval' => $form_state['values']['simplenews']['scheduler']['interval'],
  214. 'interval_frequency' => $form_state['values']['simplenews']['scheduler']['frequency'],
  215. 'start_date' => $start_date,
  216. 'stop_type' => $stoptype,
  217. 'stop_date' => $stop_date,
  218. 'stop_edition' => $form_state['values']['simplenews']['scheduler']['stop_edition'],
  219. 'php_eval' => $form_state['values']['simplenews']['scheduler']['php_eval'],
  220. 'title' => $form_state['values']['simplenews']['scheduler']['title'],
  221. );
  222. // For a new scheduler, add the next_run time.
  223. if (!isset($scheduler['next_run'])) {
  224. $record['next_run'] = $start_date;
  225. }
  226. // Update scheduler record.
  227. db_merge('simplenews_scheduler')
  228. ->key(array(
  229. 'nid' => $nid,
  230. ))
  231. ->fields($record)
  232. ->execute();
  233. drupal_set_message(t('Newsletter Schedule preferences have been saved.'));
  234. }
  235. /**
  236. * Implements hook_node_load().
  237. */
  238. function simplenews_scheduler_node_load($nodes, $types) {
  239. $nids = array_keys($nodes);
  240. $result = db_select('simplenews_scheduler', 's')
  241. ->fields('s')
  242. ->condition('nid', $nids, 'IN')
  243. ->execute()
  244. ->fetchAll();
  245. foreach ($result as $key => $record) {
  246. $nodes[$record->nid]->simplenews_scheduler = $record;
  247. }
  248. $result = db_select('simplenews_scheduler_editions', 's')
  249. ->fields('s')
  250. ->condition('eid', $nids, 'IN')
  251. ->execute()
  252. ->fetchAll();
  253. foreach ($result as $key => $record) {
  254. $nodes[$record->eid]->simplenews_scheduler_edition = $record;
  255. $nodes[$record->eid]->is_edition = TRUE;
  256. $nodes[$record->eid]->simplenews_edition_parent = $record->pid;
  257. }
  258. }
  259. /**
  260. * Implements hook_node_delete().
  261. */
  262. function simplenews_scheduler_node_delete($node) {
  263. db_delete('simplenews_scheduler')
  264. ->condition('nid', $node->nid)
  265. ->execute();
  266. }
  267. /**
  268. * Implements hook_node_view().
  269. */
  270. function simplenews_scheduler_node_view($node) {
  271. if (isset($node->simplenews_scheduler_edition) && user_access('send scheduled newsletters')) {
  272. drupal_set_message(t('This is a newsletter edititon. View the the master template of this newsletter <a href="!master_url">here</a>', array('!master_url' => url('node/' . $node->simplenews_edition_parent))));
  273. }
  274. }
  275. /**
  276. * Implements hook_cron().
  277. *
  278. * Essentially we are just checking against a status table
  279. * and cloning the node into edition nodes which will be sent.
  280. */
  281. function simplenews_scheduler_cron() {
  282. // Get the newsletters that need to be sent at this time.
  283. $now_time = REQUEST_TIME;
  284. $newsletters_to_send = simplenews_scheduler_get_newsletters_due($now_time);
  285. foreach ($newsletters_to_send as $newsletter_parent_data) {
  286. $edition_time = simplenews_scheduler_calculate_edition_time($newsletter_parent_data, $now_time);
  287. // Create a new edition.
  288. $eid = _simplenews_scheduler_new_edition($newsletter_parent_data->nid, $edition_time);
  289. // Update the edition record.
  290. simplenews_scheduler_scheduler_update($newsletter_parent_data, $now_time);
  291. // Send it.
  292. _simplenews_scheduler_send_new_edition($edition_time, $newsletter_parent_data, $eid);
  293. }
  294. }
  295. /**
  296. * Updates a scheduler record with any housekeeping changes and saves it.
  297. *
  298. * This should be called once a new edition has been created. This sets the
  299. * next_run time on the scheduler.
  300. *
  301. * @todo: Make this a general API function for saving a new or existing scheduler?
  302. *
  303. * @param $newsletter_parent_data
  304. * A row of data from {simplenews_scheduler}, as returned by
  305. * simplenews_scheduler_get_newsletters_due().
  306. * @param $now_time
  307. * The time of the operation.
  308. */
  309. function simplenews_scheduler_scheduler_update($newsletter_parent_data, $now_time) {
  310. // Set the run time for the next edition.
  311. $newsletter_parent_data->next_run = simplenews_scheduler_calculate_next_run_time($newsletter_parent_data, $now_time);
  312. drupal_write_record('simplenews_scheduler', $newsletter_parent_data, 'nid');
  313. }
  314. /**
  315. * Calculates time for the current edition about to be created.
  316. *
  317. * Because cron may run after the scheduled timestamp, one or more scheduled
  318. * edition times may have been skipped. This calculates the most recent
  319. * possible time for an edition.
  320. *
  321. * @param $newsletter_parent_data
  322. * A row of data from {simplenews_scheduler}, as returned by
  323. * simplenews_scheduler_get_newsletters_due().
  324. * @param $now_time
  325. * The time of the operation.
  326. *
  327. * @return
  328. * The calculcated creation time of the newsletter edition.
  329. */
  330. function simplenews_scheduler_calculate_edition_time($newsletter_parent_data, $now_time) {
  331. // Make an offset string of the format '+1 month'.
  332. $offset_string = _simplenews_scheduler_make_time_offset($newsletter_parent_data->send_interval, $newsletter_parent_data->interval_frequency);
  333. // Make a DateInterval object that represents this.
  334. $date_interval = DateInterval::createFromDateString($offset_string);
  335. // Take the last run time and add as many intervals as possible without going
  336. // past 'now'.
  337. // Create a date object to act as a pointer we'll advance and increment.
  338. if ($newsletter_parent_data->last_run) {
  339. // Generate a date string to initialize a DateTime() object, otherwise the
  340. // timezone is ignored.
  341. $start_date = date('Y-m-d H:i:s', $newsletter_parent_data->last_run);
  342. }
  343. else {
  344. $start_date = date('Y-m-d H:i:s', $newsletter_parent_data->start_date);
  345. }
  346. // Initialize the DateTime object using the configured ste timezone.
  347. $pointer_date = new DateTime($start_date);
  348. while ($pointer_date->getTimestamp() <= $now_time) {
  349. // Get the last iteration's timestamp before we change the pointer.
  350. $timestamp_old = $pointer_date->getTimestamp();
  351. // Add interval to the pointer time.
  352. $pointer_date->add($date_interval);
  353. // Check if the pointer is now in the future.
  354. if ($pointer_date->getTimestamp() > $now_time) {
  355. // If so, return the last iteration timestamp as the edition time.
  356. return $timestamp_old;
  357. }
  358. }
  359. }
  360. /**
  361. * Calculates time for the next edition to be sent.
  362. *
  363. * This is set in the {simplenews_scheduler} table when a new edition is run,
  364. * for subsequent cron runs to query against.
  365. *
  366. * The time is strictly in the future; that is, if the $now_time is a valid
  367. * edition time, a schedule interval is added to it. This is to allow for cron
  368. * runs that need to calculate the next run time at the time of the current
  369. * edition being sent.
  370. *
  371. * @param $newsletter_parent_data
  372. * A row of data from {simplenews_scheduler}, as returned by
  373. * simplenews_scheduler_get_newsletters_due().
  374. * @param $now_time
  375. * The time of the operation.
  376. *
  377. * @return
  378. * The calculcated run time for the next future edition.
  379. */
  380. function simplenews_scheduler_calculate_next_run_time($newsletter_parent_data, $now_time) {
  381. // Make an offset string of the format '+1 month'.
  382. $offset_string = _simplenews_scheduler_make_time_offset($newsletter_parent_data->send_interval, $newsletter_parent_data->interval_frequency);
  383. // Make a DateInterval object that represents this.
  384. $date_interval = DateInterval::createFromDateString($offset_string);
  385. // Create a date object to act as a pointer we'll advance and increment.
  386. if ($newsletter_parent_data->last_run) {
  387. // Generate a date string to initialize a DateTime() object, otherwise the
  388. // timezone is ignored.
  389. $start_date = date('Y-m-d H:i:s', $newsletter_parent_data->last_run);
  390. }
  391. else {
  392. $start_date = date('Y-m-d H:i:s', $newsletter_parent_data->start_date);
  393. }
  394. // Initialize the DateTime object using the configured ste timezone.
  395. $pointer_date = new DateTime($start_date);
  396. // Add as many offsets as possible until we get into the future.
  397. while ($pointer_date->getTimestamp() <= $now_time) {
  398. // Add interval to the pointer time.
  399. $pointer_date->add($date_interval);
  400. }
  401. return $pointer_date->getTimestamp();
  402. }
  403. /**
  404. * Helper to create a PHP time offset string.
  405. *
  406. * @param $interval
  407. * A time interval. One of hour, day, week, month.
  408. * @param $frequency
  409. * An integer that specifies how many of the $interval to create an offset for.
  410. *
  411. * @return
  412. * A string representing a time offset that can be understood by strtotime(),
  413. * eg '+1 month'.
  414. */
  415. function _simplenews_scheduler_make_time_offset($interval, $frequency) {
  416. $offset_string = "+{$frequency} {$interval}";
  417. return $offset_string;
  418. }
  419. /**
  420. * Get the newsletters that need to have new editions sent.
  421. *
  422. * This is a helper function for hook_cron that has the current date abstracted
  423. * out so it can be tested.
  424. *
  425. * @param $timestamp
  426. * A unix timestamp at which to determine which newsletters are due to be
  427. * sent. In ordinary operation this should be the current time.
  428. *
  429. * @return
  430. * An array of newsletter data arrays in the form of rows from the
  431. * {simplenews_scheduler} table, keyed by newsletter nid.
  432. */
  433. function simplenews_scheduler_get_newsletters_due($timestamp) {
  434. // Get all newsletters that need to be sent.
  435. $result = db_query("SELECT * FROM {simplenews_scheduler} WHERE activated = 1 AND next_run <= :now AND (stop_date > :now OR stop_date = 0)", array(':now' => $timestamp));
  436. $newsletters = array();
  437. foreach ($result as $newsletter_parent_data) {
  438. // The node id of the parent node.
  439. $pid = $newsletter_parent_data->nid;
  440. // Check upon if sending should stop with a given edition number.
  441. $stop = $newsletter_parent_data->stop_type;
  442. $stop_edition = $newsletter_parent_data->stop_edition;
  443. $edition_count = db_query('SELECT COUNT(*) FROM {simplenews_scheduler_editions} WHERE pid = :pid', array(':pid' => $pid))->fetchField();
  444. // Don't create new edition if the edition number would exceed the given maximum value.
  445. if ($stop == 2 && $edition_count >= $stop_edition) {
  446. continue;
  447. }
  448. // does this newsletter have something to evaluate to check running condition?
  449. if (strlen($newsletter_parent_data->php_eval)) {
  450. $eval_result = eval($newsletter_parent_data->php_eval);
  451. if (!$eval_result) {
  452. continue;
  453. }
  454. }
  455. $newsletters[$pid] = $newsletter_parent_data;
  456. }
  457. return $newsletters;
  458. }
  459. /**
  460. * Helper for hook_cron() to send a new edition.
  461. *
  462. * @param $edition_time
  463. * The time of the operation. Usually the current time unless testing.
  464. * @param $newsletter_parent_data
  465. * A row of data from {simplenews_scheduler}, as returned by
  466. * simplenews_scheduler_get_newsletters_due().
  467. * @param $eid
  468. * The node id of the new edition to send. This should already have been
  469. * created by _simplenews_scheduler_new_edition().
  470. */
  471. function _simplenews_scheduler_send_new_edition($edition_time, $newsletter_parent_data, $eid) {
  472. $pid = $newsletter_parent_data->nid;
  473. // persist last_run
  474. db_update('simplenews_scheduler')
  475. ->fields(array('last_run' => $edition_time))
  476. ->condition('nid', $pid)
  477. ->execute();
  478. // Send the newsletter edition to each subscriber of the parent newsletter.
  479. $node = node_load($eid);
  480. module_load_include('inc', 'simplenews', 'includes/simplenews.mail');
  481. simplenews_add_node_to_spool($node);
  482. }
  483. /**
  484. * Function clones a node from the given template newsletter node.
  485. */
  486. function simplenews_scheduler_clone_node($node) {
  487. if (isset($node->nid)) {
  488. $clone = clone $node;
  489. $clone->nid = NULL;
  490. $clone->vid = NULL;
  491. $clone->tnid = NULL;
  492. $clone->created = NULL;
  493. $clone->book['mlid'] = NULL;
  494. $clone->path = NULL;
  495. //$clone->title = $original_node->title;
  496. // Add an extra property as a flag.
  497. $clone->clone_from_original_nid = $node->nid;
  498. node_save($clone);
  499. return $clone;
  500. }
  501. }
  502. /**
  503. * Menu callback to provide an overview page with the scheduled newsletters.
  504. *
  505. * @todo replace the output of this function with a default view that
  506. * will be provided by the views integration of this module. Code below
  507. * is ported from D6!
  508. */
  509. function simplenews_scheduler_node_page($node) {
  510. drupal_set_title(t('Scheduled newsletter editions'));
  511. $nid = _simplenews_scheduler_get_pid($node);
  512. $output = '';
  513. $rows = array();
  514. if ($nid == $node->nid) { // This is the template newsletter.
  515. $output .= '<p>' . t('This is a newsletter template node of which all corresponding editions nodes are based on.') . '</p>';
  516. }
  517. else { // This is a newsletter edition.
  518. $output .= '<p>' . t('This node is part of a scheduled newsletter configuration. View the original newsletter <a href="@parent">here</a>.', array('@parent' => url('node/' . $nid))) . '</p>';
  519. }
  520. // Load the corresponding editions from the database to further process.
  521. $result = db_select('simplenews_scheduler_editions', 's')
  522. ->extend('PagerDefault')
  523. ->limit(20)
  524. ->fields('s')
  525. ->condition('s.pid', $nid)
  526. ->execute()
  527. ->fetchAll();
  528. foreach ($result as $row) {
  529. $node = node_load($row->eid);
  530. $rows[] = array(l($node->title, 'node/' . $row->eid), format_date($row->date_issued, 'custom', 'Y-m-d H:i'));
  531. }
  532. // Display a table with all editions.
  533. $tablecontent = array(
  534. 'header' => array(t('Edition Node'), t('Date sent')),
  535. 'rows' => $rows,
  536. 'attributes' => array('class' => array('schedule-history')),
  537. 'empty' => '<p>' . t('No scheduled newsletter editions have been sent.') . '</p>',
  538. );
  539. $output .= theme('table', $tablecontent);
  540. $output .= theme('pager', array('tags' => 20));
  541. return $output;
  542. }
  543. /**
  544. * Check whether to display the Scheduled Newsletter tab.
  545. */
  546. function _simplenews_scheduler_tab_permission($node) {
  547. // Check if this is a simplenews node type and permission.
  548. if (simplenews_check_node_types($node->type) && user_access('overview scheduled newsletters')) {
  549. // Check if this is either a scheduler newsletter or an edition.
  550. return !empty($node->simplenews_scheduler) || !empty($node->is_edition);
  551. }
  552. }
  553. /**
  554. * Find Full HTML input format.
  555. *
  556. * Use the Drupal API for finding the Full HTML input format, this is what the subsequent newsletter editions
  557. * need to be set to.
  558. */
  559. function _simplenews_scheduler_get_full_html_format() {
  560. global $user;
  561. $formats = filter_formats($user);
  562. foreach ($formats as $index => $format) {
  563. if (stristr($format->name, 'Full HTML')) {
  564. return $index;
  565. }
  566. }
  567. return false;
  568. }
  569. /**
  570. * Create a new newsletter edition based on the master edition of this newsletter.
  571. *
  572. * This does no checking of whether a new edition should be made; it's up to
  573. * the caller to determine this first.
  574. *
  575. * @param $nid
  576. * The node id of the parent newsletter node to use as a template.
  577. * @param $edition_time
  578. * Desired edition creation time.
  579. *
  580. * @return
  581. * The node id of the new edition node.
  582. */
  583. function _simplenews_scheduler_new_edition($nid, $edition_time) {
  584. // Load the template node and clone an edition.
  585. $template_node = node_load($nid);
  586. $edition_node = simplenews_scheduler_clone_node($template_node);
  587. // Set the node's creation time as the given timestamp.
  588. $edition_node->created = $edition_time;
  589. // Run the title through token replacement.
  590. // Get title pattern from the scheduler record, not newsletter node.
  591. // $edition_node->title = token_replace($edition_node->title, array('node' => $edition_node));
  592. $schedrecord = db_select('simplenews_scheduler', 's')
  593. ->fields('s')
  594. ->condition('nid', $template_node->nid)
  595. ->execute()
  596. ->fetchAssoc();
  597. $edition_node->title = token_replace($schedrecord['title'], array('node' => $template_node));
  598. // Invoke simplenews_scheduler_edition_node() to give installed modules a
  599. // chance to modify the cloned edition node if necessary before it gets saved.
  600. drupal_alter('simplenews_scheduler_edition_node', $edition_node, $template_node);
  601. // Save the changes of other modules
  602. node_save($edition_node);
  603. // Insert edition data.
  604. $values = array(
  605. 'eid' => $edition_node->nid,
  606. 'pid' => $template_node->nid,
  607. 'date_issued' => $edition_time,
  608. );
  609. db_insert('simplenews_scheduler_editions')
  610. ->fields($values)
  611. ->execute();
  612. // Add a watchdog entry.
  613. $variables = array('%title' => entity_label('node', $edition_node));
  614. $uri = entity_uri('node', $edition_node);
  615. $link = l(t('view'), $uri['path'], $uri['options']);
  616. watchdog('simplenews_sched', 'Created a new newsletter edition %title', $variables, WATCHDOG_NOTICE, $link);
  617. // Prepare the correct status for Simplenews to pickup.
  618. simplenews_newsletter_update_sent_status($edition_node);
  619. return $edition_node->nid;
  620. }
  621. /**
  622. * Helper function to get the identifier of newsletter.
  623. *
  624. * @param $node
  625. * The node object for the newsletter.
  626. *
  627. * @return
  628. * If the node is a newsletter edition, the node id of its parent template
  629. * newsletter; if the node is a template newsletter, its own node id; and
  630. * FALSE if the node is not part of a scheduled newsletter set.
  631. */
  632. function _simplenews_scheduler_get_pid($node) {
  633. // First assume this is a newsletter edition,
  634. if (isset($node->simplenews_scheduler_edition)) {
  635. return $node->simplenews_scheduler_edition->pid;
  636. }
  637. // or this itself is the parent newsletter.
  638. elseif (isset($node->simplenews_scheduler)) {
  639. return $node->nid;
  640. }
  641. return FALSE;
  642. }