danielebarchiesi@0
|
1 <?php
|
danielebarchiesi@0
|
2
|
danielebarchiesi@0
|
3 /**
|
danielebarchiesi@0
|
4 * @file
|
danielebarchiesi@0
|
5 * CTools' multi-step form wizard tool.
|
danielebarchiesi@0
|
6 *
|
danielebarchiesi@0
|
7 * This tool enables the creation of multi-step forms that go from one
|
danielebarchiesi@0
|
8 * form to another. The forms themselves can allow branching if they
|
danielebarchiesi@0
|
9 * like, and there are a number of configurable options to how
|
danielebarchiesi@0
|
10 * the wizard operates.
|
danielebarchiesi@0
|
11 *
|
danielebarchiesi@0
|
12 * The wizard can also be friendly to ajax forms, such as when used
|
danielebarchiesi@0
|
13 * with the modal tool.
|
danielebarchiesi@0
|
14 *
|
danielebarchiesi@0
|
15 * The wizard provides callbacks throughout the process, allowing the
|
danielebarchiesi@0
|
16 * owner to control the flow. The general flow of what happens is:
|
danielebarchiesi@0
|
17 *
|
danielebarchiesi@0
|
18 * Generate a form
|
danielebarchiesi@0
|
19 * submit a form
|
danielebarchiesi@0
|
20 * based upon button clicked, 'finished', 'next form', 'cancel' or 'return'.
|
danielebarchiesi@0
|
21 *
|
danielebarchiesi@0
|
22 * Each action has its own callback, so cached objects can be modifed and or
|
danielebarchiesi@0
|
23 * turned into real objects. Each callback can make decisions about where to
|
danielebarchiesi@0
|
24 * go next if it wishes to override the default flow.
|
danielebarchiesi@0
|
25 */
|
danielebarchiesi@0
|
26
|
danielebarchiesi@0
|
27 /**
|
danielebarchiesi@0
|
28 * Display a multi-step form.
|
danielebarchiesi@0
|
29 *
|
danielebarchiesi@0
|
30 * Aside from the addition of the $form_info which contains an array of
|
danielebarchiesi@0
|
31 * information and configuration so the multi-step wizard can do its thing,
|
danielebarchiesi@0
|
32 * this function works a lot like drupal_build_form.
|
danielebarchiesi@0
|
33 *
|
danielebarchiesi@0
|
34 * Remember that the form builders for this form will receive
|
danielebarchiesi@0
|
35 * &$form, &$form_state, NOT just &$form_state and no additional args.
|
danielebarchiesi@0
|
36 *
|
danielebarchiesi@0
|
37 * @param $form_info
|
danielebarchiesi@0
|
38 * An array of form info. @todo document the array.
|
danielebarchiesi@0
|
39 * @param $step
|
danielebarchiesi@0
|
40 * The current form step.
|
danielebarchiesi@0
|
41 * @param &$form_state
|
danielebarchiesi@0
|
42 * The form state array; this is a reference so the caller can get back
|
danielebarchiesi@0
|
43 * whatever information the form(s) involved left for it.
|
danielebarchiesi@0
|
44 */
|
danielebarchiesi@0
|
45 function ctools_wizard_multistep_form($form_info, $step, &$form_state) {
|
danielebarchiesi@0
|
46 // Make sure 'wizard' always exists for the form when dealing
|
danielebarchiesi@0
|
47 // with form caching.
|
danielebarchiesi@0
|
48 ctools_form_include($form_state, 'wizard');
|
danielebarchiesi@0
|
49
|
danielebarchiesi@0
|
50 // allow order array to be optional
|
danielebarchiesi@0
|
51 if (empty($form_info['order'])) {
|
danielebarchiesi@0
|
52 foreach ($form_info['forms'] as $step_id => $params) {
|
danielebarchiesi@0
|
53 $form_info['order'][$step_id] = $params['title'];
|
danielebarchiesi@0
|
54 }
|
danielebarchiesi@0
|
55 }
|
danielebarchiesi@0
|
56
|
danielebarchiesi@0
|
57 if (!isset($step)) {
|
danielebarchiesi@0
|
58 $keys = array_keys($form_info['order']);
|
danielebarchiesi@0
|
59 $step = array_shift($keys);
|
danielebarchiesi@0
|
60 }
|
danielebarchiesi@0
|
61
|
danielebarchiesi@0
|
62 ctools_wizard_defaults($form_info);
|
danielebarchiesi@0
|
63
|
danielebarchiesi@0
|
64 // If automated caching is enabled, ensure that everything is as it
|
danielebarchiesi@0
|
65 // should be.
|
danielebarchiesi@0
|
66 if (!empty($form_info['auto cache'])) {
|
danielebarchiesi@0
|
67 // If the cache mechanism hasn't been set, default to the simple
|
danielebarchiesi@0
|
68 // mechanism and use the wizard ID to ensure uniqueness so cache
|
danielebarchiesi@0
|
69 // objects don't stomp on each other.
|
danielebarchiesi@0
|
70 if (!isset($form_info['cache mechanism'])) {
|
danielebarchiesi@0
|
71 $form_info['cache mechanism'] = 'simple::wizard::' . $form_info['id'];
|
danielebarchiesi@0
|
72 }
|
danielebarchiesi@0
|
73
|
danielebarchiesi@0
|
74 // If not set, default the cache key to the wizard ID. This is often
|
danielebarchiesi@0
|
75 // a unique ID of the object being edited or similar.
|
danielebarchiesi@0
|
76 if (!isset($form_info['cache key'])) {
|
danielebarchiesi@0
|
77 $form_info['cache key'] = $form_info['id'];
|
danielebarchiesi@0
|
78 }
|
danielebarchiesi@0
|
79
|
danielebarchiesi@0
|
80 // If not set, default the cache location to storage. This is often
|
danielebarchiesi@0
|
81 // somnething like 'conf'.
|
danielebarchiesi@0
|
82 if (!isset($form_info['cache location'])) {
|
danielebarchiesi@0
|
83 $form_info['cache location'] = 'storage';
|
danielebarchiesi@0
|
84 }
|
danielebarchiesi@0
|
85
|
danielebarchiesi@0
|
86 // If absolutely nothing was set for the cache area to work on
|
danielebarchiesi@0
|
87 if (!isset($form_state[$form_info['cache location']])) {
|
danielebarchiesi@0
|
88 ctools_include('cache');
|
danielebarchiesi@0
|
89 $form_state[$form_info['cache location']] = ctools_cache_get($form_info['cache mechanism'], $form_info['cache key']);
|
danielebarchiesi@0
|
90 }
|
danielebarchiesi@0
|
91 }
|
danielebarchiesi@0
|
92
|
danielebarchiesi@0
|
93 $form_state['step'] = $step;
|
danielebarchiesi@0
|
94 $form_state['form_info'] = $form_info;
|
danielebarchiesi@0
|
95
|
danielebarchiesi@0
|
96 // Ensure we have form information for the current step.
|
danielebarchiesi@0
|
97 if (!isset($form_info['forms'][$step])) {
|
danielebarchiesi@0
|
98 return;
|
danielebarchiesi@0
|
99 }
|
danielebarchiesi@0
|
100
|
danielebarchiesi@0
|
101 // Ensure that whatever include file(s) were requested by the form info are
|
danielebarchiesi@0
|
102 // actually included.
|
danielebarchiesi@0
|
103 $info = $form_info['forms'][$step];
|
danielebarchiesi@0
|
104
|
danielebarchiesi@0
|
105 if (!empty($info['include'])) {
|
danielebarchiesi@0
|
106 if (is_array($info['include'])) {
|
danielebarchiesi@0
|
107 foreach ($info['include'] as $file) {
|
danielebarchiesi@0
|
108 ctools_form_include_file($form_state, $file);
|
danielebarchiesi@0
|
109 }
|
danielebarchiesi@0
|
110 }
|
danielebarchiesi@0
|
111 else {
|
danielebarchiesi@0
|
112 ctools_form_include_file($form_state, $info['include']);
|
danielebarchiesi@0
|
113 }
|
danielebarchiesi@0
|
114 }
|
danielebarchiesi@0
|
115
|
danielebarchiesi@0
|
116 // This tells drupal_build_form to apply our wrapper to the form. It
|
danielebarchiesi@0
|
117 // will give it buttons and the like.
|
danielebarchiesi@0
|
118 $form_state['wrapper_callback'] = 'ctools_wizard_wrapper';
|
danielebarchiesi@0
|
119 if (!isset($form_state['rerender'])) {
|
danielebarchiesi@0
|
120 $form_state['rerender'] = FALSE;
|
danielebarchiesi@0
|
121 }
|
danielebarchiesi@0
|
122
|
danielebarchiesi@0
|
123 $form_state['no_redirect'] = TRUE;
|
danielebarchiesi@0
|
124
|
danielebarchiesi@0
|
125 $output = drupal_build_form($info['form id'], $form_state);
|
danielebarchiesi@0
|
126
|
danielebarchiesi@0
|
127 if (empty($form_state['executed']) || !empty($form_state['rerender'])) {
|
danielebarchiesi@0
|
128 if (empty($form_state['title']) && !empty($info['title'])) {
|
danielebarchiesi@0
|
129 $form_state['title'] = $info['title'];
|
danielebarchiesi@0
|
130 }
|
danielebarchiesi@0
|
131
|
danielebarchiesi@0
|
132 if (!empty($form_state['ajax render'])) {
|
danielebarchiesi@0
|
133 // Any include files should already be included by this point:
|
danielebarchiesi@0
|
134 return $form_state['ajax render']($form_state, $output);
|
danielebarchiesi@0
|
135 }
|
danielebarchiesi@0
|
136
|
danielebarchiesi@0
|
137 // Automatically use the modal tool if set to true.
|
danielebarchiesi@0
|
138 if (!empty($form_state['modal']) && empty($form_state['modal return'])) {
|
danielebarchiesi@0
|
139 ctools_include('modal');
|
danielebarchiesi@0
|
140
|
danielebarchiesi@0
|
141 // This overwrites any previous commands.
|
danielebarchiesi@0
|
142 $form_state['commands'] = ctools_modal_form_render($form_state, $output);
|
danielebarchiesi@0
|
143 }
|
danielebarchiesi@0
|
144 }
|
danielebarchiesi@0
|
145
|
danielebarchiesi@0
|
146 if (!empty($form_state['executed'])) {
|
danielebarchiesi@0
|
147 // We use the plugins get_function format because it's powerful and
|
danielebarchiesi@0
|
148 // not limited to just functions.
|
danielebarchiesi@0
|
149 ctools_include('plugins');
|
danielebarchiesi@0
|
150
|
danielebarchiesi@0
|
151 if (isset($form_state['clicked_button']['#wizard type'])) {
|
danielebarchiesi@0
|
152 $type = $form_state['clicked_button']['#wizard type'];
|
danielebarchiesi@0
|
153 // If we have a callback depending upon the type of button that was
|
danielebarchiesi@0
|
154 // clicked, call it.
|
danielebarchiesi@0
|
155 if ($function = ctools_plugin_get_function($form_info, "$type callback")) {
|
danielebarchiesi@0
|
156 $function($form_state);
|
danielebarchiesi@0
|
157 }
|
danielebarchiesi@0
|
158
|
danielebarchiesi@0
|
159 // If auto-caching is on, we need to write the cache on next and
|
danielebarchiesi@0
|
160 // clear the cache on finish.
|
danielebarchiesi@0
|
161 if (!empty($form_info['auto cache'])) {
|
danielebarchiesi@0
|
162 if ($type == 'next') {
|
danielebarchiesi@0
|
163 ctools_include('cache');
|
danielebarchiesi@0
|
164 ctools_cache_set($form_info['cache mechanism'], $form_info['cache key'], $form_state[$form_info['cache location']]);
|
danielebarchiesi@0
|
165 }
|
danielebarchiesi@0
|
166 elseif ($type == 'finish') {
|
danielebarchiesi@0
|
167 ctools_include('cache');
|
danielebarchiesi@0
|
168 ctools_cache_clear($form_info['cache mechanism'], $form_info['cache key']);
|
danielebarchiesi@0
|
169 }
|
danielebarchiesi@0
|
170 }
|
danielebarchiesi@0
|
171
|
danielebarchiesi@0
|
172 // Set a couple of niceties:
|
danielebarchiesi@0
|
173 if ($type == 'finish') {
|
danielebarchiesi@0
|
174 $form_state['complete'] = TRUE;
|
danielebarchiesi@0
|
175 }
|
danielebarchiesi@0
|
176
|
danielebarchiesi@0
|
177 if ($type == 'cancel') {
|
danielebarchiesi@0
|
178 $form_state['cancel'] = TRUE;
|
danielebarchiesi@0
|
179 }
|
danielebarchiesi@0
|
180
|
danielebarchiesi@0
|
181 // If the modal is in use, some special code for it:
|
danielebarchiesi@0
|
182 if (!empty($form_state['modal']) && empty($form_state['modal return'])) {
|
danielebarchiesi@0
|
183 if ($type != 'next') {
|
danielebarchiesi@0
|
184 // Automatically dismiss the modal if we're not going to another form.
|
danielebarchiesi@0
|
185 ctools_include('modal');
|
danielebarchiesi@0
|
186 $form_state['commands'][] = ctools_modal_command_dismiss();
|
danielebarchiesi@0
|
187 }
|
danielebarchiesi@0
|
188 }
|
danielebarchiesi@0
|
189 }
|
danielebarchiesi@0
|
190
|
danielebarchiesi@0
|
191 if (empty($form_state['ajax'])) {
|
danielebarchiesi@0
|
192 // redirect, if one is set.
|
danielebarchiesi@0
|
193 if ($form_state['redirect']) {
|
danielebarchiesi@0
|
194 if (is_array($form_state['redirect'])) {
|
danielebarchiesi@0
|
195 call_user_func_array('drupal_goto', $form_state['redirect']);
|
danielebarchiesi@0
|
196 }
|
danielebarchiesi@0
|
197 else {
|
danielebarchiesi@0
|
198 drupal_goto($form_state['redirect']);
|
danielebarchiesi@0
|
199 }
|
danielebarchiesi@0
|
200 }
|
danielebarchiesi@0
|
201 }
|
danielebarchiesi@0
|
202 else if (isset($form_state['ajax next'])) {
|
danielebarchiesi@0
|
203 // Clear a few items off the form state so we don't double post:
|
danielebarchiesi@0
|
204 $next = $form_state['ajax next'];
|
danielebarchiesi@0
|
205 unset($form_state['ajax next']);
|
danielebarchiesi@0
|
206 unset($form_state['executed']);
|
danielebarchiesi@0
|
207 unset($form_state['post']);
|
danielebarchiesi@0
|
208 unset($form_state['next']);
|
danielebarchiesi@0
|
209 return ctools_wizard_multistep_form($form_info, $next, $form_state);
|
danielebarchiesi@0
|
210 }
|
danielebarchiesi@0
|
211
|
danielebarchiesi@0
|
212 // If the callbacks wanted to do something besides go to the next form,
|
danielebarchiesi@0
|
213 // it needs to have set $form_state['commands'] with something that can
|
danielebarchiesi@0
|
214 // be rendered.
|
danielebarchiesi@0
|
215 }
|
danielebarchiesi@0
|
216
|
danielebarchiesi@0
|
217 // Render ajax commands if we have any.
|
danielebarchiesi@0
|
218 if (isset($form_state['ajax']) && isset($form_state['commands']) && empty($form_state['modal return'])) {
|
danielebarchiesi@0
|
219 return ajax_render($form_state['commands']);
|
danielebarchiesi@0
|
220 }
|
danielebarchiesi@0
|
221
|
danielebarchiesi@0
|
222 // Otherwise, return the output.
|
danielebarchiesi@0
|
223 return $output;
|
danielebarchiesi@0
|
224 }
|
danielebarchiesi@0
|
225
|
danielebarchiesi@0
|
226 /**
|
danielebarchiesi@0
|
227 * Provide a wrapper around another form for adding multi-step information.
|
danielebarchiesi@0
|
228 */
|
danielebarchiesi@0
|
229 function ctools_wizard_wrapper($form, &$form_state) {
|
danielebarchiesi@0
|
230 $form_info = &$form_state['form_info'];
|
danielebarchiesi@0
|
231 $info = $form_info['forms'][$form_state['step']];
|
danielebarchiesi@0
|
232
|
danielebarchiesi@0
|
233 // Determine the next form from this step.
|
danielebarchiesi@0
|
234 // Create a form trail if we're supposed to have one.
|
danielebarchiesi@0
|
235 $trail = array();
|
danielebarchiesi@0
|
236 $previous = TRUE;
|
danielebarchiesi@0
|
237 foreach ($form_info['order'] as $id => $title) {
|
danielebarchiesi@0
|
238 if ($id == $form_state['step']) {
|
danielebarchiesi@0
|
239 $previous = FALSE;
|
danielebarchiesi@0
|
240 $class = 'wizard-trail-current';
|
danielebarchiesi@0
|
241 }
|
danielebarchiesi@0
|
242 elseif ($previous) {
|
danielebarchiesi@0
|
243 $not_first = TRUE;
|
danielebarchiesi@0
|
244 $class = 'wizard-trail-previous';
|
danielebarchiesi@0
|
245 $form_state['previous'] = $id;
|
danielebarchiesi@0
|
246 }
|
danielebarchiesi@0
|
247 else {
|
danielebarchiesi@0
|
248 $class = 'wizard-trail-next';
|
danielebarchiesi@0
|
249 if (!isset($form_state['next'])) {
|
danielebarchiesi@0
|
250 $form_state['next'] = $id;
|
danielebarchiesi@0
|
251 }
|
danielebarchiesi@0
|
252 if (empty($form_info['show trail'])) {
|
danielebarchiesi@0
|
253 break;
|
danielebarchiesi@0
|
254 }
|
danielebarchiesi@0
|
255 }
|
danielebarchiesi@0
|
256
|
danielebarchiesi@0
|
257 if (!empty($form_info['show trail'])) {
|
danielebarchiesi@0
|
258 if (!empty($form_info['free trail'])) {
|
danielebarchiesi@0
|
259 // ctools_wizard_get_path() returns results suitable for
|
danielebarchiesi@0
|
260 // $form_state['redirect] which can only be directly used in
|
danielebarchiesi@0
|
261 // drupal_goto. We have to futz a bit with it.
|
danielebarchiesi@0
|
262 $path = ctools_wizard_get_path($form_info, $id);
|
danielebarchiesi@0
|
263 $options = array();
|
danielebarchiesi@0
|
264 if (!empty($path[1])) {
|
danielebarchiesi@0
|
265 $options = $path[1];
|
danielebarchiesi@0
|
266 }
|
danielebarchiesi@0
|
267 $title = l($title, $path[0], $options);
|
danielebarchiesi@0
|
268 }
|
danielebarchiesi@0
|
269 $trail[] = '<span class="' . $class . '">' . $title . '</span>';
|
danielebarchiesi@0
|
270 }
|
danielebarchiesi@0
|
271 }
|
danielebarchiesi@0
|
272
|
danielebarchiesi@0
|
273 // Display the trail if instructed to do so.
|
danielebarchiesi@0
|
274 if (!empty($form_info['show trail'])) {
|
danielebarchiesi@0
|
275 ctools_add_css('wizard');
|
danielebarchiesi@0
|
276 $form['ctools_trail'] = array(
|
danielebarchiesi@0
|
277 '#markup' => theme(array('ctools_wizard_trail__' . $form_info['id'], 'ctools_wizard_trail'), array('trail' => $trail)),
|
danielebarchiesi@0
|
278 '#weight' => -1000,
|
danielebarchiesi@0
|
279 );
|
danielebarchiesi@0
|
280 }
|
danielebarchiesi@0
|
281
|
danielebarchiesi@0
|
282 if (empty($form_info['no buttons'])) {
|
danielebarchiesi@0
|
283 // Ensure buttons stay on the bottom.
|
danielebarchiesi@0
|
284 $form['buttons'] = array(
|
danielebarchiesi@0
|
285 '#type' => 'actions',
|
danielebarchiesi@0
|
286 '#weight' => 1000,
|
danielebarchiesi@0
|
287 );
|
danielebarchiesi@0
|
288
|
danielebarchiesi@0
|
289 $button_attributes = array();
|
danielebarchiesi@0
|
290 if (!empty($form_state['ajax']) && empty($form_state['modal'])) {
|
danielebarchiesi@0
|
291 $button_attributes = array('class' => array('ctools-use-ajax'));
|
danielebarchiesi@0
|
292 }
|
danielebarchiesi@0
|
293
|
danielebarchiesi@0
|
294 if (!empty($form_info['show back']) && isset($form_state['previous'])) {
|
danielebarchiesi@0
|
295 $form['buttons']['previous'] = array(
|
danielebarchiesi@0
|
296 '#type' => 'submit',
|
danielebarchiesi@0
|
297 '#value' => $form_info['back text'],
|
danielebarchiesi@0
|
298 '#next' => $form_state['previous'],
|
danielebarchiesi@0
|
299 '#wizard type' => 'next',
|
danielebarchiesi@0
|
300 '#weight' => -2000,
|
danielebarchiesi@0
|
301 '#limit_validation_errors' => array(),
|
danielebarchiesi@0
|
302 // hardcode the submit so that it doesn't try to save data.
|
danielebarchiesi@0
|
303 '#submit' => array('ctools_wizard_submit'),
|
danielebarchiesi@0
|
304 '#attributes' => $button_attributes,
|
danielebarchiesi@0
|
305 );
|
danielebarchiesi@0
|
306
|
danielebarchiesi@0
|
307 if (isset($form_info['no back validate']) || isset($info['no back validate'])) {
|
danielebarchiesi@0
|
308 $form['buttons']['previous']['#validate'] = array();
|
danielebarchiesi@0
|
309 }
|
danielebarchiesi@0
|
310 }
|
danielebarchiesi@0
|
311
|
danielebarchiesi@0
|
312 // If there is a next form, place the next button.
|
danielebarchiesi@0
|
313 if (isset($form_state['next']) || !empty($form_info['free trail'])) {
|
danielebarchiesi@0
|
314 $form['buttons']['next'] = array(
|
danielebarchiesi@0
|
315 '#type' => 'submit',
|
danielebarchiesi@0
|
316 '#value' => $form_info['next text'],
|
danielebarchiesi@0
|
317 '#next' => !empty($form_info['free trail']) ? $form_state['step'] : $form_state['next'],
|
danielebarchiesi@0
|
318 '#wizard type' => 'next',
|
danielebarchiesi@0
|
319 '#weight' => -1000,
|
danielebarchiesi@0
|
320 '#attributes' => $button_attributes,
|
danielebarchiesi@0
|
321 );
|
danielebarchiesi@0
|
322 }
|
danielebarchiesi@0
|
323
|
danielebarchiesi@0
|
324 // There are two ways the return button can appear. If this is not the
|
danielebarchiesi@0
|
325 // end of the form list (i.e, there is a next) then it's "update and return"
|
danielebarchiesi@0
|
326 // to be clear. If this is the end of the path and there is no next, we
|
danielebarchiesi@0
|
327 // call it 'Finish'.
|
danielebarchiesi@0
|
328
|
danielebarchiesi@0
|
329 // Even if there is no direct return path (some forms may not want you
|
danielebarchiesi@0
|
330 // leaving in the middle) the final button is always a Finish and it does
|
danielebarchiesi@0
|
331 // whatever the return action is.
|
danielebarchiesi@0
|
332 if (!empty($form_info['show return']) && !empty($form_state['next'])) {
|
danielebarchiesi@0
|
333 $form['buttons']['return'] = array(
|
danielebarchiesi@0
|
334 '#type' => 'submit',
|
danielebarchiesi@0
|
335 '#value' => $form_info['return text'],
|
danielebarchiesi@0
|
336 '#wizard type' => 'return',
|
danielebarchiesi@0
|
337 '#attributes' => $button_attributes,
|
danielebarchiesi@0
|
338 );
|
danielebarchiesi@0
|
339 }
|
danielebarchiesi@0
|
340 else if (empty($form_state['next']) || !empty($form_info['free trail'])) {
|
danielebarchiesi@0
|
341 $form['buttons']['return'] = array(
|
danielebarchiesi@0
|
342 '#type' => 'submit',
|
danielebarchiesi@0
|
343 '#value' => $form_info['finish text'],
|
danielebarchiesi@0
|
344 '#wizard type' => 'finish',
|
danielebarchiesi@0
|
345 '#attributes' => $button_attributes,
|
danielebarchiesi@0
|
346 );
|
danielebarchiesi@0
|
347 }
|
danielebarchiesi@0
|
348
|
danielebarchiesi@0
|
349 // If we are allowed to cancel, place a cancel button.
|
danielebarchiesi@0
|
350 if ((isset($form_info['cancel path']) && !isset($form_info['show cancel'])) || !empty($form_info['show cancel'])) {
|
danielebarchiesi@0
|
351 $form['buttons']['cancel'] = array(
|
danielebarchiesi@0
|
352 '#type' => 'submit',
|
danielebarchiesi@0
|
353 '#value' => $form_info['cancel text'],
|
danielebarchiesi@0
|
354 '#wizard type' => 'cancel',
|
danielebarchiesi@0
|
355 // hardcode the submit so that it doesn't try to save data.
|
danielebarchiesi@0
|
356 '#limit_validation_errors' => array(),
|
danielebarchiesi@0
|
357 '#submit' => array('ctools_wizard_submit'),
|
danielebarchiesi@0
|
358 '#attributes' => $button_attributes,
|
danielebarchiesi@0
|
359 );
|
danielebarchiesi@0
|
360 }
|
danielebarchiesi@0
|
361
|
danielebarchiesi@0
|
362 // Set up optional validate handlers.
|
danielebarchiesi@0
|
363 $form['#validate'] = array();
|
danielebarchiesi@0
|
364 if (function_exists($info['form id'] . '_validate')) {
|
danielebarchiesi@0
|
365 $form['#validate'][] = $info['form id'] . '_validate';
|
danielebarchiesi@0
|
366 }
|
danielebarchiesi@0
|
367 if (isset($info['validate']) && function_exists($info['validate'])) {
|
danielebarchiesi@0
|
368 $form['#validate'][] = $info['validate'];
|
danielebarchiesi@0
|
369 }
|
danielebarchiesi@0
|
370
|
danielebarchiesi@0
|
371 // Set up our submit handler after theirs. Since putting something here will
|
danielebarchiesi@0
|
372 // skip Drupal's autodetect, we autodetect for it.
|
danielebarchiesi@0
|
373
|
danielebarchiesi@0
|
374 // We make sure ours is after theirs so that they get to change #next if
|
danielebarchiesi@0
|
375 // the want to.
|
danielebarchiesi@0
|
376 $form['#submit'] = array();
|
danielebarchiesi@0
|
377 if (function_exists($info['form id'] . '_submit')) {
|
danielebarchiesi@0
|
378 $form['#submit'][] = $info['form id'] . '_submit';
|
danielebarchiesi@0
|
379 }
|
danielebarchiesi@0
|
380 if (isset($info['submit']) && function_exists($info['submit'])) {
|
danielebarchiesi@0
|
381 $form['#submit'][] = $info['submit'];
|
danielebarchiesi@0
|
382 }
|
danielebarchiesi@0
|
383 $form['#submit'][] = 'ctools_wizard_submit';
|
danielebarchiesi@0
|
384 }
|
danielebarchiesi@0
|
385
|
danielebarchiesi@0
|
386 if (!empty($form_state['ajax'])) {
|
danielebarchiesi@0
|
387 $params = ctools_wizard_get_path($form_state['form_info'], $form_state['step']);
|
danielebarchiesi@0
|
388 if (count($params) > 1) {
|
danielebarchiesi@0
|
389 $url = array_shift($params);
|
danielebarchiesi@0
|
390 $options = array();
|
danielebarchiesi@0
|
391
|
danielebarchiesi@0
|
392 $keys = array(0 => 'query', 1 => 'fragment');
|
danielebarchiesi@0
|
393 foreach ($params as $key => $value) {
|
danielebarchiesi@0
|
394 if (isset($keys[$key]) && isset($value)) {
|
danielebarchiesi@0
|
395 $options[$keys[$key]] = $value;
|
danielebarchiesi@0
|
396 }
|
danielebarchiesi@0
|
397 }
|
danielebarchiesi@0
|
398
|
danielebarchiesi@0
|
399 $params = array($url, $options);
|
danielebarchiesi@0
|
400 }
|
danielebarchiesi@0
|
401 $form['#action'] = call_user_func_array('url', $params);
|
danielebarchiesi@0
|
402 }
|
danielebarchiesi@0
|
403
|
danielebarchiesi@0
|
404 if (isset($info['wrapper']) && function_exists($info['wrapper'])) {
|
danielebarchiesi@0
|
405 $form = $info['wrapper']($form, $form_state);
|
danielebarchiesi@0
|
406 }
|
danielebarchiesi@0
|
407
|
danielebarchiesi@0
|
408 if (isset($form_info['wrapper']) && function_exists($form_info['wrapper'])) {
|
danielebarchiesi@0
|
409 $form = $form_info['wrapper']($form, $form_state);
|
danielebarchiesi@0
|
410 }
|
danielebarchiesi@0
|
411 return $form;
|
danielebarchiesi@0
|
412 }
|
danielebarchiesi@0
|
413
|
danielebarchiesi@0
|
414 /**
|
danielebarchiesi@0
|
415 * On a submit, go to the next form.
|
danielebarchiesi@0
|
416 */
|
danielebarchiesi@0
|
417 function ctools_wizard_submit(&$form, &$form_state) {
|
danielebarchiesi@0
|
418 if (isset($form_state['clicked_button']['#wizard type'])) {
|
danielebarchiesi@0
|
419 $type = $form_state['clicked_button']['#wizard type'];
|
danielebarchiesi@0
|
420
|
danielebarchiesi@0
|
421 // if AJAX enabled, we proceed slightly differently here.
|
danielebarchiesi@0
|
422 if (!empty($form_state['ajax'])) {
|
danielebarchiesi@0
|
423 if ($type == 'next') {
|
danielebarchiesi@0
|
424 $form_state['ajax next'] = $form_state['clicked_button']['#next'];
|
danielebarchiesi@0
|
425 }
|
danielebarchiesi@0
|
426 }
|
danielebarchiesi@0
|
427 else {
|
danielebarchiesi@0
|
428 if ($type == 'cancel' && isset($form_state['form_info']['cancel path'])) {
|
danielebarchiesi@0
|
429 $form_state['redirect'] = $form_state['form_info']['cancel path'];
|
danielebarchiesi@0
|
430 }
|
danielebarchiesi@0
|
431 else if ($type == 'next') {
|
danielebarchiesi@0
|
432 $form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']);
|
danielebarchiesi@0
|
433 if (!empty($_GET['destination'])) {
|
danielebarchiesi@0
|
434 // We don't want drupal_goto redirect this request
|
danielebarchiesi@0
|
435 // back. ctools_wizard_get_path ensures that the destination is
|
danielebarchiesi@0
|
436 // carried over on subsequent pages.
|
danielebarchiesi@0
|
437 unset($_GET['destination']);
|
danielebarchiesi@0
|
438 }
|
danielebarchiesi@0
|
439 }
|
danielebarchiesi@0
|
440 else if (isset($form_state['form_info']['return path'])) {
|
danielebarchiesi@0
|
441 $form_state['redirect'] = $form_state['form_info']['return path'];
|
danielebarchiesi@0
|
442 }
|
danielebarchiesi@0
|
443 else if ($type == 'finish' && isset($form_state['form_info']['cancel path'])) {
|
danielebarchiesi@0
|
444 $form_state['redirect'] = $form_state['form_info']['cancel path'];
|
danielebarchiesi@0
|
445 }
|
danielebarchiesi@0
|
446 }
|
danielebarchiesi@0
|
447 }
|
danielebarchiesi@0
|
448 }
|
danielebarchiesi@0
|
449
|
danielebarchiesi@0
|
450 /**
|
danielebarchiesi@0
|
451 * Create a path from the form info and a given step.
|
danielebarchiesi@0
|
452 */
|
danielebarchiesi@0
|
453 function ctools_wizard_get_path($form_info, $step) {
|
danielebarchiesi@0
|
454 if (is_array($form_info['path'])) {
|
danielebarchiesi@0
|
455 foreach ($form_info['path'] as $id => $part) {
|
danielebarchiesi@0
|
456 $form_info['path'][$id] = str_replace('%step', $step, $form_info['path'][$id]);
|
danielebarchiesi@0
|
457 }
|
danielebarchiesi@0
|
458 $path = $form_info['path'];
|
danielebarchiesi@0
|
459 }
|
danielebarchiesi@0
|
460 else {
|
danielebarchiesi@0
|
461 $path = array(str_replace('%step', $step, $form_info['path']));
|
danielebarchiesi@0
|
462 }
|
danielebarchiesi@0
|
463
|
danielebarchiesi@0
|
464 // If destination is set, carry it over so it'll take effect when
|
danielebarchiesi@0
|
465 // saving. The submit handler will unset destination to avoid drupal_goto
|
danielebarchiesi@0
|
466 // redirecting us.
|
danielebarchiesi@0
|
467 if (!empty($_GET['destination'])) {
|
danielebarchiesi@0
|
468 // Ensure that options is an array.
|
danielebarchiesi@0
|
469 if (!isset($path[1]) || !is_array($path[1])) {
|
danielebarchiesi@0
|
470 $path[1] = array();
|
danielebarchiesi@0
|
471 }
|
danielebarchiesi@0
|
472 // Ensure that the query part of options is an array.
|
danielebarchiesi@0
|
473 $path[1] += array('query' => array());
|
danielebarchiesi@0
|
474 // Add the destination parameter, if not set already.
|
danielebarchiesi@0
|
475 $path[1]['query'] += drupal_get_destination();
|
danielebarchiesi@0
|
476 }
|
danielebarchiesi@0
|
477
|
danielebarchiesi@0
|
478 return $path;
|
danielebarchiesi@0
|
479 }
|
danielebarchiesi@0
|
480
|
danielebarchiesi@0
|
481 /**
|
danielebarchiesi@0
|
482 * Set default parameters and callbacks if none are given.
|
danielebarchiesi@0
|
483 * Callbacks follows pattern:
|
danielebarchiesi@0
|
484 * $form_info['id']_$hook
|
danielebarchiesi@0
|
485 * $form_info['id']_$form_info['forms'][$step_key]_$hook
|
danielebarchiesi@0
|
486 */
|
danielebarchiesi@0
|
487 function ctools_wizard_defaults(&$form_info) {
|
danielebarchiesi@0
|
488 $hook = $form_info['id'];
|
danielebarchiesi@0
|
489 $defaults = array(
|
danielebarchiesi@0
|
490 'show trail' => FALSE,
|
danielebarchiesi@0
|
491 'free trail' => FALSE,
|
danielebarchiesi@0
|
492 'show back' => FALSE,
|
danielebarchiesi@0
|
493 'show cancel' => FALSE,
|
danielebarchiesi@0
|
494 'show return' => FALSE,
|
danielebarchiesi@0
|
495 'next text' => t('Continue'),
|
danielebarchiesi@0
|
496 'back text' => t('Back'),
|
danielebarchiesi@0
|
497 'return text' => t('Update and return'),
|
danielebarchiesi@0
|
498 'finish text' => t('Finish'),
|
danielebarchiesi@0
|
499 'cancel text' => t('Cancel'),
|
danielebarchiesi@0
|
500 );
|
danielebarchiesi@0
|
501
|
danielebarchiesi@0
|
502 if (!empty($form_info['free trail'])) {
|
danielebarchiesi@0
|
503 $defaults['next text'] = t('Update');
|
danielebarchiesi@0
|
504 $defaults['finish text'] = t('Save');
|
danielebarchiesi@0
|
505 }
|
danielebarchiesi@0
|
506
|
danielebarchiesi@0
|
507 $form_info = $form_info + $defaults;
|
danielebarchiesi@0
|
508 // set form callbacks if they aren't defined
|
danielebarchiesi@0
|
509 foreach ($form_info['forms'] as $step => $params) {
|
danielebarchiesi@0
|
510 if (!$params['form id']) {
|
danielebarchiesi@0
|
511 $form_callback = $hook . '_' . $step . '_form';
|
danielebarchiesi@0
|
512 $form_info['forms'][$step]['form id'] = $form_callback;
|
danielebarchiesi@0
|
513 }
|
danielebarchiesi@0
|
514 }
|
danielebarchiesi@0
|
515
|
danielebarchiesi@0
|
516 // set button callbacks
|
danielebarchiesi@0
|
517 $callbacks = array(
|
danielebarchiesi@0
|
518 'back callback' => '_back',
|
danielebarchiesi@0
|
519 'next callback' => '_next',
|
danielebarchiesi@0
|
520 'return callback' => '_return',
|
danielebarchiesi@0
|
521 'cancel callback' => '_cancel',
|
danielebarchiesi@0
|
522 'finish callback' => '_finish',
|
danielebarchiesi@0
|
523 );
|
danielebarchiesi@0
|
524
|
danielebarchiesi@0
|
525 foreach ($callbacks as $key => $callback) {
|
danielebarchiesi@0
|
526 // never overwrite if explicity defined
|
danielebarchiesi@0
|
527 if (empty($form_info[$key])) {
|
danielebarchiesi@0
|
528 $wizard_callback = $hook . $callback;
|
danielebarchiesi@0
|
529 if (function_exists($wizard_callback)) {
|
danielebarchiesi@0
|
530 $form_info[$key] = $wizard_callback;
|
danielebarchiesi@0
|
531 }
|
danielebarchiesi@0
|
532 }
|
danielebarchiesi@0
|
533 }
|
danielebarchiesi@0
|
534 }
|