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 = array();
  119. if (isset($batch['finished_callback'][1])) {
  120. $finished_args = $batch["finished_callback"][1];
  121. }
  122. $params = array();
  123. // If we are NOT going to fp_goto, then add the $batch as the first argument.
  124. if ($finished_callback != "fp_goto") {
  125. // Add our $batch to the beginning of the $args array.
  126. //array_unshift($finished_args, $batch);
  127. $params = array (0 => &$batch);
  128. }
  129. foreach ($finished_args as $val) {
  130. $params[] = $val;
  131. }
  132. if (isset($batch["success_message"])) {
  133. fp_add_message($batch["success_message"]);
  134. }
  135. // Okay, let's call our function:
  136. return call_user_func_array($finished_callback, $params);
  137. }
  138. /**
  139. * This is the page the user sees while a batch is being processed.
  140. *
  141. * It will contain all the AJAX and such necessary to begin the batch process.
  142. *
  143. * @param unknown_type $batch_id
  144. */
  145. function batch_processing_page($batch_id) {
  146. $rtn = "";
  147. $batch = batch_get($batch_id);
  148. if ($batch["token"] != batch_get_token()) {
  149. return "<p>" . t("Sorry, there is a token mismatch or this batch no longer exists.") . "</p>";
  150. }
  151. fp_set_title($batch["title"]);
  152. fp_add_css(fp_get_module_path("batch") . "/css/batch.css");
  153. // Set up our setting...
  154. fp_add_js(array("batch_id" => $batch_id), "setting");
  155. // Add our general js file...
  156. fp_add_js(fp_get_module_path("batch") . "/js/batch.js");
  157. $rtn .= "<div class='batch-progress-bar-wrapper'>
  158. <div class='batch-progress-bar' id='batch-progress-bar'></div>
  159. </div>
  160. <div id='batch-progress-message'>" . t("Initializing... please wait...") . "</div>
  161. ";
  162. return $rtn;
  163. }
  164. /**
  165. * Implementation of hook_cron
  166. *
  167. * Delete old batches from the batch_queue table. These would be normal "old" batches we no longer need,
  168. * or ones that never finished for some reason and are just taking up space.
  169. *
  170. */
  171. function batch_cron() {
  172. $older_than = strtotime("6 HOURS AGO");
  173. db_query("DELETE FROM batch_queue WHERE created < ? ", $older_than);
  174. }
  175. /**
  176. * Create a new batch process.
  177. *
  178. *
  179. *
  180. * @param $batch
  181. * This is an array containing everything we need to know about the batch we are creating.
  182. * The non-required fields will be substituted with default values if left out.
  183. *
  184. * - operation :[REQUIRED] an array containing the callback function to execute plus any arguments. Ex: "operation" => array('my_function_to_call', array($arg1, $arg2))
  185. * - finished_callback : an array containing the callback function and args to call once we are finished with the batch.
  186. * Ex: "finished_callback" => array('my_finished_function', array($arg1, $arg2))
  187. * 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
  188. * was. You can override that here, however.
  189. * - title : A title to display to the user while the batch is running. Ex: 'title' => t('Processing Batch...')
  190. * - progress_message : A message to be displayed while the batch runs. It may use replacement patterns: @current, @total, @percent.
  191. * Ex: 'progress_message' => 'Processed @current out of @total.' // Do NOT run through t()! It will be done at run time.
  192. * - 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"
  193. * - success_message : An optional message which will display to the user when the batch is complete. Ex: "The process is now complete."
  194. * - display_percent : boolean, default FALSE. If set to TRUE, display percentage value in progress bar.
  195. *
  196. *
  197. * @return $batch_id - the id from batch_queue table for this batch.
  198. */
  199. function batch_set($batch) {
  200. global $user;
  201. if (!isset($batch["title"])) {
  202. $batch["title"] = t("Processing Batch...");
  203. }
  204. if (!isset($batch["progress_message"])) {
  205. $batch["progress_message"] = t("Processed @current out of @total.");
  206. }
  207. if (!isset($batch["display_percent"])) {
  208. $batch["display_percent"] = FALSE;
  209. }
  210. // Add to the database
  211. $ser_batch = serialize($batch);
  212. $token = batch_get_token();
  213. db_query("INSERT INTO batch_queue (token, created, batch_data)
  214. VALUES ('?', '?', '?') ", $token, time(), $ser_batch);
  215. $batch_id = db_insert_id();
  216. // 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.
  217. $_SESSION["fp_batch_id"] = $batch_id;
  218. // Return back the $bid for the next function to work with.
  219. return $batch_id;
  220. }
  221. /**
  222. * Return the batch_data array for this batch_id, or FALSE if it doesn't exist or can't be unserialized.
  223. *
  224. * @param unknown_type $batch_id
  225. */
  226. function batch_get($batch_id) {
  227. $res = db_query("SELECT * FROM batch_queue
  228. WHERE batch_id = ? ", $batch_id);
  229. $cur = db_fetch_array($res);
  230. if ($batch = unserialize($cur["batch_data"])) {
  231. $batch["batch_id"] = $batch_id;
  232. $batch["token"] = $cur["token"];
  233. $batch["created"] = $cur["created"];
  234. return $batch;
  235. }
  236. return FALSE;
  237. }
  238. /**
  239. * Return a token for this user.
  240. *
  241. */
  242. function batch_get_token() {
  243. global $user;
  244. $uid = 0;
  245. if (isset($user->user_id)) {
  246. $uid = $user->user_id;
  247. }
  248. // Return back md5 of user_id + session_id.
  249. return md5($uid . session_id());
  250. }
  251. /**
  252. * Implementation of hook_perm
  253. *
  254. */
  255. function batch_perm() {
  256. $perms = array (
  257. "batch_run_test" => array(
  258. "title" => t("Run test batch function"),
  259. "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."),
  260. "admin_restricted" => TRUE, // means only appears for admin (user_id == 1)
  261. ),
  262. );
  263. return $perms;
  264. }
  265. /**
  266. * A batch process is being initiated from a form submission.
  267. *
  268. * @param unknown_type $batch_id
  269. */
  270. function batch_start_batch_from_form_submit($batch_id, $redirect_path = "", $redirect_query = "") {
  271. // We need to confirm that this user is allowed to access THIS batch.
  272. $current_user_token = batch_get_token();
  273. $batch = batch_get($batch_id);
  274. if ($batch["token"] == $current_user_token) {
  275. // Yes, we can proceed!
  276. // 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.
  277. if (!isset($batch["finished_callback"])) {
  278. $batch["finished_callback"] = array("fp_goto", array($redirect_path, $redirect_query));
  279. // Update the database...
  280. db_query("UPDATE batch_queue
  281. SET batch_data = '?'
  282. WHERE batch_id = '?'", serialize($batch), $batch_id);
  283. }
  284. // Let's fp_goto to our batch-processing page...
  285. fp_goto("batch-processing/$batch_id");
  286. }
  287. else {
  288. // No... some problem.
  289. fp_add_message(t("Sorry, this batch process could not be initialized. Bad token, or batch does not exist?"), "error");
  290. return;
  291. }
  292. }

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.