rules_scheduler.module 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /**
  3. * @file
  4. * Rules scheduler module.
  5. */
  6. define('RULES_SCHEDULER_PATH', 'admin/config/workflow/rules/schedule');
  7. /**
  8. * Implements hook_cron().
  9. */
  10. function rules_scheduler_cron() {
  11. if (rules_scheduler_queue_tasks()) {
  12. // hook_exit() is not invoked for cron runs, so register it as shutdown
  13. // callback for logging the rules log to the watchdog.
  14. drupal_register_shutdown_function('rules_exit');
  15. // Clear the log before running tasks via the queue to avoid logging
  16. // unrelated logs from previous cron-operations.
  17. RulesLog::logger()->clear();
  18. }
  19. }
  20. /**
  21. * Implements hook_cron_queue_info().
  22. */
  23. function rules_scheduler_cron_queue_info() {
  24. $queues['rules_scheduler_tasks'] = array(
  25. 'worker callback' => 'rules_scheduler_run_task',
  26. 'time' => 15,
  27. );
  28. return $queues;
  29. }
  30. /**
  31. * Queue worker callback for running a single task.
  32. *
  33. * @param array $task
  34. * The task to process.
  35. */
  36. function rules_scheduler_run_task(array $task) {
  37. try {
  38. // BC support for tasks that have been already queued, before update
  39. // rules_scheduler_update_7204() ran.
  40. if (isset($task['state'])) {
  41. $task['data'] = $task['state'];
  42. }
  43. rules_scheduler_task_handler($task)->runTask();
  44. }
  45. catch (RulesEvaluationException $e) {
  46. rules_log($e->msg, $e->args, $e->severity);
  47. rules_log('Unable to execute task with identifier %id scheduled on date %date.', array('%id' => $task['identifier'], '%date' => format_date($task['date'])), RulesLog::ERROR);
  48. }
  49. }
  50. /**
  51. * Returns the task handler for a given task.
  52. *
  53. * @param array $task
  54. * A task (queue item) array.
  55. *
  56. * @throws RulesEvaluationException
  57. * If the task handler class is missing.
  58. *
  59. * @return RulesSchedulerTaskHandlerInterface
  60. * The task handler.
  61. */
  62. function rules_scheduler_task_handler(array $task) {
  63. $class = !empty($task['handler']) ? $task['handler'] : 'RulesSchedulerDefaultTaskHandler';
  64. if (!class_exists($class)) {
  65. throw new RulesEvaluationException('Missing task handler implementation %class.', array('%class' => $class), NULL, RulesLog::ERROR);
  66. }
  67. return new $class($task);
  68. }
  69. /**
  70. * Implements hook_rules_ui_menu_alter().
  71. *
  72. * Adds a menu item for the 'schedule' operation.
  73. */
  74. function rules_scheduler_rules_ui_menu_alter(&$items, $base_path, $base_count) {
  75. $items[$base_path . '/manage/%rules_config/schedule'] = array(
  76. 'title callback' => 'rules_get_title',
  77. 'title arguments' => array('Schedule !plugin "!label"', $base_count + 1),
  78. 'page callback' => 'drupal_get_form',
  79. 'page arguments' => array('rules_scheduler_schedule_form', $base_count + 1, $base_path),
  80. 'access callback' => 'rules_config_access',
  81. 'access arguments' => array('update', $base_count + 1),
  82. 'file' => 'rules_scheduler.admin.inc',
  83. 'file path' => drupal_get_path('module', 'rules_scheduler'),
  84. );
  85. }
  86. /**
  87. * Implements hook_menu().
  88. */
  89. function rules_scheduler_menu() {
  90. $items = array();
  91. $items[RULES_SCHEDULER_PATH] = array(
  92. 'title' => 'Schedule',
  93. 'type' => MENU_LOCAL_TASK,
  94. 'page callback' => 'rules_scheduler_schedule_page',
  95. 'access arguments' => array('administer rules'),
  96. 'file' => 'rules_scheduler.admin.inc',
  97. );
  98. $items[RULES_SCHEDULER_PATH . '/%rules_scheduler_task/delete'] = array(
  99. 'title' => 'Delete a scheduled task',
  100. 'type' => MENU_CALLBACK,
  101. 'page callback' => 'drupal_get_form',
  102. 'page arguments' => array('rules_scheduler_delete_task', 5),
  103. 'access arguments' => array('administer rules'),
  104. 'file' => 'rules_scheduler.admin.inc',
  105. );
  106. return $items;
  107. }
  108. /**
  109. * Loads a task by a given task ID.
  110. *
  111. * @param int $tid
  112. * The task ID.
  113. */
  114. function rules_scheduler_task_load($tid) {
  115. $result = db_select('rules_scheduler', 'r')
  116. ->fields('r')
  117. ->condition('tid', (int) $tid)
  118. ->execute();
  119. return $result->fetchAssoc();
  120. }
  121. /**
  122. * Deletes a task by a given task ID.
  123. *
  124. * @param int $tid
  125. * The task ID.
  126. */
  127. function rules_scheduler_task_delete($tid) {
  128. db_delete('rules_scheduler')
  129. ->condition('tid', $tid)
  130. ->execute();
  131. }
  132. /**
  133. * Schedule a task to be executed later on.
  134. *
  135. * @param array $task
  136. * An array representing the task with the following keys:
  137. * - config: The machine readable name of the to-be-scheduled component.
  138. * - date: Timestamp when the component should be executed.
  139. * - state: (deprecated) Rules evaluation state to use for scheduling.
  140. * - data: Any additional data to store with the task.
  141. * - handler: The name of the task handler class.
  142. * - identifier: User provided string to identify the task per scheduled
  143. * configuration.
  144. */
  145. function rules_scheduler_schedule_task($task) {
  146. // Map the deprecated 'state' property into 'data'.
  147. if (isset($task['state'])) {
  148. $task['data'] = $task['state'];
  149. unset($task['state']);
  150. }
  151. if (!empty($task['identifier'])) {
  152. // If there is a task with the same identifier and component, we replace it.
  153. db_delete('rules_scheduler')
  154. ->condition('config', $task['config'])
  155. ->condition('identifier', $task['identifier'])
  156. ->execute();
  157. }
  158. drupal_write_record('rules_scheduler', $task);
  159. }
  160. /**
  161. * Queue tasks that are ready for execution.
  162. *
  163. * @return bool
  164. * TRUE if any queue items where created, otherwise FALSE.
  165. */
  166. function rules_scheduler_queue_tasks() {
  167. $items_created = FALSE;
  168. // Limit adding tasks to 1000 per cron run.
  169. $result = db_select('rules_scheduler', 'r', array('fetch' => PDO::FETCH_ASSOC))
  170. ->fields('r')
  171. ->condition('date', time(), '<=')
  172. ->orderBy('date')
  173. ->range(0, 1000)
  174. ->execute();
  175. $queue = DrupalQueue::get('rules_scheduler_tasks');
  176. foreach ($result as $task) {
  177. // Add the task to the queue and remove the entry afterwards.
  178. if ($queue->createItem($task)) {
  179. $items_created = TRUE;
  180. rules_scheduler_task_handler($task)->afterTaskQueued();
  181. }
  182. }
  183. return $items_created;
  184. }
  185. /**
  186. * Implements hook_rules_config_delete().
  187. */
  188. function rules_scheduler_rules_config_delete($rules_config) {
  189. // Only react on real delete, not revert.
  190. if (!$rules_config->hasStatus(ENTITY_IN_CODE)) {
  191. // Delete all tasks scheduled for this config.
  192. db_delete('rules_scheduler')
  193. ->condition('config', $rules_config->name)
  194. ->execute();
  195. }
  196. }
  197. /**
  198. * Implements hook_views_api().
  199. */
  200. function rules_scheduler_views_api() {
  201. return array(
  202. 'api' => '3.0-alpha1',
  203. 'path' => drupal_get_path('module', 'rules_scheduler') . '/includes',
  204. );
  205. }