batch_test.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <?php
  2. /**
  3. * @file
  4. * Helper module for the Batch API tests.
  5. */
  6. /**
  7. * Implement hook_menu().
  8. */
  9. function batch_test_menu() {
  10. $items = array();
  11. $items['batch-test'] = array(
  12. 'title' => 'Batch test',
  13. 'page callback' => 'drupal_get_form',
  14. 'page arguments' => array('batch_test_simple_form'),
  15. 'access callback' => TRUE,
  16. );
  17. // Simple form: one submit handler, setting a batch.
  18. $items['batch-test/simple'] = array(
  19. 'title' => 'Simple',
  20. 'type' => MENU_DEFAULT_LOCAL_TASK,
  21. 'weight' => 0,
  22. );
  23. // Multistep form: two steps, each setting a batch.
  24. $items['batch-test/multistep'] = array(
  25. 'title' => 'Multistep',
  26. 'page callback' => 'drupal_get_form',
  27. 'page arguments' => array('batch_test_multistep_form'),
  28. 'access callback' => TRUE,
  29. 'type' => MENU_LOCAL_TASK,
  30. 'weight' => 1,
  31. );
  32. // Chained form: four submit handlers, several of which set a batch.
  33. $items['batch-test/chained'] = array(
  34. 'title' => 'Chained',
  35. 'page callback' => 'drupal_get_form',
  36. 'page arguments' => array('batch_test_chained_form'),
  37. 'access callback' => TRUE,
  38. 'type' => MENU_LOCAL_TASK,
  39. 'weight' => 2,
  40. );
  41. // Programmatic form: the page submits the 'Chained' form through
  42. // drupal_form_submit().
  43. $items['batch-test/programmatic'] = array(
  44. 'title' => 'Programmatic',
  45. 'page callback' => 'batch_test_programmatic',
  46. 'access callback' => TRUE,
  47. 'type' => MENU_LOCAL_TASK,
  48. 'weight' => 3,
  49. );
  50. // No form: fire a batch simply by accessing a page.
  51. $items['batch-test/no-form'] = array(
  52. 'title' => 'Simple page',
  53. 'page callback' => 'batch_test_no_form',
  54. 'access callback' => TRUE,
  55. 'type' => MENU_LOCAL_TASK,
  56. 'weight' => 4,
  57. );
  58. // No form: fire a batch; return > 100% complete
  59. $items['batch-test/large-percentage'] = array(
  60. 'title' => 'Simple page with batch over 100% complete',
  61. 'page callback' => 'batch_test_large_percentage',
  62. 'access callback' => TRUE,
  63. 'type' => MENU_LOCAL_TASK,
  64. 'weight' => 5,
  65. );
  66. // Tests programmatic form submission within a batch operation.
  67. $items['batch-test/nested-programmatic'] = array(
  68. 'title' => 'Nested programmatic',
  69. 'page callback' => 'batch_test_nested_drupal_form_submit',
  70. 'access callback' => TRUE,
  71. 'type' => MENU_LOCAL_TASK,
  72. 'weight' => 6,
  73. );
  74. // Landing page to test redirects.
  75. $items['batch-test/redirect'] = array(
  76. 'title' => 'Redirect',
  77. 'page callback' => 'batch_test_redirect_page',
  78. 'access callback' => TRUE,
  79. 'type' => MENU_LOCAL_TASK,
  80. 'weight' => 7,
  81. );
  82. // This item lives under 'admin' so that the page uses the admin theme.
  83. $items['admin/batch-test/test-theme'] = array(
  84. 'page callback' => 'batch_test_theme_batch',
  85. 'access callback' => TRUE,
  86. 'type' => MENU_CALLBACK,
  87. );
  88. return $items;
  89. }
  90. /**
  91. * Simple form.
  92. */
  93. function batch_test_simple_form() {
  94. $form['batch'] = array(
  95. '#type' => 'select',
  96. '#title' => 'Choose batch',
  97. '#options' => array(
  98. 'batch_0' => 'batch 0',
  99. 'batch_1' => 'batch 1',
  100. 'batch_2' => 'batch 2',
  101. 'batch_3' => 'batch 3',
  102. 'batch_4' => 'batch 4',
  103. ),
  104. );
  105. $form['submit'] = array(
  106. '#type' => 'submit',
  107. '#value' => 'Submit',
  108. );
  109. return $form;
  110. }
  111. /**
  112. * Submit handler for the simple form.
  113. */
  114. function batch_test_simple_form_submit($form, &$form_state) {
  115. batch_test_stack(NULL, TRUE);
  116. $function = '_batch_test_' . $form_state['values']['batch'];
  117. batch_set($function());
  118. $form_state['redirect'] = 'batch-test/redirect';
  119. }
  120. /**
  121. * Multistep form.
  122. */
  123. function batch_test_multistep_form($form, &$form_state) {
  124. if (empty($form_state['storage']['step'])) {
  125. $form_state['storage']['step'] = 1;
  126. }
  127. $form['step_display'] = array(
  128. '#markup' => 'step ' . $form_state['storage']['step'] . '<br/>',
  129. );
  130. $form['submit'] = array(
  131. '#type' => 'submit',
  132. '#value' => 'Submit',
  133. );
  134. return $form;
  135. }
  136. /**
  137. * Submit handler for the multistep form.
  138. */
  139. function batch_test_multistep_form_submit($form, &$form_state) {
  140. batch_test_stack(NULL, TRUE);
  141. switch ($form_state['storage']['step']) {
  142. case 1:
  143. batch_set(_batch_test_batch_1());
  144. break;
  145. case 2:
  146. batch_set(_batch_test_batch_2());
  147. break;
  148. }
  149. if ($form_state['storage']['step'] < 2) {
  150. $form_state['storage']['step']++;
  151. $form_state['rebuild'] = TRUE;
  152. }
  153. // This will only be effective on the last step.
  154. $form_state['redirect'] = 'batch-test/redirect';
  155. }
  156. /**
  157. * Form with chained submit callbacks.
  158. */
  159. function batch_test_chained_form() {
  160. // This value is used to test that $form_state persists through batched
  161. // submit handlers.
  162. $form['value'] = array(
  163. '#type' => 'textfield',
  164. '#title' => 'Value',
  165. '#default_value' => 1,
  166. );
  167. $form['submit'] = array(
  168. '#type' => 'submit',
  169. '#value' => 'Submit',
  170. );
  171. $form['#submit'] = array(
  172. 'batch_test_chained_form_submit_1',
  173. 'batch_test_chained_form_submit_2',
  174. 'batch_test_chained_form_submit_3',
  175. 'batch_test_chained_form_submit_4',
  176. );
  177. return $form;
  178. }
  179. /**
  180. * Submit handler #1 for the chained form.
  181. */
  182. function batch_test_chained_form_submit_1($form, &$form_state) {
  183. batch_test_stack(NULL, TRUE);
  184. batch_test_stack('submit handler 1');
  185. batch_test_stack('value = ' . $form_state['values']['value']);
  186. $form_state['values']['value']++;
  187. batch_set(_batch_test_batch_1());
  188. // This redirect should not be taken into account.
  189. $form_state['redirect'] = 'should/be/discarded';
  190. }
  191. /**
  192. * Submit handler #2 for the chained form.
  193. */
  194. function batch_test_chained_form_submit_2($form, &$form_state) {
  195. batch_test_stack('submit handler 2');
  196. batch_test_stack('value = ' . $form_state['values']['value']);
  197. $form_state['values']['value']++;
  198. batch_set(_batch_test_batch_2());
  199. // This redirect should not be taken into account.
  200. $form_state['redirect'] = 'should/be/discarded';
  201. }
  202. /**
  203. * Submit handler #3 for the chained form.
  204. */
  205. function batch_test_chained_form_submit_3($form, &$form_state) {
  206. batch_test_stack('submit handler 3');
  207. batch_test_stack('value = ' . $form_state['values']['value']);
  208. $form_state['values']['value']++;
  209. // This redirect should not be taken into account.
  210. $form_state['redirect'] = 'should/be/discarded';
  211. }
  212. /**
  213. * Submit handler #4 for the chained form.
  214. */
  215. function batch_test_chained_form_submit_4($form, &$form_state) {
  216. batch_test_stack('submit handler 4');
  217. batch_test_stack('value = ' . $form_state['values']['value']);
  218. $form_state['values']['value']++;
  219. batch_set(_batch_test_batch_3());
  220. // This is the redirect that should prevail.
  221. $form_state['redirect'] = 'batch-test/redirect';
  222. }
  223. /**
  224. * Menu callback: programmatically submits the 'Chained' form.
  225. */
  226. function batch_test_programmatic($value = 1) {
  227. $form_state = array(
  228. 'values' => array('value' => $value)
  229. );
  230. drupal_form_submit('batch_test_chained_form', $form_state);
  231. return 'Got out of a programmatic batched form.';
  232. }
  233. /**
  234. * Menu callback: programmatically submits a form within a batch.
  235. */
  236. function batch_test_nested_drupal_form_submit($value = 1) {
  237. // Set the batch and process it.
  238. $batch['operations'] = array(
  239. array('_batch_test_nested_drupal_form_submit_callback', array($value)),
  240. );
  241. batch_set($batch);
  242. batch_process('batch-test/redirect');
  243. }
  244. /**
  245. * Batch operation: submits form_test_mock_form using drupal_form_submit().
  246. */
  247. function _batch_test_nested_drupal_form_submit_callback($value) {
  248. $state['values']['test_value'] = $value;
  249. drupal_form_submit('batch_test_mock_form', $state);
  250. }
  251. /**
  252. * A simple form with a textfield and submit button.
  253. */
  254. function batch_test_mock_form($form, $form_state) {
  255. $form['test_value'] = array(
  256. '#type' => 'textfield',
  257. );
  258. $form['submit'] = array(
  259. '#type' => 'submit',
  260. '#value' => t('Submit'),
  261. );
  262. return $form;
  263. }
  264. /**
  265. * Submit handler for the batch_test_mock form.
  266. */
  267. function batch_test_mock_form_submit($form, &$form_state) {
  268. batch_test_stack('mock form submitted with value = ' . $form_state['values']['test_value']);
  269. }
  270. /**
  271. * Menu callback: fire a batch process without a form submission.
  272. */
  273. function batch_test_no_form() {
  274. batch_test_stack(NULL, TRUE);
  275. batch_set(_batch_test_batch_1());
  276. batch_process('batch-test/redirect');
  277. }
  278. /**
  279. * Menu callback: fire a batch process without a form submission.
  280. */
  281. function batch_test_large_percentage() {
  282. batch_test_stack(NULL, TRUE);
  283. batch_set(_batch_test_batch_5());
  284. batch_process('batch-test/redirect');
  285. }
  286. /**
  287. * Menu callback: successful redirection.
  288. */
  289. function batch_test_redirect_page() {
  290. return 'Redirection successful.';
  291. }
  292. /**
  293. * Batch 0: no operation.
  294. */
  295. function _batch_test_batch_0() {
  296. $batch = array(
  297. 'operations' => array(),
  298. 'finished' => '_batch_test_finished_0',
  299. 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
  300. );
  301. return $batch;
  302. }
  303. /**
  304. * Batch 1: repeats a simple operation.
  305. *
  306. * Operations: op 1 from 1 to 10.
  307. */
  308. function _batch_test_batch_1() {
  309. // Ensure the batch takes at least two iterations.
  310. $total = 10;
  311. $sleep = (1000000 / $total) * 2;
  312. $operations = array();
  313. for ($i = 1; $i <= $total; $i++) {
  314. $operations[] = array('_batch_test_callback_1', array($i, $sleep));
  315. }
  316. $batch = array(
  317. 'operations' => $operations,
  318. 'finished' => '_batch_test_finished_1',
  319. 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
  320. );
  321. return $batch;
  322. }
  323. /**
  324. * Batch 2: single multistep operation.
  325. *
  326. * Operations: op 2 from 1 to 10.
  327. */
  328. function _batch_test_batch_2() {
  329. // Ensure the batch takes at least two iterations.
  330. $total = 10;
  331. $sleep = (1000000 / $total) * 2;
  332. $operations = array(
  333. array('_batch_test_callback_2', array(1, $total, $sleep)),
  334. );
  335. $batch = array(
  336. 'operations' => $operations,
  337. 'finished' => '_batch_test_finished_2',
  338. 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
  339. );
  340. return $batch;
  341. }
  342. /**
  343. * Batch 3: both single and multistep operations.
  344. *
  345. * Operations:
  346. * - op 1 from 1 to 5,
  347. * - op 2 from 1 to 5,
  348. * - op 1 from 6 to 10,
  349. * - op 2 from 6 to 10.
  350. */
  351. function _batch_test_batch_3() {
  352. // Ensure the batch takes at least two iterations.
  353. $total = 10;
  354. $sleep = (1000000 / $total) * 2;
  355. $operations = array();
  356. for ($i = 1; $i <= round($total / 2); $i++) {
  357. $operations[] = array('_batch_test_callback_1', array($i, $sleep));
  358. }
  359. $operations[] = array('_batch_test_callback_2', array(1, $total / 2, $sleep));
  360. for ($i = round($total / 2) + 1; $i <= $total; $i++) {
  361. $operations[] = array('_batch_test_callback_1', array($i, $sleep));
  362. }
  363. $operations[] = array('_batch_test_callback_2', array(6, $total / 2, $sleep));
  364. $batch = array(
  365. 'operations' => $operations,
  366. 'finished' => '_batch_test_finished_3',
  367. 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
  368. );
  369. return $batch;
  370. }
  371. /**
  372. * Batch 4: batch within a batch.
  373. *
  374. * Operations:
  375. * - op 1 from 1 to 5,
  376. * - set batch 2 (op 2 from 1 to 10, should run at the end)
  377. * - op 1 from 6 to 10,
  378. */
  379. function _batch_test_batch_4() {
  380. // Ensure the batch takes at least two iterations.
  381. $total = 10;
  382. $sleep = (1000000 / $total) * 2;
  383. $operations = array();
  384. for ($i = 1; $i <= round($total / 2); $i++) {
  385. $operations[] = array('_batch_test_callback_1', array($i, $sleep));
  386. }
  387. $operations[] = array('_batch_test_nested_batch_callback', array());
  388. for ($i = round($total / 2) + 1; $i <= $total; $i++) {
  389. $operations[] = array('_batch_test_callback_1', array($i, $sleep));
  390. }
  391. $batch = array(
  392. 'operations' => $operations,
  393. 'finished' => '_batch_test_finished_4',
  394. 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
  395. );
  396. return $batch;
  397. }
  398. /**
  399. * Batch 5: repeats a simple operation.
  400. *
  401. * Operations: op 1 from 1 to 10.
  402. */
  403. function _batch_test_batch_5() {
  404. // Ensure the batch takes at least two iterations.
  405. $total = 10;
  406. $sleep = (1000000 / $total) * 2;
  407. $operations = array();
  408. for ($i = 1; $i <= $total; $i++) {
  409. $operations[] = array('_batch_test_callback_5', array($i, $sleep));
  410. }
  411. $batch = array(
  412. 'operations' => $operations,
  413. 'finished' => '_batch_test_finished_5',
  414. 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
  415. );
  416. return $batch;
  417. }
  418. /**
  419. * Menu callback: run a batch for testing theme used on the progress page.
  420. */
  421. function batch_test_theme_batch() {
  422. batch_test_stack(NULL, TRUE);
  423. $batch = array(
  424. 'operations' => array(
  425. array('_batch_test_theme_callback', array()),
  426. ),
  427. );
  428. batch_set($batch);
  429. batch_process('batch-test/redirect');
  430. }
  431. /**
  432. * Batch callback function for testing the theme used on the progress page.
  433. */
  434. function _batch_test_theme_callback() {
  435. // Because drupalGet() steps through the full progressive batch before
  436. // returning control to the test function, we cannot test that the correct
  437. // theme is being used on the batch processing page by viewing that page
  438. // directly. Instead, we save the theme being used in a variable here, so
  439. // that it can be loaded and inspected in the thread running the test.
  440. global $theme;
  441. batch_test_stack($theme);
  442. }
  443. /**
  444. * Helper function: store or retrieve traced execution data.
  445. */
  446. function batch_test_stack($data = NULL, $reset = FALSE) {
  447. if ($reset) {
  448. variable_del('batch_test_stack');
  449. }
  450. if (!isset($data)) {
  451. return variable_get('batch_test_stack', array());
  452. }
  453. $stack = variable_get('batch_test_stack', array());
  454. $stack[] = $data;
  455. variable_set('batch_test_stack', $stack);
  456. }