1 |
<?php |
2 |
/** |
3 |
* This file contains the FormWizard class. |
4 |
* |
5 |
* $Id: FormWizard.inc,v 1.5.2.1 2005/05/12 01:24:02 hemna Exp $ |
6 |
* |
7 |
* @author Walter A. Boring IV <waboring@newsblob.com> |
8 |
* @package phpHtmlLib |
9 |
* @subpackage FormProcessing |
10 |
* |
11 |
* @copyright LGPL - See LICENCE |
12 |
* @todo Finish this. It doesn't work now. |
13 |
* |
14 |
*/ |
15 |
|
16 |
define("WIZARD_VISITED", "_wizard_visited"); |
17 |
define("WIZARD_STEP", "_wizard_step"); |
18 |
define("WIZARD_TO_STEP", "_wizard_to_step"); |
19 |
define("WIZARD_ID", "_wizard_id"); |
20 |
define("WIZARD_ACTION", "_wizard_action"); |
21 |
|
22 |
define("WIZARD_NEXT", "NEXT"); |
23 |
define("WIZARD_PREV", "PREV"); |
24 |
define("WIZARD_JUMP", "JUMP"); |
25 |
define("WIZARD_FINAL", "FINAL"); |
26 |
|
27 |
|
28 |
/** |
29 |
* This is a magic container that allows you |
30 |
* to chain together multiple FormContent objects |
31 |
* to automatically create a Wizard process. |
32 |
* |
33 |
* @package phpHtmlLib |
34 |
* @subpackage FormProcessing |
35 |
* |
36 |
* @todo Finish this. It doesn't work now. |
37 |
*/ |
38 |
class FormWizard extends FormProcessor { |
39 |
|
40 |
/** |
41 |
* This holds the array of |
42 |
* step objects for the |
43 |
* wizard |
44 |
*/ |
45 |
var $_steps = array(); |
46 |
|
47 |
|
48 |
/** |
49 |
* Holds a bunch of state |
50 |
* variables |
51 |
*/ |
52 |
var $_vars = array("to_step" => 0, |
53 |
"num_steps" => 0, |
54 |
"on_confirm" => FALSE, |
55 |
WIZARD_ID => NULL ); |
56 |
|
57 |
/** |
58 |
* The constructor |
59 |
* |
60 |
*/ |
61 |
function FormWizard() { |
62 |
//user_error(__CLASS__."::".__FUNCTION__." - this class isn't done yet."); |
63 |
$this->_session_test(); |
64 |
$this->set_form_name( "form_wizard" ); |
65 |
$this->set_form_action( $_SERVER["PHP_SELF"] ); |
66 |
|
67 |
//let the child class add the steps |
68 |
$this->user_setup(); |
69 |
|
70 |
//init our vars |
71 |
$this->_init(); |
72 |
|
73 |
//Set up the Validation object |
74 |
//and the FormErrors object to |
75 |
//be used by this form. |
76 |
$this->setup_validation(); |
77 |
|
78 |
//set the current step |
79 |
$this->_set_step(); |
80 |
|
81 |
//now process the form |
82 |
$this->_process_form(); |
83 |
|
84 |
//now we need to process the current step. |
85 |
} |
86 |
|
87 |
|
88 |
/** |
89 |
* This function renders the |
90 |
* FormWizard |
91 |
* |
92 |
*/ |
93 |
function render($indent_level=0, $output_debug=0) { |
94 |
$container = container(); |
95 |
|
96 |
//add the JS |
97 |
$container->add( $this->_build_js() ); |
98 |
|
99 |
$container->add( $this->_build_toolbar(), |
100 |
html_br(2) ); |
101 |
|
102 |
if ($this->_has_errors) { |
103 |
//we need to render the form errors |
104 |
//and then the form |
105 |
$container->add( $this->render_error($indent_level, $output_debug) ); |
106 |
} else { |
107 |
//there are no errors! |
108 |
debug( "have we visited?" ,null,4); |
109 |
//debug( $_REQUEST ); |
110 |
if (isset($_REQUEST[FORM_VISITED]) && $_REQUEST[FORM_VISITED] == 1) { |
111 |
//looks like the form has been processed? |
112 |
debug( "confirmed = ".$this->_confirmed ); |
113 |
debug( "curr step = ".$this->_current_step() ); |
114 |
if (!$this->_confirmed && $this->_vars["on_confirm"]) { |
115 |
debug("FINAL DUDE!"); |
116 |
$container->add( $this->render_confirm($indent_level, $output_debug) ); |
117 |
} else { |
118 |
//Looks like the action worked |
119 |
$success = $this->_form_content->form_success(); |
120 |
debug("DO ACTION!"); |
121 |
if ($this->_form_success_render) { |
122 |
$container->add( $this->render_form($indent_level, $output_debug, $success) ); |
123 |
} else { |
124 |
if (method_exists($success,"render")) { |
125 |
//looks like this is an object. |
126 |
//we'll assume it has a render function. |
127 |
$container->add( $success->render($indent_level, $output_debug) ); |
128 |
} else { |
129 |
//since its not an object, |
130 |
//lets just display it. |
131 |
$container->add( $success ); |
132 |
} |
133 |
} |
134 |
} |
135 |
} else { |
136 |
$container->add( $this->render_form($indent_level, $output_debug) ); |
137 |
} |
138 |
} |
139 |
|
140 |
return $container->render($indent_level, $output_debug); |
141 |
} |
142 |
|
143 |
|
144 |
/** |
145 |
* This method does the logic of |
146 |
* doing the form processing |
147 |
*/ |
148 |
function _process_form() { |
149 |
//Has this step been visited yet? |
150 |
|
151 |
if (!$this->_is_step_visited($this->_current_step()) && |
152 |
$this->_current_step() <= $this->_vars["num_steps"]) { |
153 |
$this->_form_content->form_init_data(); |
154 |
} |
155 |
|
156 |
//we only need to process the form |
157 |
//if it has been visited. Otherwise |
158 |
//it just gets rendered. |
159 |
if (@$_REQUEST[FORM_VISITED] == 1) { |
160 |
debug("PROCESS STEP ".$this->_current_step()); |
161 |
$this->_form_content->set_action($_REQUEST[FORM_ACTION]); |
162 |
|
163 |
//let see if this was a confirmation page. |
164 |
if ( @$_REQUEST[WIZARD_ACTION] == WIZARD_FINAL ) { |
165 |
//looks like this was a submit on a |
166 |
//confirmation page. we don't need |
167 |
//to do form field validation. |
168 |
$this->_confirmed = TRUE; |
169 |
for ($i=0; $i<=$this->_vars["num_steps"]-1; $i++) { |
170 |
$this->_has_errors = !$this->_steps[$i]["form"]->form_action(); |
171 |
} |
172 |
} else { |
173 |
//we haven't been confirmed, so we |
174 |
//need to validate the form. |
175 |
if ($this->can_validate()) { |
176 |
//looks like we should do validation |
177 |
if ($_REQUEST[WIZARD_ACTION] != WIZARD_PREV) { |
178 |
//we don't need to validate if we haven't |
179 |
//finished this step yet. |
180 |
$this->do_validation(); |
181 |
} |
182 |
} |
183 |
if (!$this->_has_errors) { |
184 |
//no errors were found |
185 |
//make sure we don't have any backend errors |
186 |
$this->_has_errors = !$this->_form_content->form_backend_validation(); |
187 |
if (!$this->_has_errors) { |
188 |
//ok this step validated. lets display the next. |
189 |
//mark this step as visited |
190 |
$current_step = $this->_current_step(); |
191 |
debug( "CURRENT STEP ".$current_step); |
192 |
|
193 |
switch ($_REQUEST[WIZARD_ACTION]) { |
194 |
case WIZARD_NEXT: |
195 |
debug("NEXT"); |
196 |
if ($current_step == $this->_vars["num_steps"]) { |
197 |
debug("SHOW CONFIRMATION"); |
198 |
//we need to show the confirmation |
199 |
//don't process |
200 |
$this->_vars["on_confirm"] = TRUE; |
201 |
$this->_step_visited( $current_step ); |
202 |
$this->_set_current_step($current_step+1); |
203 |
} else { |
204 |
$this->_step_visited( $current_step ); |
205 |
$this->_set_current_step($current_step+1); |
206 |
unset($_REQUEST[FORM_VISITED]); |
207 |
$this->_form_content = &$this->_steps[$this->_current_step()-1]["form"]; |
208 |
//$this->_process_form(); |
209 |
} |
210 |
break; |
211 |
|
212 |
case WIZARD_PREV: |
213 |
debug("PREV"); |
214 |
$this->_set_current_step($current_step-1); |
215 |
unset($_REQUEST[FORM_VISITED]); |
216 |
$this->_form_content = &$this->_steps[$this->_current_step()-1]["form"]; |
217 |
//$this->_process_form(); |
218 |
break; |
219 |
} |
220 |
} |
221 |
} |
222 |
} |
223 |
} |
224 |
debug("bail"); |
225 |
} |
226 |
|
227 |
|
228 |
/** |
229 |
* This function renders the confirmation |
230 |
* page. This page sits in between the |
231 |
* front end form, and the action handler. |
232 |
* This only gets called after a form |
233 |
* and its data has been successfully |
234 |
* validated. |
235 |
* |
236 |
* @param int - $indent_level |
237 |
* @param int - $output_debug |
238 |
* |
239 |
* @return string - the raw html |
240 |
*/ |
241 |
function render_confirm( $indent_level, $output_debug ) { |
242 |
debug("BUILD CONFIRM"); |
243 |
//build the $this->_form object. |
244 |
$this->_build_form_tag(); |
245 |
|
246 |
//add the confirm object/html |
247 |
for($i=0; $i<=$this->_vars["num_steps"]-1; $i++) { |
248 |
$title = "Step ".($i+1)." : ".$this->_steps[$i]["title"]; |
249 |
$this->_form->add( $this->_steps[$i]["form"]->form_confirm($title, FALSE), |
250 |
html_br(2)); |
251 |
} |
252 |
//$confirm = &$this->_form_content->form_confirm( $this->data ); |
253 |
//$this->_form->add_reference( $confirm ); |
254 |
|
255 |
//ok add all of the submitted data as hidden form fields |
256 |
$this->_add_confirm_data(); |
257 |
|
258 |
//Ok lets add our hidden vars |
259 |
$this->__hidden_fields(); |
260 |
|
261 |
return $this->_form->render($indent_level, $output_debug); |
262 |
} |
263 |
|
264 |
/** |
265 |
* A subclass can override this function |
266 |
* to setup the class variables after |
267 |
* the constructor. The constructor |
268 |
* automatically calls this function. |
269 |
* |
270 |
*/ |
271 |
function user_setup() { |
272 |
trigger_error("FormWizard::user_setup() - ". |
273 |
"child class must override this method ". |
274 |
"to add the wizard steps"); |
275 |
} |
276 |
|
277 |
|
278 |
/** |
279 |
* This adds a step to the wizard |
280 |
* |
281 |
* @param string - the title for the step |
282 |
* @param string - the description for the step |
283 |
* @param string - the help url for the step (if any) |
284 |
* @param FormContent - the form content object |
285 |
* that is the step. |
286 |
*/ |
287 |
function add_step( $title, $desc, $help, &$step ) { |
288 |
$this->_steps[] = array("title" => $title, |
289 |
"desc" => $desc, |
290 |
"help" => $help, |
291 |
"form" => &$step); |
292 |
$this->_vars["num_steps"]++; |
293 |
} |
294 |
|
295 |
|
296 |
/** |
297 |
* This function initializes all of the fields we |
298 |
* need to keep track of for the internal state |
299 |
* of the wizard. It also walks each of the |
300 |
* step FormContent objects and initializes them. |
301 |
* |
302 |
* We save some of the state of the wizard in |
303 |
* the session. |
304 |
*/ |
305 |
function _init() { |
306 |
if (!isset($_REQUEST[WIZARD_VISITED])) { |
307 |
|
308 |
$this->_vars[WIZARD_ID] = uniqid("wizard_"); |
309 |
//debug( $this->_vars ); |
310 |
|
311 |
$this->_init_session(); |
312 |
$this->_vars["to_step"] = 2; |
313 |
} else { |
314 |
$this->_vars[WIZARD_ID] = $_REQUEST[WIZARD_ID]; |
315 |
$current_step = $this->_current_step(); |
316 |
$this->_vars["to_step"] = $current_step++; |
317 |
|
318 |
//if ($this->_vars["current_step"] == count($this->_steps)) { |
319 |
// $this->_vars["to_step"] = WIZARD_FINAL; |
320 |
//} else { |
321 |
// $this->_vars["to_step"] = WIZARD_NEXT; |
322 |
//} |
323 |
} |
324 |
//debug( $_SESSION ); |
325 |
|
326 |
//initialize all of the Forms |
327 |
//so they retain their data. |
328 |
for ($i=0; $i<=$this->_vars["num_steps"]-1; $i++) { |
329 |
$this->_steps[$i]["form"]->form_init_elements(); |
330 |
} |
331 |
|
332 |
} |
333 |
|
334 |
/** |
335 |
* This function sets the _form_content |
336 |
* object for the current step we are operating on. |
337 |
* The parent FormProcessor needs this object set |
338 |
* in order to process the step correctly. |
339 |
* |
340 |
*/ |
341 |
function _set_step() { |
342 |
$this->_form_content = &$this->_steps[$this->_current_step()-1]["form"]; |
343 |
} |
344 |
|
345 |
function __hidden_fields() { |
346 |
|
347 |
//add all of the other steps fields |
348 |
//we need to add all the previous visted steps |
349 |
//fields so we save em. |
350 |
$step_count = 1; |
351 |
foreach( $this->_steps as $step ) { |
352 |
if ($step_count != $this->_current_step()) { |
353 |
$this->_form_content = &$step["form"]; |
354 |
$this->_add_confirm_data(); |
355 |
} |
356 |
$step_count++; |
357 |
} |
358 |
|
359 |
if (isset($_REQUEST[WIZARD_ACTION]) && |
360 |
$_REQUEST[WIZARD_ACTION] == WIZARD_FINAL) { |
361 |
$this->_set_step(); |
362 |
} |
363 |
|
364 |
parent::__hidden_fields(); |
365 |
|
366 |
//$this->_form->add( form_hidden(WIZARD_STEP, $this->_vars["current_step"]) ); |
367 |
$this->_form->add( form_hidden(WIZARD_TO_STEP, $this->_vars["to_step"]) ); |
368 |
$this->_form->add( form_hidden(WIZARD_ID, $this->_vars[WIZARD_ID]) ); |
369 |
$this->_form->add( form_hidden(WIZARD_ACTION, WIZARD_NEXT) ); |
370 |
$this->_form->add( form_hidden(WIZARD_VISITED, 1) ); |
371 |
} |
372 |
|
373 |
|
374 |
/** |
375 |
* This builds the javascript needed for the |
376 |
* navigation of the wizard |
377 |
* |
378 |
* @return string |
379 |
*/ |
380 |
function _build_js() { |
381 |
$name = $this->_form_attributes["name"]; |
382 |
$js = " |
383 |
function wizard_cancel_confirm(butname, txt) { |
384 |
if (confirm(txt)){ |
385 |
document.$name.".WIZARD_ACTION.".value = butname; |
386 |
document.$name.submit(); |
387 |
} |
388 |
} |
389 |
function wizard_submit(txt) { |
390 |
document.$name.".WIZARD_ACTION.".value = txt; |
391 |
document.$name.submit(); |
392 |
} |
393 |
function wizard_submit2(txt, step) { |
394 |
document.$name.".WIZARD_ACTION.".value = txt; |
395 |
document.$name.".WIZARD_TO_STEP.".value = step; |
396 |
document.$name.submit(); |
397 |
}"; |
398 |
|
399 |
$script = html_script(); |
400 |
$script->add( $js ); |
401 |
return $script; |
402 |
} |
403 |
|
404 |
|
405 |
|
406 |
/** |
407 |
* This renders the toolbar/step table |
408 |
* for the navigation of the wizard |
409 |
*/ |
410 |
function _build_toolbar() { |
411 |
$current_step = $this->_current_step(); |
412 |
xxx("current step ".$current_step); |
413 |
if ($this->_vars["on_confirm"]) { |
414 |
$current_step++; |
415 |
$step_title = "Confirmation"; |
416 |
} else { |
417 |
$step_title = $this->_steps[$current_step - 1]["title"]; |
418 |
} |
419 |
|
420 |
$title = "Step ".$current_step." : ". $step_title; |
421 |
$table = new InfoTable($title, 500); |
422 |
|
423 |
//build the table of steps. |
424 |
$c = container(); |
425 |
$step_num = 1; |
426 |
foreach( $this->_steps as $step ) { |
427 |
$c->add( $this->_build_step_image( $step_num, $step["title"] ) ); |
428 |
|
429 |
if ($step_num != $this->_vars["num_steps"]) { |
430 |
$arrow = html_img("/phphtmllib/images/wizard/arrow.png"); |
431 |
$arrow->set_style("vertical-align:super"); |
432 |
$c->add( $arrow ); |
433 |
} |
434 |
|
435 |
$step_num++; |
436 |
} |
437 |
|
438 |
//add the confirmation step |
439 |
xxx($step_num); |
440 |
$c->add( $arrow ); |
441 |
$c->add( $this->_build_step_image( $step_num, "Confirmation" ) ); |
442 |
|
443 |
|
444 |
$tr = html_tr("", $c ); |
445 |
$tr->set_style("text-align: center"); |
446 |
$table->add_row( $tr ); |
447 |
|
448 |
//now show the description |
449 |
if ($this->_vars["on_confirm"] == true) { |
450 |
$desc = "Confirmation"; |
451 |
} else { |
452 |
$desc = $this->_steps[$current_step - 1]["desc"]; |
453 |
} |
454 |
$tr = html_tr("", $desc ); |
455 |
$tr->set_style("text-align: center"); |
456 |
$table->add_row( $tr ); |
457 |
|
458 |
//now add the controls |
459 |
$c = html_div(); |
460 |
$c->set_style("padding-top: 5px;"); |
461 |
|
462 |
if ($current_step != 1) { |
463 |
$link = html_a("javascript:wizard_submit2('".WIZARD_PREV."',".($current_step-1).");", |
464 |
html_img("/phphtmllib/images/wizard/previous_step.png")); |
465 |
$c->add( $link ); |
466 |
} |
467 |
|
468 |
if ($current_step == $this->_vars["num_steps"]+1) { |
469 |
$link = html_a("javascript:wizard_submit2('".WIZARD_FINAL."',".($current_step).");", |
470 |
html_img("/phphtmllib/images/wizard/finish_steps.png") ); |
471 |
|
472 |
$c->add( _HTML_SPACE, _HTML_SPACE, $link); |
473 |
} else { |
474 |
$link = html_a("javascript:wizard_submit2('".WIZARD_NEXT."',".($current_step+1).");", |
475 |
html_img("/phphtmllib/images/wizard/next_step.png") ); |
476 |
$c->add( $link ); |
477 |
} |
478 |
|
479 |
|
480 |
$tr = html_tr("", $c ); |
481 |
$tr->set_style("text-align: center;"); |
482 |
$table->add_row( $tr ); |
483 |
|
484 |
return $table; |
485 |
} |
486 |
|
487 |
|
488 |
/** |
489 |
* This function builds an image for a step # |
490 |
* |
491 |
* @param string - the step # to build |
492 |
* @return Atag object |
493 |
*/ |
494 |
function &_build_step_image( $step_num, $step_title ) { |
495 |
$title = "Step ".$step_num." "; |
496 |
|
497 |
$current_step = $this->_current_step(); |
498 |
//if ($this->_vars["on_confirm"]) { |
499 |
//$current_step++; |
500 |
//} |
501 |
|
502 |
if ($step_num == $current_step) { |
503 |
$title .= " (Current) : ".$step_title; |
504 |
$img = html_img("/phphtmllib/images/wizard/".$step_num."_red.png", 30, 30, 0, |
505 |
$title, NULL, $title); |
506 |
|
507 |
} else if ($this->_is_step_visited($step_num) || |
508 |
$current_step == $this->_vars["num_steps"]+1) { |
509 |
$title .= " (Completed) : ".$step_title; |
510 |
$img = html_img("/phphtmllib/images/wizard/".$step_num."_black.png", 30, 30 ,0, |
511 |
$title, NULL, $title); |
512 |
|
513 |
} else { |
514 |
$title .= " (Not Completed) : ".$step_title; |
515 |
$img = html_img("/phphtmllib/images/wizard/".$step_num."_gray.png", 30,30,0, |
516 |
$title, NULL, $title); |
517 |
} |
518 |
|
519 |
return $img; |
520 |
} |
521 |
|
522 |
|
523 |
|
524 |
/*****************************************/ |
525 |
/* SESSION RELATED METHODS */ |
526 |
/*****************************************/ |
527 |
|
528 |
/** |
529 |
* This method initializes the session |
530 |
* variable that we use |
531 |
* |
532 |
*/ |
533 |
function _init_session() { |
534 |
//create a unique id for this wizard |
535 |
//so we can have multple wizards |
536 |
//running per session. |
537 |
$_SESSION[$this->_vars[WIZARD_ID]] = array(); |
538 |
$_SESSION[$this->_vars[WIZARD_ID]]; |
539 |
$this->_set_current_step(1); |
540 |
|
541 |
//mark all steps as NOT visited |
542 |
for ($i=0; $i<=$this->_vars["num_steps"]-1; $i++) { |
543 |
$this->_step_visited($i, FALSE); |
544 |
} |
545 |
} |
546 |
|
547 |
/** |
548 |
* This returns the current step id |
549 |
* from the session |
550 |
* |
551 |
*/ |
552 |
function _current_step() { |
553 |
return $_SESSION[$this->_vars[WIZARD_ID]]["current_step"]; |
554 |
} |
555 |
|
556 |
/** |
557 |
* This sets the current step id |
558 |
* |
559 |
* @param int - the new step # |
560 |
*/ |
561 |
function _set_current_step($step) { |
562 |
debug( "SET STEP ".$step); |
563 |
$_SESSION[$this->_vars[WIZARD_ID]]["current_step"] = $step; |
564 |
} |
565 |
|
566 |
|
567 |
/** |
568 |
* This sets the state variable for the |
569 |
* step to let us know it has been visited or not |
570 |
* |
571 |
* @param int - the step to mark |
572 |
* @param boolean - TRUE = visited |
573 |
*/ |
574 |
function _step_visited($step_num, $visited=TRUE) { |
575 |
$_SESSION[$this->_vars[WIZARD_ID]]["visited_steps"][$step_num] = $visited; |
576 |
} |
577 |
|
578 |
/** |
579 |
* This tests to see if the step has been visited or not. |
580 |
* |
581 |
* @param int - the step to mark |
582 |
* @return boolean - TRUE = visited |
583 |
*/ |
584 |
function _is_step_visited($step_num) { |
585 |
if (isset($_SESSION[$this->_vars[WIZARD_ID]]["visited_steps"][$step_num])) { |
586 |
return $_SESSION[$this->_vars[WIZARD_ID]]["visited_steps"][$step_num]; |
587 |
} else { |
588 |
return FALSE; |
589 |
} |
590 |
} |
591 |
|
592 |
/** |
593 |
* This function cleans up the saved Session state |
594 |
* for the wizard. This gets called when we have |
595 |
* completed the wizard w/o errors. |
596 |
* |
597 |
*/ |
598 |
function _clean() { |
599 |
unset($_SESSION[$this->_vars[WIZARD_ID]]); |
600 |
} |
601 |
|
602 |
/** |
603 |
* This ensures that we have sessions started |
604 |
* |
605 |
*/ |
606 |
function _session_test() { |
607 |
if (!session_id()) { |
608 |
//we have to have sessions started. |
609 |
session_start(); |
610 |
} |
611 |
} |
612 |
} |
613 |
?> |