PageRenderTime 58ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/sites/all/modules/ctools/plugins/export_ui/ctools_export_ui.class.php

https://gitlab.com/endomorphosis/tactilevision
PHP | 1455 lines | 851 code | 186 blank | 418 comment | 139 complexity | 5564eed84c9c665e722425d7d00419fd MD5 | raw file
  1. <?php
  2. // $Id: ctools_export_ui.class.php,v 1.1.2.20 2010/10/15 21:05:55 merlinofchaos Exp $
  3. /**
  4. * Base class for export UI.
  5. */
  6. class ctools_export_ui {
  7. var $plugin;
  8. var $name;
  9. var $options = array();
  10. /**
  11. * Fake constructor -- this is easier to deal with than the real
  12. * constructor because we are retaining PHP4 compatibility, which
  13. * would require all child classes to implement their own constructor.
  14. */
  15. function init($plugin) {
  16. ctools_include('export');
  17. $this->plugin = $plugin;
  18. }
  19. /**
  20. * Get a page title for the current page from our plugin strings.
  21. */
  22. function get_page_title($op, $item = NULL) {
  23. if (empty($this->plugin['strings']['title'][$op])) {
  24. return;
  25. }
  26. // Replace %title that might be there with the exportable title.
  27. $title = $this->plugin['strings']['title'][$op];
  28. if (!empty($item)) {
  29. $export_key = $this->plugin['export']['key'];
  30. $title = (str_replace('%title', check_plain($item->{$export_key}), $title));
  31. }
  32. return $title;
  33. }
  34. /**
  35. * Add text on the top of the page.
  36. */
  37. function help_area($form_state) {
  38. // If needed add advanced help strings.
  39. $output = '';
  40. if (!empty($this->plugin['use advanced help'])) {
  41. $config = $this->plugin['advanced help'];
  42. if ($config['enabled']) {
  43. $output = theme('advanced_help_topic', $config['module'], $config['topic']);
  44. $output .= '&nbsp;' . $this->plugin['strings']['advanced help']['enabled'];
  45. }
  46. else {
  47. $output = $this->plugin['strings']['advanced help']['disabled'];
  48. }
  49. }
  50. return $output;
  51. }
  52. // ------------------------------------------------------------------------
  53. // Menu item manipulation
  54. /**
  55. * hook_menu() entry point.
  56. *
  57. * Child implementations that need to add or modify menu items should
  58. * probably call parent::hook_menu($items) and then modify as needed.
  59. */
  60. function hook_menu(&$items) {
  61. // During upgrades, the schema can be empty as this is called prior to
  62. // actual update functions being run. Ensure that we can cope with this
  63. // situation.
  64. if (empty($this->plugin['schema'])) {
  65. return;
  66. }
  67. $prefix = ctools_export_ui_plugin_base_path($this->plugin);
  68. if (isset($this->plugin['menu']['items']) && is_array($this->plugin['menu']['items'])) {
  69. $my_items = array();
  70. foreach ($this->plugin['menu']['items'] as $item) {
  71. // Add menu item defaults.
  72. $item += array(
  73. 'file' => 'export-ui.inc',
  74. 'file path' => drupal_get_path('module', 'ctools') . '/includes',
  75. );
  76. $path = !empty($item['path']) ? $prefix . '/' . $item['path'] : $prefix;
  77. unset($item['path']);
  78. $my_items[$path] = $item;
  79. }
  80. $items += $my_items;
  81. }
  82. }
  83. /**
  84. * Menu callback to determine if an operation is accessible.
  85. *
  86. * This function enforces a basic access check on the configured perm
  87. * string, and then additional checks as needed.
  88. *
  89. * @param $op
  90. * The 'op' of the menu item, which is defined by 'allowed operations'
  91. * and embedded into the arguments in the menu item.
  92. * @param $item
  93. * If an op that works on an item, then the item object, otherwise NULL.
  94. *
  95. * @return
  96. * TRUE if the current user has access, FALSE if not.
  97. */
  98. function access($op, $item) {
  99. if (!user_access($this->plugin['access'])) {
  100. return FALSE;
  101. }
  102. // More fine-grained access control:
  103. if ($op == 'add' && !user_access($this->plugin['create access'])) {
  104. return FALSE;
  105. }
  106. // More fine-grained access control:
  107. if (($op == 'revert' || $op == 'delete') && !user_access($this->plugin['delete access'])) {
  108. return FALSE;
  109. }
  110. // If we need to do a token test, do it here.
  111. if (!empty($this->plugin['allowed operations'][$op]['token']) && (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $op))) {
  112. return FALSE;
  113. }
  114. switch ($op) {
  115. case 'import':
  116. return user_access('use PHP for block visibility');
  117. case 'revert':
  118. return ($item->export_type & EXPORT_IN_DATABASE) && ($item->export_type & EXPORT_IN_CODE);
  119. case 'delete':
  120. return ($item->export_type & EXPORT_IN_DATABASE) && !($item->export_type & EXPORT_IN_CODE);
  121. case 'disable':
  122. return empty($item->disabled);
  123. case 'enable':
  124. return !empty($item->disabled);
  125. default:
  126. return TRUE;
  127. }
  128. }
  129. // ------------------------------------------------------------------------
  130. // These methods are the API for generating the list of exportable items.
  131. /**
  132. * Master entry point for handling a list.
  133. *
  134. * It is unlikely that a child object will need to override this method,
  135. * unless the listing mechanism is going to be highly specialized.
  136. */
  137. function list_page($js, $input) {
  138. $this->items = ctools_export_crud_load_all($this->plugin['schema'], $js);
  139. // Respond to a reset command by clearing session and doing a drupal goto
  140. // back to the base URL.
  141. if (isset($input['op']) && $input['op'] == t('Reset')) {
  142. unset($_SESSION['ctools_export_ui'][$this->plugin['name']]);
  143. if (!$js) {
  144. return drupal_goto($_GET['q']);
  145. }
  146. // clear everything but form id, form build id and form token:
  147. $keys = array_keys($input);
  148. foreach ($keys as $id) {
  149. if (!in_array($id, array('form_id', 'form_build_id', 'form_token'))) {
  150. unset($input[$id]);
  151. }
  152. }
  153. $replace_form = TRUE;
  154. }
  155. // If there is no input, check to see if we have stored input in the
  156. // session.
  157. if (!isset($input['form_id'])) {
  158. if (isset($_SESSION['ctools_export_ui'][$this->plugin['name']]) && is_array($_SESSION['ctools_export_ui'][$this->plugin['name']])) {
  159. $input = $_SESSION['ctools_export_ui'][$this->plugin['name']];
  160. }
  161. }
  162. else {
  163. $_SESSION['ctools_export_ui'][$this->plugin['name']] = $input;
  164. unset($_SESSION['ctools_export_ui'][$this->plugin['name']]['q']);
  165. }
  166. // This is where the form will put the output.
  167. $this->rows = array();
  168. $this->sorts = array();
  169. $form_state = array(
  170. 'plugin' => $this->plugin,
  171. 'input' => $input,
  172. 'rerender' => TRUE,
  173. 'no_redirect' => TRUE,
  174. 'object' => &$this,
  175. );
  176. $help_area = $this->help_area($form_state);
  177. ctools_include('form');
  178. $form = ctools_build_form('ctools_export_ui_list_form', $form_state);
  179. $output = $this->list_header($form_state) . $this->list_render($form_state) . $this->list_footer($form_state);
  180. if (!$js) {
  181. $this->list_css();
  182. return $help_area . $form . $output;
  183. }
  184. ctools_include('ajax');
  185. $commands = array();
  186. $commands[] = ctools_ajax_command_replace('#ctools-export-ui-list-items', $output);
  187. if (!empty($replace_form)) {
  188. $commands[] = ctools_ajax_command_replace('#ctools-export-ui-list-form', $form);
  189. }
  190. ctools_ajax_render($commands);
  191. }
  192. /**
  193. * Create the filter/sort form at the top of a list of exports.
  194. *
  195. * This handles the very default conditions, and most lists are expected
  196. * to override this and call through to parent::list_form() in order to
  197. * get the base form and then modify it as necessary to add search
  198. * gadgets for custom fields.
  199. */
  200. function list_form(&$form, &$form_state) {
  201. // This forces the form to *always* treat as submitted which is
  202. // necessary to make it work.
  203. $form['#token'] = FALSE;
  204. if (empty($form_state['input'])) {
  205. $form["#post"] = TRUE;
  206. }
  207. // Add the 'q' in if we are not using clean URLs or it can get lost when
  208. // using this kind of form.
  209. if (!variable_get('clean_url', FALSE)) {
  210. $form['q'] = array(
  211. '#type' => 'hidden',
  212. '#value' => $_GET['q'],
  213. );
  214. }
  215. $all = array('all' => t('- All -'));
  216. $form['top row'] = array(
  217. '#prefix' => '<div class="ctools-export-ui-row ctools-export-ui-top-row clear-block">',
  218. '#suffix' => '</div>',
  219. );
  220. $form['bottom row'] = array(
  221. '#prefix' => '<div class="ctools-export-ui-row ctools-export-ui-bottom-row clear-block">',
  222. '#suffix' => '</div>',
  223. );
  224. $form['top row']['storage'] = array(
  225. '#type' => 'select',
  226. '#title' => t('Storage'),
  227. '#options' => $all + array(
  228. t('Normal') => t('Normal'),
  229. t('Default') => t('Default'),
  230. t('Overridden') => t('Overridden'),
  231. ),
  232. '#default_value' => 'all',
  233. );
  234. $form['top row']['disabled'] = array(
  235. '#type' => 'select',
  236. '#title' => t('Enabled'),
  237. '#options' => $all + array(
  238. '0' => t('Enabled'),
  239. '1' => t('Disabled')
  240. ),
  241. '#default_value' => 'all',
  242. );
  243. $form['top row']['search'] = array(
  244. '#type' => 'textfield',
  245. '#title' => t('Search'),
  246. );
  247. $form['bottom row']['order'] = array(
  248. '#type' => 'select',
  249. '#title' => t('Sort by'),
  250. '#options' => $this->list_sort_options(),
  251. '#default_value' => 'disabled',
  252. );
  253. $form['bottom row']['sort'] = array(
  254. '#type' => 'select',
  255. '#title' => t('Order'),
  256. '#options' => array(
  257. 'asc' => t('Up'),
  258. 'desc' => t('Down'),
  259. ),
  260. '#default_value' => 'asc',
  261. );
  262. $form['bottom row']['submit'] = array(
  263. '#type' => 'submit',
  264. '#id' => 'ctools-export-ui-list-items-apply',
  265. '#value' => t('Apply'),
  266. '#attributes' => array('class' => 'ctools-use-ajax ctools-auto-submit-click'),
  267. );
  268. $form['bottom row']['reset'] = array(
  269. '#type' => 'submit',
  270. '#id' => 'ctools-export-ui-list-items-apply',
  271. '#value' => t('Reset'),
  272. '#attributes' => array('class' => 'ctools-use-ajax'),
  273. );
  274. ctools_add_js('ajax-responder');
  275. ctools_add_js('auto-submit');
  276. drupal_add_js('misc/jquery.form.js');
  277. $form['#prefix'] = '<div class="clear-block">';
  278. $form['#suffix'] = '</div>';
  279. $form['#attributes'] = array('class' => 'ctools-auto-submit-full-form');
  280. }
  281. /**
  282. * Validate the filter/sort form.
  283. *
  284. * It is very rare that a filter form needs validation, but if it is
  285. * needed, override this.
  286. */
  287. function list_form_validate(&$form, &$form_state) { }
  288. /**
  289. * Submit the filter/sort form.
  290. *
  291. * This submit handler is actually responsible for building up all of the
  292. * rows that will later be rendered, since it is doing the filtering and
  293. * sorting.
  294. *
  295. * For the most part, you should not need to override this method, as the
  296. * fiddly bits call through to other functions.
  297. */
  298. function list_form_submit(&$form, &$form_state) {
  299. // Filter and re-sort the pages.
  300. $plugin = $this->plugin;
  301. $prefix = ctools_export_ui_plugin_base_path($plugin);
  302. foreach ($this->items as $name => $item) {
  303. // Call through to the filter and see if we're going to render this
  304. // row. If it returns TRUE, then this row is filtered out.
  305. if ($this->list_filter($form_state, $item)) {
  306. continue;
  307. }
  308. // Note: Creating this list seems a little clumsy, but can't think of
  309. // better ways to do this.
  310. $allowed_operations = drupal_map_assoc(array_keys($plugin['allowed operations']));
  311. $not_allowed_operations = array('import');
  312. if ($item->type == t('Normal')) {
  313. $not_allowed_operations[] = 'revert';
  314. }
  315. elseif ($item->type == t('Overridden')) {
  316. $not_allowed_operations[] = 'delete';
  317. }
  318. else {
  319. $not_allowed_operations[] = 'revert';
  320. $not_allowed_operations[] = 'delete';
  321. }
  322. $not_allowed_operations[] = empty($item->disabled) ? 'enable' : 'disable';
  323. foreach ($not_allowed_operations as $op) {
  324. // Remove the operations that are not allowed for the specific
  325. // exportable.
  326. unset($allowed_operations[$op]);
  327. }
  328. $operations = array();
  329. foreach ($allowed_operations as $op) {
  330. $operations[$op] = array(
  331. 'title' => $plugin['allowed operations'][$op]['title'],
  332. 'href' => ctools_export_ui_plugin_menu_path($plugin, $op, $name),
  333. );
  334. if (!empty($plugin['allowed operations'][$op]['ajax'])) {
  335. $operations[$op]['attributes'] = array('class' => 'ctools-use-ajax');
  336. }
  337. if (!empty($plugin['allowed operations'][$op]['token'])) {
  338. $operations[$op]['query'] = array('token' => drupal_get_token($op));
  339. }
  340. }
  341. $this->list_build_row($item, $form_state, $operations);
  342. }
  343. // Now actually sort
  344. if ($form_state['values']['sort'] == 'desc') {
  345. arsort($this->sorts);
  346. }
  347. else {
  348. asort($this->sorts);
  349. }
  350. // Nuke the original.
  351. $rows = $this->rows;
  352. $this->rows = array();
  353. // And restore.
  354. foreach ($this->sorts as $name => $title) {
  355. $this->rows[$name] = $rows[$name];
  356. }
  357. }
  358. /**
  359. * Determine if a row should be filtered out.
  360. *
  361. * This handles the default filters for the export UI list form. If you
  362. * added additional filters in list_form() then this is where you should
  363. * handle them.
  364. *
  365. * @return
  366. * TRUE if the item should be excluded.
  367. */
  368. function list_filter($form_state, $item) {
  369. if ($form_state['values']['storage'] != 'all' && $form_state['values']['storage'] != $item->type) {
  370. return TRUE;
  371. }
  372. if ($form_state['values']['disabled'] != 'all' && $form_state['values']['disabled'] != !empty($item->disabled)) {
  373. return TRUE;
  374. }
  375. if ($form_state['values']['search']) {
  376. $search = strtolower($form_state['values']['search']);
  377. foreach ($this->list_search_fields() as $field) {
  378. if (strpos(strtolower($item->$field), $search) !== FALSE) {
  379. $hit = TRUE;
  380. break;
  381. }
  382. }
  383. if (empty($hit)) {
  384. return TRUE;
  385. }
  386. }
  387. }
  388. /**
  389. * Provide a list of fields to test against for the default "search" widget.
  390. *
  391. * This widget will search against whatever fields are configured here. By
  392. * default it will attempt to search against the name, title and description fields.
  393. */
  394. function list_search_fields() {
  395. $fields = array(
  396. $this->plugin['export']['key'],
  397. );
  398. if (!empty($this->plugin['export']['admin_title'])) {
  399. $fields[] = $this->plugin['export']['admin_title'];
  400. }
  401. if (!empty($this->plugin['export']['admin_description'])) {
  402. $fields[] = $this->plugin['export']['admin_description'];
  403. }
  404. return $fields;
  405. }
  406. /**
  407. * Provide a list of sort options.
  408. *
  409. * Override this if you wish to provide more or change how these work.
  410. * The actual handling of the sorting will happen in build_row().
  411. */
  412. function list_sort_options() {
  413. if (!empty($this->plugin['export']['admin_title'])) {
  414. $options = array(
  415. 'disabled' => t('Enabled, title'),
  416. $this->plugin['export']['admin_title'] => t('Title'),
  417. );
  418. }
  419. else {
  420. $options = array(
  421. 'disabled' => t('Enabled, name'),
  422. );
  423. }
  424. $options += array(
  425. 'name' => t('Name'),
  426. 'storage' => t('Storage'),
  427. );
  428. return $options;
  429. }
  430. /**
  431. * Add listing CSS to the page.
  432. *
  433. * Override this if you need custom CSS for your list.
  434. */
  435. function list_css() {
  436. ctools_add_css('export-ui-list');
  437. }
  438. /**
  439. * Build a row based on the item.
  440. *
  441. * By default all of the rows are placed into a table by the render
  442. * method, so this is building up a row suitable for theme('table').
  443. * This doesn't have to be true if you override both.
  444. */
  445. function list_build_row($item, &$form_state, $operations) {
  446. // Set up sorting
  447. $name = $item->{$this->plugin['export']['key']};
  448. // Note: $item->type should have already been set up by export.inc so
  449. // we can use it safely.
  450. switch ($form_state['values']['order']) {
  451. case 'disabled':
  452. $this->sorts[$name] = empty($item->disabled) . $name;
  453. break;
  454. case 'title':
  455. $this->sorts[$name] = $item->{$this->plugin['export']['admin_title']};
  456. break;
  457. case 'name':
  458. $this->sorts[$name] = $name;
  459. break;
  460. case 'storage':
  461. $this->sorts[$name] = $item->type . $name;
  462. break;
  463. }
  464. $this->rows[$name]['data'] = array();
  465. $this->rows[$name]['class'] = !empty($item->disabled) ? 'ctools-export-ui-disabled' : 'ctools-export-ui-enabled';
  466. // If we have an admin title, make it the first row.
  467. if (!empty($this->plugin['export']['admin_title'])) {
  468. $this->rows[$name]['data'][] = array('data' => check_plain($item->{$this->plugin['export']['admin_title']}), 'class' => 'ctools-export-ui-title');
  469. }
  470. $this->rows[$name]['data'][] = array('data' => check_plain($name), 'class' => 'ctools-export-ui-name');
  471. $this->rows[$name]['data'][] = array('data' => check_plain($item->type), 'class' => 'ctools-export-ui-storage');
  472. $this->rows[$name]['data'][] = array('data' => theme('links', $operations), 'class' => 'ctools-export-ui-operations');
  473. // Add an automatic mouseover of the description if one exists.
  474. if (!empty($this->plugin['export']['admin_description'])) {
  475. $this->rows[$name]['title'] = $item->{$this->plugin['export']['admin_description']};
  476. }
  477. }
  478. /**
  479. * Provide the table header.
  480. *
  481. * If you've added columns via list_build_row() but are still using a
  482. * table, override this method to set up the table header.
  483. */
  484. function list_table_header() {
  485. $header = array();
  486. if (!empty($this->plugin['export']['admin_title'])) {
  487. $header[] = array('data' => t('Title'), 'class' => 'ctools-export-ui-title');
  488. }
  489. $header[] = array('data' => t('Name'), 'class' => 'ctools-export-ui-name');
  490. $header[] = array('data' => t('Storage'), 'class' => 'ctools-export-ui-storage');
  491. $header[] = array('data' => t('Operations'), 'class' => 'ctools-export-ui-operations');
  492. return $header;
  493. }
  494. /**
  495. * Render all of the rows together.
  496. *
  497. * By default we place all of the rows in a table, and this should be the
  498. * way most lists will go.
  499. *
  500. * Whatever you do if this method is overridden, the ID is important for AJAX
  501. * so be sure it exists.
  502. */
  503. function list_render(&$form_state) {
  504. return theme('table', $this->list_table_header(), $this->rows, array('id' => 'ctools-export-ui-list-items'));
  505. }
  506. /**
  507. * Render a header to go before the list.
  508. *
  509. * This will appear after the filter/sort widgets.
  510. */
  511. function list_header($form_state) { }
  512. /**
  513. * Render a footer to go after thie list.
  514. *
  515. * This is a good place to add additional links.
  516. */
  517. function list_footer($form_state) { }
  518. // ------------------------------------------------------------------------
  519. // These methods are the API for adding/editing exportable items
  520. function add_page($js, $input, $step = NULL) {
  521. drupal_set_title($this->get_page_title('add'));
  522. // If a step not set, they are trying to create a new item. If a step
  523. // is set, they're in the process of creating an item.
  524. if (!empty($this->plugin['use wizard']) && !empty($step)) {
  525. $item = $this->edit_cache_get(NULL, 'add');
  526. }
  527. if (empty($item)) {
  528. $item = ctools_export_crud_new($this->plugin['schema']);
  529. }
  530. $form_state = array(
  531. 'plugin' => $this->plugin,
  532. 'object' => &$this,
  533. 'ajax' => $js,
  534. 'item' => $item,
  535. 'op' => 'add',
  536. 'form type' => 'add',
  537. 'rerender' => TRUE,
  538. 'no_redirect' => TRUE,
  539. 'step' => $step,
  540. // Store these in case additional args are needed.
  541. 'function args' => func_get_args(),
  542. );
  543. $output = $this->edit_execute_form($form_state);
  544. if (!empty($form_state['executed'])) {
  545. $export_key = $this->plugin['export']['key'];
  546. drupal_goto(str_replace('%ctools_export_ui', $form_state['item']->{$export_key}, $this->plugin['redirect']['add']));
  547. }
  548. return $output;
  549. }
  550. /**
  551. * Main entry point to edit an item.
  552. */
  553. function edit_page($js, $input, $item, $step = NULL) {
  554. drupal_set_title($this->get_page_title('edit', $item));
  555. // Check to see if there is a cached item to get if we're using the wizard.
  556. if (!empty($this->plugin['use wizard'])) {
  557. $cached = $this->edit_cache_get($item, 'edit');
  558. if (!empty($cached)) {
  559. $item = $cached;
  560. }
  561. }
  562. $form_state = array(
  563. 'plugin' => $this->plugin,
  564. 'object' => &$this,
  565. 'ajax' => $js,
  566. 'item' => $item,
  567. 'op' => 'edit',
  568. 'form type' => 'edit',
  569. 'rerender' => TRUE,
  570. 'no_redirect' => TRUE,
  571. 'step' => $step,
  572. // Store these in case additional args are needed.
  573. 'function args' => func_get_args(),
  574. );
  575. $output = $this->edit_execute_form($form_state);
  576. if (!empty($form_state['executed'])) {
  577. $export_key = $this->plugin['export']['key'];
  578. drupal_goto(str_replace('%ctools_export_ui', $form_state['item']->{$export_key}, $this->plugin['redirect']['edit']));
  579. }
  580. return $output;
  581. }
  582. /**
  583. * Main entry point to clone an item.
  584. */
  585. function clone_page($js, $input, $original, $step = NULL) {
  586. drupal_set_title($this->get_page_title('clone', $original));
  587. // If a step not set, they are trying to create a new clone. If a step
  588. // is set, they're in the process of cloning an item.
  589. if (!empty($this->plugin['use wizard']) && !empty($step)) {
  590. $item = $this->edit_cache_get(NULL, 'clone');
  591. }
  592. if (empty($item)) {
  593. // To make a clone of an item, we first export it and then re-import it.
  594. // Export the handler, which is a fantastic way to clean database IDs out of it.
  595. $export = ctools_export_crud_export($this->plugin['schema'], $original);
  596. $item = ctools_export_crud_import($this->plugin['schema'], $export);
  597. $item->{$this->plugin['export']['key']} = 'clone_of_' . $item->{$this->plugin['export']['key']};
  598. }
  599. // Tabs and breadcrumb disappearing, this helps alleviate through cheating.
  600. ctools_include('menu');
  601. $trail = ctools_get_menu_trail(ctools_export_ui_plugin_base_path($this->plugin));
  602. menu_set_active_trail($trail);
  603. $name = $original->{$this->plugin['export']['key']};
  604. $form_state = array(
  605. 'plugin' => $this->plugin,
  606. 'object' => &$this,
  607. 'ajax' => $js,
  608. 'item' => $item,
  609. 'op' => 'add',
  610. 'form type' => 'clone',
  611. 'original name' => $name,
  612. 'rerender' => TRUE,
  613. 'no_redirect' => TRUE,
  614. 'step' => $step,
  615. // Store these in case additional args are needed.
  616. 'function args' => func_get_args(),
  617. );
  618. $output = $this->edit_execute_form($form_state);
  619. if (!empty($form_state['executed'])) {
  620. $export_key = $this->plugin['export']['key'];
  621. drupal_goto(str_replace('%ctools_export_ui', $form_state['item']->{$export_key}, $this->plugin['redirect']['clone']));
  622. }
  623. return $output;
  624. }
  625. /**
  626. * Execute the form.
  627. *
  628. * Add and Edit both funnel into this, but they have a few different
  629. * settings.
  630. */
  631. function edit_execute_form(&$form_state) {
  632. if (!empty($this->plugin['use wizard'])) {
  633. return $this->edit_execute_form_wizard($form_state);
  634. }
  635. else {
  636. return $this->edit_execute_form_standard($form_state);
  637. }
  638. }
  639. /**
  640. * Execute the standard form for editing.
  641. *
  642. * By default, export UI will provide a single form for editing an object.
  643. */
  644. function edit_execute_form_standard(&$form_state) {
  645. ctools_include('form');
  646. $output = ctools_build_form('ctools_export_ui_edit_item_form', $form_state);
  647. if (!empty($form_state['executed'])) {
  648. $this->edit_save_form($form_state);
  649. }
  650. return $output;
  651. }
  652. /**
  653. * Get the form info for the wizard.
  654. *
  655. * This gets the form info out of the plugin, then adds defaults based on
  656. * how we want edit forms to work.
  657. *
  658. * Overriding this can allow child UIs to tweak this info for specialized
  659. * wizards.
  660. *
  661. * @param array $form_state
  662. * The already created form state.
  663. */
  664. function get_wizard_info(&$form_state) {
  665. if (!isset($form_state['step'])) {
  666. $form_state['step'] = NULL;
  667. }
  668. $export_key = $this->plugin['export']['key'];
  669. // When cloning, the name of the item being cloned is referenced in the
  670. // path, not the name of this item.
  671. if ($form_state['form type'] == 'clone') {
  672. $name = $form_state['original name'];
  673. }
  674. else {
  675. $name = $form_state['item']->{$export_key};
  676. }
  677. $form_info = !empty($this->plugin['form info']) ? $this->plugin['form info'] : array();
  678. $form_info += array(
  679. 'id' => 'ctools_export_ui_edit',
  680. 'path' => ctools_export_ui_plugin_menu_path($this->plugin, $form_state['form type'], $name) . '/%step',
  681. 'show trail' => TRUE,
  682. 'free trail' => TRUE,
  683. 'show back' => $form_state['form type'] == 'add',
  684. 'show return' => FALSE,
  685. 'show cancel' => TRUE,
  686. 'finish callback' => 'ctools_export_ui_wizard_finish',
  687. 'next callback' => 'ctools_export_ui_wizard_next',
  688. 'back callback' => 'ctools_export_ui_wizard_back',
  689. 'cancel callback' => 'ctools_export_ui_wizard_cancel',
  690. 'order' => array(),
  691. 'import order' => array(
  692. 'import' => t('Import code'),
  693. 'settings' => t('Settings'),
  694. ),
  695. );
  696. // Set the order of forms based on the op if we have a specific one.
  697. if (isset($form_info[$form_state['form type'] . ' order'])) {
  698. $form_info['order'] = $form_info[$form_state['form type'] . ' order'];
  699. }
  700. // We have generic fallback forms we can use if they are not specified,
  701. // and they automatically delegate back to the UI object. Use these if
  702. // not specified.
  703. foreach ($form_info['order'] as $key => $title) {
  704. if (empty($form_info['forms'][$key])) {
  705. $form_info['forms'][$key] = array(
  706. 'form id' => 'ctools_export_ui_edit_item_wizard_form',
  707. );
  708. }
  709. }
  710. // 'free trail' means the wizard can freely go back and form from item
  711. // via the trail and not with next/back buttons.
  712. if ($form_state['form type'] == 'add' || ($form_state['form type'] == 'import' && empty($form_state['item']->{$export_key}))) {
  713. $form_info['free trail'] = FALSE;
  714. }
  715. return $form_info;
  716. }
  717. /**
  718. * Execute the wizard for editing.
  719. *
  720. * For complex objects, sometimes a wizard is needed. This is normally
  721. * activated by setting 'use wizard' => TRUE in the plugin definition
  722. * and then creating a 'form info' array to feed the wizard the data
  723. * it needs.
  724. *
  725. * When creating this wizard, the plugin is responsible for defining all forms
  726. * that will be utilized with the wizard.
  727. *
  728. * Using 'add order' or 'edit order' can be used to ensure that add/edit order
  729. * is different.
  730. */
  731. function edit_execute_form_wizard(&$form_state) {
  732. $form_info = $this->get_wizard_info($form_state);
  733. // If there aren't any forms set, fail.
  734. if (empty($form_info['order'])) {
  735. return MENU_NOT_FOUND;
  736. }
  737. // Figure out if this is a new instance of the wizard
  738. if (empty($form_state['step'])) {
  739. $form_state['step'] = reset(array_keys($form_info['order']));
  740. }
  741. if (empty($form_info['order'][$form_state['step']]) && empty($form_info['forms'][$form_state['step']])) {
  742. return MENU_NOT_FOUND;
  743. }
  744. ctools_include('wizard');
  745. $output = ctools_wizard_multistep_form($form_info, $form_state['step'], $form_state);
  746. if (!empty($form_state['complete'])) {
  747. $this->edit_save_form($form_state);
  748. }
  749. else if ($output && !empty($form_state['item']->export_ui_item_is_cached)) {
  750. // @todo this should be in the plugin strings
  751. drupal_set_message(t('You have unsaved changes. These changes will not be made permanent until you click <em>Save</em>.'), 'warning');
  752. }
  753. // Unset the executed flag if any non-wizard button was pressed. Those
  754. // buttons require special handling by whatever client is operating them.
  755. if (!empty($form_state['executed']) && empty($form_state['clicked_button']['#wizard type'])) {
  756. unset($form_state['executed']);
  757. }
  758. return $output;
  759. }
  760. /**
  761. * Wizard 'back' callback when using a wizard to edit an item.
  762. *
  763. * The wizard callback delegates this back to the object.
  764. */
  765. function edit_wizard_back(&$form_state) {
  766. // This only exists so child implementations can use it.
  767. }
  768. /**
  769. * Wizard 'next' callback when using a wizard to edit an item.
  770. *
  771. * The wizard callback delegates this back to the object.
  772. */
  773. function edit_wizard_next(&$form_state) {
  774. $this->edit_cache_set($form_state['item'], $form_state['form type']);
  775. }
  776. /**
  777. * Wizard 'cancel' callback when using a wizard to edit an item.
  778. *
  779. * The wizard callback delegates this back to the object.
  780. */
  781. function edit_wizard_cancel(&$form_state) {
  782. $this->edit_cache_clear($form_state['item'], $form_state['form type']);
  783. }
  784. /**
  785. * Wizard 'cancel' callback when using a wizard to edit an item.
  786. *
  787. * The wizard callback delegates this back to the object.
  788. */
  789. function edit_wizard_finish(&$form_state) {
  790. $form_state['complete'] = TRUE;
  791. // If we are importing, and overwrite was selected, delete the original so
  792. // that this one writes properly.
  793. if ($form_state['form type'] == 'import' && !empty($form_state['item']->export_ui_allow_overwrite)) {
  794. ctools_export_crud_delete($this->plugin['schema'], $form_state['item']);
  795. }
  796. $this->edit_cache_clear($form_state['item'], $form_state['form type']);
  797. }
  798. /**
  799. * Retrieve the item currently being edited from the object cache.
  800. */
  801. function edit_cache_get($item, $op = 'edit') {
  802. ctools_include('object-cache');
  803. if (is_string($item)) {
  804. $name = $item;
  805. }
  806. else {
  807. $name = $this->edit_cache_get_key($item, $op);
  808. }
  809. $cache = ctools_object_cache_get('ctui_' . $this->plugin['name'], $name);
  810. if ($cache) {
  811. $cache->export_ui_item_is_cached = TRUE;
  812. return $cache;
  813. }
  814. }
  815. /**
  816. * Cache the item currently currently being edited.
  817. */
  818. function edit_cache_set($item, $op = 'edit') {
  819. ctools_include('object-cache');
  820. $name = $this->edit_cache_get_key($item, $op);
  821. return $this->edit_cache_set_key($item, $name);
  822. }
  823. function edit_cache_set_key($item, $name) {
  824. return ctools_object_cache_set('ctui_' . $this->plugin['name'], $name, $item);
  825. }
  826. /**
  827. * Clear the object cache for the currently edited item.
  828. */
  829. function edit_cache_clear($item, $op = 'edit') {
  830. ctools_include('object-cache');
  831. $name = $this->edit_cache_get_key($item, $op);
  832. return ctools_object_cache_clear('ctui_' . $this->plugin['name'], $name);
  833. }
  834. /**
  835. * Figure out what the cache key is for this object.
  836. */
  837. function edit_cache_get_key($item, $op) {
  838. $export_key = $this->plugin['export']['key'];
  839. return $op == 'edit' ? $item->{$this->plugin['export']['key']} : "::$op";
  840. }
  841. /**
  842. * Called to save the final product from the edit form.
  843. */
  844. function edit_save_form($form_state) {
  845. $item = &$form_state['item'];
  846. $export_key = $this->plugin['export']['key'];
  847. $result = ctools_export_crud_save($this->plugin['schema'], $item);
  848. if ($result) {
  849. $message = str_replace('%title', check_plain($item->{$export_key}), $this->plugin['strings']['confirmation'][$form_state['op']]['success']);
  850. drupal_set_message($message);
  851. }
  852. else {
  853. $message = str_replace('%title', check_plain($item->{$export_key}), $this->plugin['strings']['confirmation'][$form_state['op']]['fail']);
  854. drupal_set_message($message, 'error');
  855. }
  856. }
  857. /**
  858. * Provide the actual editing form.
  859. */
  860. function edit_form(&$form, &$form_state) {
  861. $export_key = $this->plugin['export']['key'];
  862. $item = $form_state['item'];
  863. $schema = ctools_export_get_schema($this->plugin['schema']);
  864. // TODO: Drupal 7 has a nifty method of auto guessing names from
  865. // titles that is standard. We should integrate that here as a
  866. // nice standard.
  867. // Guess at a couple of our standard fields.
  868. if (!empty($this->plugin['export']['admin_title'])) {
  869. $form['info'][$this->plugin['export']['admin_title']] = array(
  870. '#type' => 'textfield',
  871. '#title' => t('Administrative title'),
  872. '#description' => t('This will appear in the administrative interface to easily identify it.'),
  873. '#default_value' => $item->{$this->plugin['export']['admin_title']},
  874. );
  875. }
  876. $form['info'][$export_key] = array(
  877. '#title' => t($schema['export']['key name']),
  878. '#type' => 'textfield',
  879. '#default_value' => $item->{$export_key},
  880. '#description' => t('The unique ID for this @export.', array('@export' => $this->plugin['title singular'])),
  881. '#required' => TRUE,
  882. '#maxlength' => 255,
  883. );
  884. if ($form_state['op'] === 'edit') {
  885. $form['info'][$export_key]['#disabled'] = TRUE;
  886. $form['info'][$export_key]['#value'] = $item->{$export_key};
  887. }
  888. else {
  889. $form['info'][$export_key]['#element_validate'] = array('ctools_export_ui_edit_name_validate');
  890. }
  891. if (!empty($this->plugin['export']['admin_description'])) {
  892. $form['info'][$this->plugin['export']['admin_description']] = array(
  893. '#type' => 'textarea',
  894. '#title' => t('Administrative description'),
  895. '#default_value' => $item->{$this->plugin['export']['admin_description']},
  896. );
  897. }
  898. // Add plugin's form definitions.
  899. if (!empty($this->plugin['form']['settings'])) {
  900. // Pass $form by reference.
  901. $this->plugin['form']['settings']($form, $form_state);
  902. }
  903. // Add the buttons if the wizard is not in use.
  904. if (empty($form_state['form_info'])) {
  905. // Make sure that whatever happens, the buttons go to the bottom.
  906. $form['buttons']['#weight'] = 100;
  907. // Add buttons.
  908. $form['buttons']['submit'] = array(
  909. '#type' => 'submit',
  910. '#value' => t('Save'),
  911. );
  912. $form['buttons']['delete'] = array(
  913. '#type' => 'submit',
  914. '#value' => $item->export_type & EXPORT_IN_CODE ? t('Revert') : t('Delete'),
  915. '#access' => $form_state['op'] === 'edit' && $item->export_type & EXPORT_IN_DATABASE,
  916. '#submit' => array('ctools_export_ui_edit_item_form_delete'),
  917. );
  918. }
  919. }
  920. /**
  921. * Validate callback for the edit form.
  922. */
  923. function edit_form_validate(&$form, &$form_state) {
  924. if (!empty($this->plugin['form']['validate'])) {
  925. // Pass $form by reference.
  926. $this->plugin['form']['validate']($form, $form_state);
  927. }
  928. }
  929. /**
  930. * Perform a final validation check before allowing the form to be
  931. * finished.
  932. */
  933. function edit_finish_validate(&$form, &$form_state) {
  934. if ($form_state['op'] != 'edit') {
  935. // Validate the name. Fake an element for form_error().
  936. $export_key = $this->plugin['export']['key'];
  937. $element = array(
  938. '#value' => $form_state['item']->{$export_key},
  939. '#parents' => array('name'),
  940. );
  941. $form_state['plugin'] = $this->plugin;
  942. ctools_export_ui_edit_name_validate($element, $form_state);
  943. }
  944. }
  945. /**
  946. * Handle the submission of the edit form.
  947. *
  948. * At this point, submission is successful. Our only responsibility is
  949. * to copy anything out of values onto the item that we are able to edit.
  950. *
  951. * If the keys all match up to the schema, this method will not need to be
  952. * overridden.
  953. */
  954. function edit_form_submit(&$form, &$form_state) {
  955. if (!empty($this->plugin['form']['submit'])) {
  956. // Pass $form by reference.
  957. $this->plugin['form']['submit']($form, $form_state);
  958. }
  959. // Transfer data from the form to the $item based upon schema values.
  960. $schema = ctools_export_get_schema($this->plugin['schema']);
  961. foreach (array_keys($schema['fields']) as $key) {
  962. if(isset($form_state['values'][$key])) {
  963. $form_state['item']->{$key} = $form_state['values'][$key];
  964. }
  965. }
  966. }
  967. // ------------------------------------------------------------------------
  968. // These methods are the API for 'other' stuff with exportables such as
  969. // enable, disable, import, export, delete
  970. /**
  971. * Callback to enable a page.
  972. */
  973. function enable_page($js, $input, $item) {
  974. return $this->set_item_state(FALSE, $js, $input, $item);
  975. }
  976. /**
  977. * Callback to disable a page.
  978. */
  979. function disable_page($js, $input, $item) {
  980. return $this->set_item_state(TRUE, $js, $input, $item);
  981. }
  982. /**
  983. * Set an item's state to enabled or disabled and output to user.
  984. *
  985. * If javascript is in use, this will rebuild the list and send that back
  986. * as though the filter form had been executed.
  987. */
  988. function set_item_state($state, $js, $input, $item) {
  989. ctools_export_set_object_status($item, $state);
  990. if (!$js) {
  991. drupal_goto(ctools_export_ui_plugin_base_path($this->plugin));
  992. }
  993. else {
  994. return $this->list_page($js, $input);
  995. }
  996. }
  997. /**
  998. * Page callback to delete an exportable item.
  999. */
  1000. function delete_page($js, $input, $item) {
  1001. $form_state = array(
  1002. 'plugin' => $this->plugin,
  1003. 'object' => &$this,
  1004. 'ajax' => $js,
  1005. 'item' => $item,
  1006. 'op' => $item->export_type & EXPORT_IN_CODE ? 'revert' : 'delete',
  1007. 'rerender' => TRUE,
  1008. 'no_redirect' => TRUE,
  1009. );
  1010. ctools_include('form');
  1011. $output = ctools_build_form('ctools_export_ui_delete_confirm_form', $form_state);
  1012. if (!empty($form_state['executed'])) {
  1013. ctools_export_crud_delete($this->plugin['schema'], $item);
  1014. $export_key = $this->plugin['export']['key'];
  1015. $message = str_replace('%title', check_plain($item->{$export_key}), $this->plugin['strings']['confirmation'][$form_state['op']]['success']);
  1016. drupal_set_message($message);
  1017. drupal_goto(ctools_export_ui_plugin_base_path($this->plugin));
  1018. }
  1019. return $output;
  1020. }
  1021. /**
  1022. * Page callback to display export information for an exportable item.
  1023. */
  1024. function export_page($js, $input, $item) {
  1025. drupal_set_title($this->get_page_title('export', $item));
  1026. return drupal_get_form('ctools_export_form', ctools_export_crud_export($this->plugin['schema'], $item), t('Export'));
  1027. }
  1028. /**
  1029. * Page callback to import information for an exportable item.
  1030. */
  1031. function import_page($js, $input, $step = NULL) {
  1032. drupal_set_title($this->get_page_title('import'));
  1033. // Import is basically a multi step wizard form, so let's go ahead and
  1034. // use CTools' wizard.inc for it.
  1035. // If a step not set, they are trying to create a new item. If a step
  1036. // is set, they're in the process of creating an item.
  1037. if (!empty($step)) {
  1038. $item = $this->edit_cache_get(NULL, 'import');
  1039. }
  1040. if (empty($item)) {
  1041. $item = ctools_export_crud_new($this->plugin['schema']);
  1042. }
  1043. $form_state = array(
  1044. 'plugin' => $this->plugin,
  1045. 'object' => &$this,
  1046. 'ajax' => $js,
  1047. 'item' => $item,
  1048. 'op' => 'add',
  1049. 'form type' => 'import',
  1050. 'rerender' => TRUE,
  1051. 'no_redirect' => TRUE,
  1052. 'step' => $step,
  1053. // Store these in case additional args are needed.
  1054. 'function args' => func_get_args(),
  1055. );
  1056. // import always uses the wizard.
  1057. $output = $this->edit_execute_form_wizard($form_state);
  1058. if (!empty($form_state['executed'])) {
  1059. $export_key = $this->plugin['export']['key'];
  1060. drupal_goto(str_replace('%ctools_export_ui', $form_state['item']->{$export_key}, $this->plugin['redirect']['add']));
  1061. }
  1062. return $output;
  1063. }
  1064. /**
  1065. * Import form. Provides simple helptext instructions and textarea for
  1066. * pasting a export definition.
  1067. */
  1068. function edit_form_import(&$form, &$form_state) {
  1069. $form['help'] = array(
  1070. '#type' => 'item',
  1071. '#value' => $this->plugin['strings']['help']['import'],
  1072. );
  1073. $form['import'] = array(
  1074. '#title' => t('@plugin code', array('@plugin' => $this->plugin['title singular proper'])),
  1075. '#type' => 'textarea',
  1076. '#rows' => 10,
  1077. '#required' => TRUE,
  1078. '#default_value' => !empty($form_state['item']->export_ui_code) ? $form_state['item']->export_ui_code : '',
  1079. );
  1080. $form['overwrite'] = array(
  1081. '#title' => t('Allow import to overwrite an existing record.'),
  1082. '#type' => 'checkbox',
  1083. '#default_value' => !empty($form_state['item']->export_ui_allow_overwrite) ? $form_state['item']->export_ui_allow_overwrite : FALSE,
  1084. );
  1085. }
  1086. /**
  1087. * Import form validate handler.
  1088. *
  1089. * Evaluates code and make sure it creates an object before we continue.
  1090. */
  1091. function edit_form_import_validate($form, &$form_state) {
  1092. $item = ctools_export_crud_import($this->plugin['schema'], $form_state['values']['import']);
  1093. if (is_string($item)) {
  1094. form_error($form['import'], t('Unable to get an import from the code. Errors reported: @errors', array('@errors' => $item)));
  1095. return;
  1096. }
  1097. $form_state['item'] = $item;
  1098. $form_state['item']->export_ui_allow_overwrite = $form_state['values']['overwrite'];
  1099. $form_state['item']->export_ui_code = $form_state['values']['import'];
  1100. }
  1101. /**
  1102. * Submit callback for import form.
  1103. *
  1104. * Stores the item in the session.
  1105. */
  1106. function edit_form_import_submit($form, &$form_state) {
  1107. // The validate function already imported and stored the item. This
  1108. // function exists simply to prevent it from going to the default
  1109. // edit_form_submit() method.
  1110. }
  1111. }
  1112. // -----------------------------------------------------------------------
  1113. // Forms to be used with this class.
  1114. //
  1115. // Since Drupal's forms are completely procedural, these forms will
  1116. // mostly just be pass-throughs back to the object.
  1117. /**
  1118. * Form callback to handle the filter/sort form when listing items.
  1119. *
  1120. * This simply loads the object defined in the plugin and hands it off.
  1121. */
  1122. function ctools_export_ui_list_form(&$form_state) {
  1123. $form = array();
  1124. $form_state['object']->list_form($form, $form_state);
  1125. return $form;
  1126. }
  1127. /**
  1128. * Validate handler for ctools_export_ui_list_form.
  1129. */
  1130. function ctools_export_ui_list_form_validate(&$form, &$form_state) {
  1131. $form_state['object']->list_form_validate($form, $form_state);
  1132. }
  1133. /**
  1134. * Submit handler for ctools_export_ui_list_form.
  1135. */
  1136. function ctools_export_ui_list_form_submit(&$form, &$form_state) {
  1137. $form_state['object']->list_form_submit($form, $form_state);
  1138. }
  1139. /**
  1140. * Form callback to edit an exportable item.
  1141. *
  1142. * This simply loads the object defined in the plugin and hands it off.
  1143. */
  1144. function ctools_export_ui_edit_item_form(&$form_state) {
  1145. $form = array();
  1146. $form_state['object']->edit_form($form, $form_state);
  1147. return $form;
  1148. }
  1149. /**
  1150. * Validate handler for ctools_export_ui_edit_item_form.
  1151. */
  1152. function ctools_export_ui_edit_item_form_validate(&$form, &$form_state) {
  1153. $form_state['object']->edit_form_validate($form, $form_state);
  1154. }
  1155. /**
  1156. * Submit handler for ctools_export_ui_edit_item_form.
  1157. */
  1158. function ctools_export_ui_edit_item_form_submit(&$form, &$form_state) {
  1159. $form_state['object']->edit_form_submit($form, $form_state);
  1160. }
  1161. /**
  1162. * Submit handler to delete for ctools_export_ui_edit_item_form
  1163. *
  1164. * @todo Put this on a callback in the object.
  1165. */
  1166. function ctools_export_ui_edit_item_form_delete(&$form, &$form_state) {
  1167. $export_key = $form_state['plugin']['export']['key'];
  1168. $path = $form_state['item']->export_type & EXPORT_IN_CODE ? 'revert' : 'delete';
  1169. drupal_goto(ctools_export_ui_plugin_menu_path($form_state['plugin'], $path, $form_state['item']->{$export_key}), array('cancel_path' => $_GET['q']));
  1170. }
  1171. /**
  1172. * Validate that an export item name is acceptable and unique during add.
  1173. */
  1174. function ctools_export_ui_edit_name_validate($element, &$form_state) {
  1175. $plugin = $form_state['plugin'];
  1176. // Check for string identifier sanity
  1177. if (!preg_match('!^[a-z0-9_]+$!', $element['#value'])) {
  1178. form_error($element, t('The export id can only consist of lowercase letters, underscores, and numbers.'));
  1179. return;
  1180. }
  1181. // Check for name collision
  1182. if (empty($form_state['item']->export_ui_allow_overwrite) && $exists = ctools_export_crud_load($plugin['schema'], $element['#value'])) {
  1183. form_error($element, t('A @plugin with this name already exists. Please choose another name or delete the existing item before creating a new one.', array('@plugin' => $plugin['title singular'])));
  1184. }
  1185. }
  1186. /**
  1187. * Delete/Revert confirm form.
  1188. *
  1189. * @todo -- call back into the object instead.
  1190. */
  1191. function ctools_export_ui_delete_confirm_form(&$form_state) {
  1192. $plugin = $form_state['plugin'];
  1193. $item = $form_state['item'];
  1194. $form = array();
  1195. $export_key = $plugin['export']['key'];
  1196. $question = str_replace('%title', check_plain($item->{$export_key}), $plugin['strings']['confirmation'][$form_state['op']]['question']);
  1197. $path = empty($_REQUEST['cancel_path']) ? ctools_export_ui_plugin_base_path($plugin) : $_REQUEST['cancel_path'];
  1198. $form = confirm_form($form,
  1199. $question,
  1200. $path,
  1201. $plugin['strings']['confirmation'][$form_state['op']]['information'],
  1202. $plugin['allowed operations'][$form_state['op']]['title'], t('Cancel')
  1203. );
  1204. return $form;
  1205. }
  1206. // --------------------------------------------------------------------------
  1207. // Forms and callbacks for using the edit system with the wizard.
  1208. /**
  1209. * Form callback to edit an exportable item using the wizard
  1210. *
  1211. * This simply loads the object defined in the plugin and hands it off.
  1212. */
  1213. function ctools_export_ui_edit_item_wizard_form(&$form, &$form_state) {
  1214. $method = 'edit_form_' . $form_state['step'];
  1215. if (!method_exists($form_state['object'], $method)) {
  1216. $method = 'edit_form';
  1217. }
  1218. $form_state['object']->$method($form, $form_state);
  1219. return $form;
  1220. }
  1221. /**
  1222. * Validate handler for ctools_export_ui_edit_item_wizard_form.
  1223. */
  1224. function ctools_export_ui_edit_item_wizard_form_validate(&$form, &$form_state) {
  1225. $method = 'edit_form_' . $form_state['step'] . '_validate';
  1226. if (!method_exists($form_state['object'], $method)) {
  1227. $method = 'edit_form_validate';
  1228. }
  1229. $form_state['object']->$method($form, $form_state);
  1230. // Additionally, if there were no errors from that, and we're finishing,
  1231. // perform a final validate to make sure everything is ok.
  1232. if (isset($form_state['clicked_button']['#wizard type']) && $form_state['clicked_button']['#wizard type'] == 'finish' && !form_get_errors()) {
  1233. $form_state['object']->edit_finish_validate($form, $form_state);
  1234. }
  1235. }
  1236. /**
  1237. * Submit handler for ctools_export_ui_edit_item_wizard_form.
  1238. */
  1239. function ctools_export_ui_edit_item_wizard_form_submit(&$form, &$form_state) {
  1240. $method = 'edit_form_' . $form_state['step'] . '_submit';
  1241. if (!method_exists($form_state['object'], $method)) {
  1242. $method = 'edit_form_submit';
  1243. }
  1244. $form_state['object']->$method($form, $form_state);
  1245. }
  1246. /**
  1247. * Wizard 'back' callback when using a wizard to edit an item.
  1248. */
  1249. function ctools_export_ui_wizard_back(&$form_state) {
  1250. $form_state['object']->edit_wizard_back($form_state);
  1251. }
  1252. /**
  1253. * Wizard 'next' callback when using a wizard to edit an item.
  1254. */
  1255. function ctools_export_ui_wizard_next(&$form_state) {
  1256. $form_state['object']->edit_wizard_next($form_state);
  1257. }
  1258. /**
  1259. * Wizard 'cancel' callback when using a wizard to edit an item.
  1260. */
  1261. function ctools_export_ui_wizard_cancel(&$form_state) {
  1262. $form_state['object']->edit_wizard_cancel($form_state);
  1263. }
  1264. /**
  1265. * Wizard 'finish' callback when using a wizard to edit an item.
  1266. */
  1267. function ctools_export_ui_wizard_finish(&$form_state) {
  1268. $form_state['object']->edit_wizard_finish($form_state);
  1269. }