batch.module

  1. 7.x modules/batch/batch.module
  2. 6.x modules/batch/batch.module
  3. 5.x modules/batch/batch.module

File

modules/batch/batch.module
View source
  1. <?php
  2. /**
  3. * The main module file for the Batch module.
  4. *
  5. * Credit where credit is due: This module is largely inspired by the excellent Batch processing functionality
  6. * baked into Drupal (like a lot of FlightPath).
  7. */
  8. /**
  9. * Implementation of hook_menu
  10. *
  11. */
  12. function batch_menu() {
  13. $items = array();
  14. // Our test batch process, for testing...
  15. $items["batch-test-form"] = array(
  16. "title" => "Batch Test - Form",
  17. "page_callback" => "fp_render_form",
  18. "page_arguments" => array("batch_test_form"),
  19. "access_arguments" => array("batch_run_test"),
  20. "type" => MENU_TYPE_CALLBACK,
  21. "file" => menu_get_module_path("batch") . "/batch.test.inc",
  22. );
  23. $items["batch-processing/%"] = array(
  24. "page_callback" => "batch_processing_page",
  25. "page_arguments" => array(1),
  26. "access_callback" => TRUE,
  27. "type" => MENU_TYPE_CALLBACK,
  28. );
  29. $items["batch-finished/%"] = array(
  30. "page_callback" => "batch_finished_page",
  31. "page_arguments" => array(1),
  32. "access_callback" => TRUE,
  33. "type" => MENU_TYPE_CALLBACK,
  34. );
  35. $items["batch-ajax-callback/%"] = array(
  36. "page_callback" => "batch_ajax_callback",
  37. "page_arguments" => array(1),
  38. "access_callback" => TRUE,
  39. "type" => MENU_TYPE_CALLBACK,
  40. );
  41. return $items;
  42. }
  43. /**
  44. * This function is called by ajax, and will trigger each run of the batch operation function, then
  45. * return appropriate results back to our javascript.
  46. *
  47. * @param unknown_type $batch_id
  48. */
  49. function batch_ajax_callback($batch_id) {
  50. $rtn = array();
  51. $batch = batch_get($batch_id);
  52. if ($batch["token"] != batch_get_token()) {
  53. // Not allowed! Bad token.
  54. header('Content-Type: application/json');
  55. print json_encode(array("error" => t("An error has occured-- you are now allowed to run this batch, or the batch does not exist.")));
  56. exit();
  57. }
  58. ///////////////////////////////////
  59. // Run the batch operation.
  60. if (isset($batch["file"]) && $batch["file"] != "") {
  61. require_once($batch["file"]);
  62. }
  63. $operation_callback = $batch["operation"][0];
  64. // If the operation callback doesn't exist, throw an error.
  65. if (!function_exists($operation_callback)) {
  66. header('Content-Type: application/json');
  67. print json_encode(array("error" => t("An error has occured: Cannot find operation callback function @function", array("@function" => addslashes($operation_callback)))));
  68. exit();
  69. }
  70. $operation_args = $batch["operation"][1];
  71. // Add our $batch to the beginning of the $args array.
  72. //array_unshift($operation_args, $batch);
  73. $params = array(0 => &$batch);
  74. foreach ($operation_args as $val) {
  75. $params[] = $val;
  76. }
  77. call_user_func_array($operation_callback, $params);
  78. // Coming out of this, $batch should be modified
  79. // Save it back to the database.
  80. db_query("UPDATE batch_queue
  81. SET batch_data = '?'
  82. WHERE batch_id = '?' ", serialize($batch), $batch_id);
  83. // Output relavant results....
  84. $current = $batch["results"]["current"];
  85. $total = $batch["results"]["total"];
  86. $percent = "";
  87. if ($total > 0) {
  88. $percent = round(($current / $total) * 100, 1) * 1;
  89. }
  90. $rtn["progress_message"] = t($batch["progress_message"], array("@current" => $current, "@total" => $total, "@percent" => $percent));
  91. $rtn["percent"] = $percent;
  92. $rtn["display_percent"] = $batch["display_percent"];
  93. $rtn["finished"] = "";
  94. if ($batch["results"]["finished"] == TRUE) {
  95. $rtn["finished"] = "finished";
  96. }
  97. header('Content-Type: application/json');
  98. $rtn["success"] = "SUCCESS"; // let javascript know we DID finish executing correctly.
  99. print json_encode($rtn);
  100. exit();
  101. }
  102. /**
  103. * We redirect to this page when we have finished a batch.
  104. *
  105. * @param unknown_type $batch_id
  106. */
  107. function batch_finished_page($batch_id) {
  108. $batch = batch_get($batch_id);
  109. if ($batch["token"] != batch_get_token()) {
  110. return "<p>" . t("Sorry, there is a token mismatch or this batch no longer exists.") . "</p>";
  111. }
  112. // Otherwise, we can just return whatever their callback function is. If this is from a form submission, we will send them
  113. // to the form's original destination.
  114. if (isset($batch["file"]) && $batch["file"] != "") {
  115. require_once($batch["file"]);
  116. }
  117. $finished_callback = $batch["finished_callback"][0];
  118. $finished_args = $batch["finished_callback"][1];
  119. if (!is_array($finished_args)) $finished_args = array();
  120. $params = array();
  121. // If we are NOT going to fp_goto, then add the $batch as the first argument.
  122. if ($finished_callback != "fp_goto") {
  123. // Add our $batch to the beginning of the $args array.
  124. //array_unshift($finished_args, $batch);
  125. $params = array (0 => &$batch);
  126. }
  127. foreach ($finished_args as $val) {
  128. $params[] = $val;
  129. }
  130. if (isset($batch["success_message"])) {
  131. fp_add_message($batch["success_message"]);
  132. }
  133. // Okay, let's call our function:
  134. return call_user_func_array($finished_callback, $params);
  135. }
  136. /**
  137. * This is the page the user sees while a batch is being processed.
  138. *
  139. * It will contain all the AJAX and such necessary to begin the batch process.
  140. *
  141. * @param unknown_type $batch_id
  142. */
  143. function batch_processing_page($batch_id) {
  144. $rtn = "";
  145. $batch = batch_get($batch_id);
  146. if ($batch["token"] != batch_get_token()) {
  147. return "<p>" . t("Sorry, there is a token mismatch or this batch no longer exists.") . "</p>";
  148. }
  149. fp_set_title($batch["title"]);
  150. fp_add_css(fp_get_module_path("batch") . "/css/batch.css");
  151. // Set up our setting...
  152. fp_add_js(array("batch_id" => $batch_id), "setting");
  153. // Add our general js file...
  154. fp_add_js(fp_get_module_path("batch") . "/js/batch.js");
  155. $rtn .= "<div class='batch-progress-bar-wrapper'>
  156. <div class='batch-progress-bar' id='batch-progress-bar'></div>
  157. </div>
  158. <div id='batch-progress-message'>" . t("Initializing... please wait...") . "</div>
  159. ";
  160. return $rtn;
  161. }
  162. /**
  163. * Implementation of hook_cron
  164. *
  165. * Delete old batches from the batch_queue table. These would be normal "old" batches we no longer need,
  166. * or ones that never finished for some reason and are just taking up space.
  167. *
  168. */
  169. function batch_cron() {
  170. $older_than = strtotime("6 HOURS AGO");
  171. db_query("DELETE FROM batch_queue WHERE created < ? ", $older_than);
  172. }
  173. /**
  174. * Create a new batch process.
  175. *
  176. *
  177. *
  178. * @param $batch
  179. * This is an array containing everything we need to know about the batch we are creating.
  180. * The non-required fields will be substituted with default values if left out.
  181. *
  182. * - operation :[REQUIRED] an array containing the callback function to execute plus any arguments. Ex: "operation" => array('my_function_to_call', array($arg1, $arg2))
  183. * - finished_callback : an array containing the callback function and args to call once we are finished with the batch.
  184. * Ex: "finished_callback" => array('my_finished_function', array($arg1, $arg2))
  185. * Generally, this field is optional. Since batches are usually set up in a form's submit handler, the default behavior is to go to whatever the form's destination
  186. * was. You can override that here, however.
  187. * - title : A title to display to the user while the batch is running. Ex: 'title' => t('Processing Batch...')
  188. * - progress_message : A message to be displayed while the batch runs. It may use replacement patterns: @current, @total, @percent.
  189. * Ex: 'progress_message' => 'Processed @current out of @total.' // Do NOT run through t()! It will be done at run time.
  190. * - file : An optional file system path to a file where the batch's operation and/or finished functions reside. Ex: "file" => menu_get_module_path("my_example") . "/runme.inc"
  191. * - success_message : An optional message which will display to the user when the batch is complete. Ex: "The process is now complete."
  192. * - display_percent : boolean, default FALSE. If set to TRUE, display percentage value in progress bar.
  193. *
  194. *
  195. * @return $batch_id - the id from batch_queue table for this batch.
  196. */
  197. function batch_set($batch) {
  198. global $user;
  199. if (!isset($batch["title"])) {
  200. $batch["title"] = t("Processing Batch...");
  201. }
  202. if (!isset($batch["progress_message"])) {
  203. $batch["progress_message"] = t("Processed @current out of @total.");
  204. }
  205. if (!isset($batch["display_percent"])) {
  206. $batch["display_percent"] = FALSE;
  207. }
  208. // Add to the database
  209. $ser_batch = serialize($batch);
  210. $token = batch_get_token();
  211. db_query("INSERT INTO batch_queue (token, created, batch_data)
  212. VALUES ('?', '?', '?') ", $token, time(), $ser_batch);
  213. $batch_id = db_insert_id();
  214. // Set a session variable so we are aware of this batch process's existence, so we can call it from system's handle_form_submit if necessary.
  215. $_SESSION["fp_batch_id"] = $batch_id;
  216. // Return back the $bid for the next function to work with.
  217. return $batch_id;
  218. }
  219. /**
  220. * Return the batch_data array for this batch_id, or FALSE if it doesn't exist or can't be unserialized.
  221. *
  222. * @param unknown_type $batch_id
  223. */
  224. function batch_get($batch_id) {
  225. $res = db_query("SELECT * FROM batch_queue
  226. WHERE batch_id = ? ", $batch_id);
  227. $cur = db_fetch_array($res);
  228. if ($batch = unserialize($cur["batch_data"])) {
  229. $batch["batch_id"] = $batch_id;
  230. $batch["token"] = $cur["token"];
  231. $batch["created"] = $cur["created"];
  232. return $batch;
  233. }
  234. return FALSE;
  235. }
  236. /**
  237. * Return a token for this user.
  238. *
  239. */
  240. function batch_get_token() {
  241. global $user;
  242. $uid = 0;
  243. if (isset($user->user_id)) {
  244. $uid = $user->user_id;
  245. }
  246. // Return back md5 of user_id + session_id.
  247. return md5($uid . session_id());
  248. }
  249. /**
  250. * Implementation of hook_perm
  251. *
  252. */
  253. function batch_perm() {
  254. $perms = array (
  255. "batch_run_test" => array(
  256. "title" => t("Run test batch function"),
  257. "description" => t("This is only useful for developers. It allows a user to execute a test batch process by visiting example.com/batch-test-form in their browser."),
  258. ),
  259. );
  260. return $perms;
  261. }
  262. /**
  263. * A batch process is being initiated from a form submission.
  264. *
  265. * @param unknown_type $batch_id
  266. */
  267. function batch_start_batch_from_form_submit($batch_id, $redirect_path = "", $redirect_query = "") {
  268. // We need to confirm that this user is allowed to access THIS batch.
  269. $current_user_token = batch_get_token();
  270. $batch = batch_get($batch_id);
  271. if ($batch["token"] == $current_user_token) {
  272. // Yes, we can proceed!
  273. // If there isn't a finished_callback set, we will set it using fp_goto() to return to the original form's redirect path and query.
  274. if (!isset($batch["finished_callback"])) {
  275. $batch["finished_callback"] = array("fp_goto", array($redirect_path, $redirect_query));
  276. // Update the database...
  277. db_query("UPDATE batch_queue
  278. SET batch_data = '?'
  279. WHERE batch_id = '?'", serialize($batch), $batch_id);
  280. }
  281. // Let's fp_goto to our batch-processing page...
  282. fp_goto("batch-processing/$batch_id");
  283. }
  284. else {
  285. // No... some problem.
  286. fp_add_message(t("Sorry, this batch process could not be initialized. Bad token, or batch does not exist?"), "error");
  287. return;
  288. }
  289. }

Functions

Namesort descending Description
batch_ajax_callback This function is called by ajax, and will trigger each run of the batch operation function, then return appropriate results back to our javascript.
batch_cron Implementation of hook_cron
batch_finished_page We redirect to this page when we have finished a batch.
batch_get Return the batch_data array for this batch_id, or FALSE if it doesn't exist or can't be unserialized.
batch_get_token Return a token for this user.
batch_menu Implementation of hook_menu
batch_perm Implementation of hook_perm
batch_processing_page This is the page the user sees while a batch is being processed.
batch_set Create a new batch process.
batch_start_batch_from_form_submit A batch process is being initiated from a form submission.