TWizard::applyNavigationProperties()   F
last analyzed

Complexity

Conditions 30
Paths > 20000

Size

Total Lines 109
Code Lines 75

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 930

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 30
eloc 75
c 1
b 0
f 0
nc 288002
nop 0
dl 0
loc 109
ccs 0
cts 97
cp 0
crap 930
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TWizard and the relevant class definitions.
5
 *
6
 * @author Qiang Xue <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
namespace Prado\Web\UI\WebControls;
12
13
use Prado\Collections\TStack;
14
use Prado\Exceptions\TConfigurationException;
15
use Prado\Exceptions\TInvalidOperationException;
16
use Prado\TPropertyValue;
17
use Prado\Exceptions\TInvalidDataValueException;
18
19
/**
20
 * Class TWizard.
21
 *
22
 * TWizard splits a large form and presents the user with a series of smaller
23
 * forms to complete. TWizard is analogous to the installation wizard commonly
24
 * used to install software in Windows.
25
 *
26
 * The smaller forms are called wizard steps ({@see \Prado\Web\UI\WebControls\TWizardStep}, which can be accessed via
27
 * {@see getWizardSteps WizardSteps}. In template, wizard steps can be added
28
 * into a wizard using the following syntax,
29
 * ```php
30
 *   <com:TWizard>
31
 *      <com:TWizardStep Title="step 1">
32
 *          content in step 1, may contain other controls
33
 *      </com:TWizardStep>
34
 *      <com:TWizardStep Title="step 2">
35
 *          content in step 2, may contain other controls
36
 *      </com:TWizardStep>
37
 *   </com:TWizard>
38
 * ```
39
 *
40
 * Each wizard step can be one of the following types:
41
 * - Start : the first step in the wizard.
42
 * - Step : the internal steps in the wizard.
43
 * - Finish : the last step that allows user interaction.
44
 * - Complete : the step that shows a summary to user (no interaction is allowed).
45
 * - Auto : the step type is determined by wizard automatically.
46
 * At any time, only one step is visible to end-users, which can be obtained
47
 * by {@see getActiveStep ActiveStep}. Its index in the step collection is given by
48
 * {@see getActiveStepIndex ActiveStepIndex}.
49
 *
50
 * Wizard content can be customized in many ways.
51
 *
52
 * The layout of a wizard consists of four parts: header, step content, navigation
53
 * and side bar. Their content are affected by the following properties, respectively,
54
 * - header: {@see setHeaderText HeaderText} and {@see setHeaderTemplate HeaderTemplate}.
55
 *   If both are present, the latter takes precedence.
56
 * - step: {@see getWizardSteps WizardSteps}.
57
 * - navigation: {@see setStartNavigationTemplate StartNavigationTemplate},
58
 *   {@see setStepNavigationTemplate StepNavigationTemplate},
59
 *   {@see setFinishNavigationTemplate FinishNavigationTemplate}.
60
 *   Default templates will be used if above templates are not set.
61
 * - side bar: {@see setSideBarTemplate SideBarTemplate}.
62
 *   A default template will be used if this template is not set.
63
 *   Its visibility is toggled by {@see setShowSideBar ShowSideBar}.
64
 *
65
 * The style of these wizard layout components can be customized via the following style properties,
66
 * - header: {@see getHeaderStyle HeaderStyle}.
67
 * - step: {@see getStepStyle StepStyle}.
68
 * - navigation: {@see getNavigationStyle NavigationStyle},
69
 *   {@see getStartNextButtonStyle StartNextButtonStyle},
70
 *   {@see getStepNextButtonStyle StepNextButtonStyle},
71
 *   {@see getStepPreviousButtonStyle StepPreviousButtonStyle},
72
 *   {@see getFinishPreviousButtonStyle FinishPreviousButtonStyle},
73
 *   {@see getFinishCompleteButtonStyle FinishCompleteButtonStyle},
74
 *   {@see getCancelButtonStyle CancelButtonStyle}.
75
 * - side bar: {@see getSideBarStyle SideBarStyle} and {@see getSideBarButtonStyle SideBarButtonStyle}.
76
 *
77
 * @author Qiang Xue <[email protected]>
78
 * @since 3.0
79
 */
80
class TWizard extends \Prado\Web\UI\WebControls\TWebControl implements \Prado\Web\UI\INamingContainer
81
{
82
	/**
83
	 * Navigation commands.
84
	 */
85
	public const CMD_PREVIOUS = 'PreviousStep';
86
	public const CMD_NEXT = 'NextStep';
87
	public const CMD_CANCEL = 'Cancel';
88
	public const CMD_COMPLETE = 'Complete';
89
	public const CMD_MOVETO = 'MoveTo';
90
	/**
91
	 * Side bar button ID
92
	 */
93
	public const ID_SIDEBAR_BUTTON = 'SideBarButton';
94
	/**
95
	 * Side bar data list
96
	 */
97
	public const ID_SIDEBAR_LIST = 'SideBarList';
98
99
	/**
100
	 * @var TMultiView multiview that contains the wizard steps
101
	 */
102
	private $_multiView;
103
	/**
104
	 * @var mixed navigation template for the start step.
105
	 */
106
	private $_startNavigationTemplate;
107
	/**
108
	 * @var mixed navigation template for internal steps.
109
	 */
110
	private $_stepNavigationTemplate;
111
	/**
112
	 * @var mixed navigation template for the finish step.
113
	 */
114
	private $_finishNavigationTemplate;
115
	/**
116
	 * @var mixed template for wizard header.
117
	 */
118
	private $_headerTemplate;
119
	/**
120
	 * @var mixed template for the side bar.
121
	 */
122
	private $_sideBarTemplate;
123
	/**
124
	 * @var TWizardStepCollection
125
	 */
126
	private $_wizardSteps;
127
	/**
128
	 * @var null|TPanel container of the wizard header
129
	 */
130
	private $_header;
131
	/**
132
	 * @var null|TPanel container of the wizard step content
133
	 */
134
	private $_stepContent;
135
	/**
136
	 * @var null|TPanel container of the wizard side bar
137
	 */
138
	private $_sideBar;
139
	/**
140
	 * @var null|TPanel navigation panel
141
	 */
142
	private $_navigation;
143
	/**
144
	 * @var null|TWizardNavigationContainer container of the start navigation
145
	 */
146
	private $_startNavigation;
147
	/**
148
	 * @var null|TWizardNavigationContainer container of the step navigation
149
	 */
150
	private $_stepNavigation;
151
	/**
152
	 * @var null|TWizardNavigationContainer container of the finish navigation
153
	 */
154
	private $_finishNavigation;
155
	/**
156
	 * @var bool whether ActiveStepIndex was already set
157
	 */
158
	private $_activeStepIndexSet = false;
159
	/**
160
	 * @var null|TDataList side bar data list.
161
	 */
162
	private $_sideBarDataList;
163
	/**
164
	 * @var bool whether navigation should be cancelled (a status set in OnSideBarButtonClick)
165
	 */
166
	private $_cancelNavigation = false;
167
168
	/**
169
	 * @return string tag name for the wizard
170
	 */
171
	protected function getTagName()
172
	{
173
		return 'div';
174
	}
175
176
	/**
177
	 * Adds {@see \Prado\Web\UI\WebControls\TWizardStep} objects into step collection.
178
	 * This method overrides the parent implementation and is
179
	 * invoked when template is being instantiated.
180
	 * @param mixed $object object instantiated in template
181
	 */
182
	public function addParsedObject($object)
183
	{
184
		if ($object instanceof TWizardStep) {
185
			$this->getWizardSteps()->add($object);
186
		}
187
	}
188
189
	/**
190
	 * @return TWizardStep the currently active wizard step
191
	 */
192
	public function getActiveStep()
193
	{
194
		return $this->getMultiView()->getActiveView();
195
	}
196
197
	/**
198
	 * @param TWizardStep $step step to be activated
199
	 * @throws TInvalidOperationException if the step is not in the wizard step collection
200
	 */
201
	public function setActiveStep($step)
202
	{
203
		if (($index = $this->getWizardSteps()->indexOf($step)) < 0) {
204
			throw new TInvalidOperationException('wizard_step_invalid');
205
		}
206
		$this->setActiveStepIndex($index);
207
	}
208
209
	/**
210
	 * @return int the zero-based index of the active wizard step
211
	 */
212
	public function getActiveStepIndex()
213
	{
214
		return $this->getMultiView()->getActiveViewIndex();
215
	}
216
217
	/**
218
	 * @param int $value the zero-based index of the wizard step to be activated
219
	 */
220
	public function setActiveStepIndex($value)
221
	{
222
		$value = TPropertyValue::ensureInteger($value);
223
		$multiView = $this->getMultiView();
224
		if ($multiView->getActiveViewIndex() !== $value) {
225
			$multiView->setActiveViewIndex($value);
226
			$this->_activeStepIndexSet = true;
227
			if ($this->_sideBarDataList !== null && $this->getSideBarTemplate() !== null) {
228
				$this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
229
				$this->_sideBarDataList->dataBind();
230
			}
231
		}
232
	}
233
234
	/**
235
	 * @return TWizardStepCollection collection of wizard steps
236
	 */
237
	public function getWizardSteps()
238
	{
239
		if ($this->_wizardSteps === null) {
240
			$this->_wizardSteps = new TWizardStepCollection($this);
241
		}
242
		return $this->_wizardSteps;
243
	}
244
245
	/**
246
	 * @return bool whether to display a cancel button in each wizard step. Defaults to false.
247
	 */
248
	public function getShowCancelButton()
249
	{
250
		return $this->getViewState('ShowCancelButton', false);
251
	}
252
253
	/**
254
	 * @param bool $value whether to display a cancel button in each wizard step.
255
	 */
256
	public function setShowCancelButton($value)
257
	{
258
		$this->setViewState('ShowCancelButton', TPropertyValue::ensureBoolean($value), false);
259
	}
260
261
	/**
262
	 * @return bool whether to display a side bar that contains links to wizard steps. Defaults to true.
263
	 */
264
	public function getShowSideBar()
265
	{
266
		return $this->getViewState('ShowSideBar', true);
267
	}
268
269
	/**
270
	 * @param bool $value whether to display a side bar that contains links to wizard steps.
271
	 */
272
	public function setShowSideBar($value)
273
	{
274
		$this->setViewState('ShowSideBar', TPropertyValue::ensureBoolean($value), true);
275
	}
276
277
	/**
278
	 * @return \Prado\Web\UI\ITemplate navigation template for the start step. Defaults to null.
279
	 */
280
	public function getStartNavigationTemplate()
281
	{
282
		return $this->_startNavigationTemplate;
283
	}
284
285
	/**
286
	 * @param \Prado\Web\UI\ITemplate $value navigation template for the start step.
287
	 */
288
	public function setStartNavigationTemplate($value)
289
	{
290
		$this->_startNavigationTemplate = $value;
291
		$this->requiresControlsRecreation();
292
	}
293
294
	/**
295
	 * @return \Prado\Web\UI\ITemplate navigation template for internal steps. Defaults to null.
296
	 */
297
	public function getStepNavigationTemplate()
298
	{
299
		return $this->_stepNavigationTemplate;
300
	}
301
302
	/**
303
	 * @param \Prado\Web\UI\ITemplate $value navigation template for internal steps.
304
	 */
305
	public function setStepNavigationTemplate($value)
306
	{
307
		$this->_stepNavigationTemplate = $value;
308
		$this->requiresControlsRecreation();
309
	}
310
311
	/**
312
	 * @return \Prado\Web\UI\ITemplate navigation template for the finish step. Defaults to null.
313
	 */
314
	public function getFinishNavigationTemplate()
315
	{
316
		return $this->_finishNavigationTemplate;
317
	}
318
319
	/**
320
	 * @param \Prado\Web\UI\ITemplate $value navigation template for the finish step.
321
	 */
322
	public function setFinishNavigationTemplate($value)
323
	{
324
		$this->_finishNavigationTemplate = $value;
325
		$this->requiresControlsRecreation();
326
	}
327
328
	/**
329
	 * @return \Prado\Web\UI\ITemplate template for wizard header. Defaults to null.
330
	 */
331
	public function getHeaderTemplate()
332
	{
333
		return $this->_headerTemplate;
334
	}
335
336
	/**
337
	 * @param \Prado\Web\UI\ITemplate $value template for wizard header.
338
	 */
339
	public function setHeaderTemplate($value)
340
	{
341
		$this->_headerTemplate = $value;
342
		$this->requiresControlsRecreation();
343
	}
344
345
	/**
346
	 * @return \Prado\Web\UI\ITemplate template for the side bar. Defaults to null.
347
	 */
348
	public function getSideBarTemplate()
349
	{
350
		return $this->_sideBarTemplate;
351
	}
352
353
	/**
354
	 * @param \Prado\Web\UI\ITemplate $value template for the side bar.
355
	 */
356
	public function setSideBarTemplate($value)
357
	{
358
		$this->_sideBarTemplate = $value;
359
		$this->requiresControlsRecreation();
360
	}
361
362
	/**
363
	 * @return string header text. Defaults to ''.
364
	 */
365
	public function getHeaderText()
366
	{
367
		return $this->getViewState('HeaderText', '');
368
	}
369
370
	/**
371
	 * @param string $value header text.
372
	 */
373
	public function setHeaderText($value)
374
	{
375
		$this->setViewState('HeaderText', TPropertyValue::ensureString($value), '');
376
	}
377
378
	/**
379
	 * @return string the URL that the browser will be redirected to if the cancel button in the
380
	 * wizard is clicked. Defaults to ''.
381
	 */
382
	public function getCancelDestinationUrl()
383
	{
384
		return $this->getViewState('CancelDestinationUrl', '');
385
	}
386
387
	/**
388
	 * @param string $value the URL that the browser will be redirected to if the cancel button in the
389
	 * wizard is clicked.
390
	 */
391
	public function setCancelDestinationUrl($value)
392
	{
393
		$this->setViewState('CancelDestinationUrl', TPropertyValue::ensureString($value), '');
394
	}
395
396
	/**
397
	 * @return string the URL that the browser will be redirected to if the wizard finishes.
398
	 * Defaults to ''.
399
	 */
400
	public function getFinishDestinationUrl()
401
	{
402
		return $this->getViewState('FinishDestinationUrl', '');
403
	}
404
405
	/**
406
	 * @param string $value the URL that the browser will be redirected to if the wizard finishes.
407
	 */
408
	public function setFinishDestinationUrl($value)
409
	{
410
		$this->setViewState('FinishDestinationUrl', TPropertyValue::ensureString($value), '');
411
	}
412
413
	/**
414
	 * @return TStyle the style for the buttons displayed in the side bar.
415
	 */
416
	public function getSideBarButtonStyle()
417
	{
418
		if (($style = $this->getViewState('SideBarButtonStyle', null)) === null) {
419
			$style = new TStyle();
420
			$this->setViewState('SideBarButtonStyle', $style, null);
421
		}
422
		return $style;
423
	}
424
425
	/**
426
	 * @return TStyle the style common for all navigation buttons.
427
	 */
428
	public function getNavigationButtonStyle()
429
	{
430
		if (($style = $this->getViewState('NavigationButtonStyle', null)) === null) {
431
			$style = new TStyle();
432
			$this->setViewState('NavigationButtonStyle', $style, null);
433
		}
434
		return $style;
435
	}
436
437
	/**
438
	 * @return TWizardNavigationButtonStyle the style for the next button in the start wizard step.
439
	 */
440
	public function getStartNextButtonStyle()
441
	{
442
		if (($style = $this->getViewState('StartNextButtonStyle', null)) === null) {
443
			$style = new TWizardNavigationButtonStyle();
444
			$style->setButtonText('Next');
445
			$this->setViewState('StartNextButtonStyle', $style, null);
446
		}
447
		return $style;
448
	}
449
450
	/**
451
	 * @return TWizardNavigationButtonStyle the style for the next button in each internal wizard step.
452
	 */
453
	public function getStepNextButtonStyle()
454
	{
455
		if (($style = $this->getViewState('StepNextButtonStyle', null)) === null) {
456
			$style = new TWizardNavigationButtonStyle();
457
			$style->setButtonText('Next');
458
			$this->setViewState('StepNextButtonStyle', $style, null);
459
		}
460
		return $style;
461
	}
462
463
	/**
464
	 * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step.
465
	 */
466
	public function getStepPreviousButtonStyle()
467
	{
468
		if (($style = $this->getViewState('StepPreviousButtonStyle', null)) === null) {
469
			$style = new TWizardNavigationButtonStyle();
470
			$style->setButtonText('Previous');
471
			$this->setViewState('StepPreviousButtonStyle', $style, null);
472
		}
473
		return $style;
474
	}
475
476
	/**
477
	 * @return TWizardNavigationButtonStyle the style for the complete button in the finish wizard step.
478
	 */
479
	public function getFinishCompleteButtonStyle()
480
	{
481
		if (($style = $this->getViewState('FinishCompleteButtonStyle', null)) === null) {
482
			$style = new TWizardNavigationButtonStyle();
483
			$style->setButtonText('Complete');
484
			$this->setViewState('FinishCompleteButtonStyle', $style, null);
485
		}
486
		return $style;
487
	}
488
489
	/**
490
	 * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step.
491
	 */
492
	public function getFinishPreviousButtonStyle()
493
	{
494
		if (($style = $this->getViewState('FinishPreviousButtonStyle', null)) === null) {
495
			$style = new TWizardNavigationButtonStyle();
496
			$style->setButtonText('Previous');
497
			$this->setViewState('FinishPreviousButtonStyle', $style, null);
498
		}
499
		return $style;
500
	}
501
502
	/**
503
	 * @return TWizardNavigationButtonStyle the style for the cancel button
504
	 */
505
	public function getCancelButtonStyle()
506
	{
507
		if (($style = $this->getViewState('CancelButtonStyle', null)) === null) {
508
			$style = new TWizardNavigationButtonStyle();
509
			$style->setButtonText('Cancel');
510
			$this->setViewState('CancelButtonStyle', $style, null);
511
		}
512
		return $style;
513
	}
514
515
	/**
516
	 * @return TPanelStyle the style for the side bar.
517
	 */
518
	public function getSideBarStyle()
519
	{
520
		if (($style = $this->getViewState('SideBarStyle', null)) === null) {
521
			$style = new TPanelStyle();
522
			$this->setViewState('SideBarStyle', $style, null);
523
		}
524
		return $style;
525
	}
526
527
	/**
528
	 * @return TPanelStyle the style for the header.
529
	 */
530
	public function getHeaderStyle()
531
	{
532
		if (($style = $this->getViewState('HeaderStyle', null)) === null) {
533
			$style = new TPanelStyle();
534
			$this->setViewState('HeaderStyle', $style, null);
535
		}
536
		return $style;
537
	}
538
539
	/**
540
	 * @return TPanelStyle the style for each internal wizard step.
541
	 */
542
	public function getStepStyle()
543
	{
544
		if (($style = $this->getViewState('StepStyle', null)) === null) {
545
			$style = new TPanelStyle();
546
			$this->setViewState('StepStyle', $style, null);
547
		}
548
		return $style;
549
	}
550
551
	/**
552
	 * @return TPanelStyle the style for the navigation panel.
553
	 */
554
	public function getNavigationStyle()
555
	{
556
		if (($style = $this->getViewState('NavigationStyle', null)) === null) {
557
			$style = new TPanelStyle();
558
			$this->setViewState('NavigationStyle', $style, null);
559
		}
560
		return $style;
561
	}
562
563
	/**
564
	 * @return bool whether to use default layout to arrange side bar and the rest wizard components. Defaults to true.
565
	 */
566
	public function getUseDefaultLayout()
567
	{
568
		return $this->getViewState('UseDefaultLayout', true);
569
	}
570
571
	/**
572
	 * @param bool $value whether to use default layout to arrange side bar and the rest wizard components.
573
	 * If true, an HTML table will be used which places the side bar in the left cell
574
	 * while the rest components in the right cell.
575
	 */
576
	public function setUseDefaultLayout($value)
577
	{
578
		$this->setViewState('UseDefaultLayout', TPropertyValue::ensureBoolean($value), true);
579
	}
580
581
	/**
582
	 * @return TPanel container of the wizard header
583
	 */
584
	public function getHeader()
585
	{
586
		return $this->_header;
587
	}
588
589
	/**
590
	 * @return TPanel container of the wizard step content
591
	 */
592
	public function getStepContent()
593
	{
594
		return $this->_stepContent;
595
	}
596
597
	/**
598
	 * @return TPanel container of the wizard side bar
599
	 */
600
	public function getSideBar()
601
	{
602
		return $this->_sideBar;
603
	}
604
605
	/**
606
	 * @return TWizardNavigationContainer container of the start navigation
607
	 */
608
	public function getStartNavigation()
609
	{
610
		return $this->_startNavigation;
611
	}
612
613
	/**
614
	 * @return TWizardNavigationContainer container of the step navigation
615
	 */
616
	public function getStepNavigation()
617
	{
618
		return $this->_stepNavigation;
619
	}
620
621
	/**
622
	 * @return TWizardNavigationContainer container of the finish navigation
623
	 */
624
	public function getFinishNavigation()
625
	{
626
		return $this->_finishNavigation;
627
	}
628
629
	/**
630
	 * Raises <b>OnActiveStepChanged</b> event.
631
	 * This event is raised when the current visible step is changed in the
632
	 * wizard.
633
	 * @param \Prado\TEventParameter $param event parameter
634
	 */
635
	public function onActiveStepChanged($param)
636
	{
637
		$this->raiseEvent('OnActiveStepChanged', $this, $param);
638
	}
639
640
	/**
641
	 * Raises <b>OnCancelButtonClick</b> event.
642
	 * This event is raised when a cancel navigation button is clicked in the
643
	 * current active step.
644
	 * @param \Prado\TEventParameter $param event parameter
645
	 */
646
	public function onCancelButtonClick($param)
647
	{
648
		$this->raiseEvent('OnCancelButtonClick', $this, $param);
649
		if (($url = $this->getCancelDestinationUrl()) !== '') {
650
			$this->getResponse()->redirect($url);
651
		}
652
	}
653
654
	/**
655
	 * Raises <b>OnCompleteButtonClick</b> event.
656
	 * This event is raised when a finish navigation button is clicked in the
657
	 * current active step.
658
	 * @param TWizardNavigationEventParameter $param event parameter
659
	 */
660
	public function onCompleteButtonClick($param)
661
	{
662
		$this->raiseEvent('OnCompleteButtonClick', $this, $param);
663
		if (($url = $this->getFinishDestinationUrl()) !== '') {
664
			$this->getResponse()->redirect($url);
665
		}
666
	}
667
668
	/**
669
	 * Raises <b>OnNextButtonClick</b> event.
670
	 * This event is raised when a next navigation button is clicked in the
671
	 * current active step.
672
	 * @param TWizardNavigationEventParameter $param event parameter
673
	 */
674
	public function onNextButtonClick($param)
675
	{
676
		$this->raiseEvent('OnNextButtonClick', $this, $param);
677
	}
678
679
	/**
680
	 * Raises <b>OnPreviousButtonClick</b> event.
681
	 * This event is raised when a previous navigation button is clicked in the
682
	 * current active step.
683
	 * @param TWizardNavigationEventParameter $param event parameter
684
	 */
685
	public function onPreviousButtonClick($param)
686
	{
687
		$this->raiseEvent('OnPreviousButtonClick', $this, $param);
688
	}
689
690
	/**
691
	 * Raises <b>OnSideBarButtonClick</b> event.
692
	 * This event is raised when a link button in the side bar is clicked.
693
	 * @param TWizardNavigationEventParameter $param event parameter
694
	 */
695
	public function onSideBarButtonClick($param)
696
	{
697
		$this->raiseEvent('OnSideBarButtonClick', $this, $param);
698
	}
699
700
	/**
701
	 * Returns the multiview that holds the wizard steps.
702
	 * This method should only be used by control developers.
703
	 * @return TMultiView the multiview holding wizard steps
704
	 */
705
	public function getMultiView()
706
	{
707
		if ($this->_multiView === null) {
708
			$this->_multiView = new TMultiView();
709
			$this->_multiView->setID('WizardMultiView');
710
			$this->_multiView->attachEventHandler('OnActiveViewChanged', [$this, 'onActiveStepChanged']);
711
			$this->_multiView->ignoreBubbleEvents();
712
		}
713
		return $this->_multiView;
714
	}
715
716
	/**
717
	 * Adds a wizard step to the multiview.
718
	 * This method should only be used by control developers.
719
	 * It is invoked when a step is added into the step collection of the wizard.
720
	 * @param TWizardStep $step wizard step to be added into multiview.
721
	 */
722
	public function addedWizardStep($step)
723
	{
724
		if (($wizard = $step->getWizard()) !== null) {
725
			$wizard->getWizardSteps()->remove($step);
726
		}
727
		$step->setWizard($this);
728
		$this->wizardStepsChanged();
729
	}
730
731
	/**
732
	 * Removes a wizard step from the multiview.
733
	 * This method should only be used by control developers.
734
	 * It is invoked when a step is removed from the step collection of the wizard.
735
	 * @param TWizardStep $step wizard step to be removed from multiview.
736
	 */
737
	public function removedWizardStep($step)
738
	{
739
		$step->setWizard(null);
740
		$this->wizardStepsChanged();
741
	}
742
743
	/**
744
	 * Creates the child controls of the wizard.
745
	 * This method overrides the parent implementation.
746
	 * @param \Prado\TEventParameter $param event parameter
747
	 */
748
	public function onInit($param)
749
	{
750
		parent::onInit($param);
751
		$this->ensureChildControls();
752
		$this->setEnsureId(true);
753
		if ($this->getActiveStepIndex() < 0 && $this->getWizardSteps()->getCount() > 0) {
754
			$this->setActiveStepIndex(0);
755
		}
756
	}
757
758
	/**
759
	 * Saves the current active step index into history.
760
	 * This method is invoked by the framework when the control state is being saved.
761
	 */
762
	public function saveState()
763
	{
764
		$index = $this->getActiveStepIndex();
765
		$history = $this->getHistory();
766
		if (!$history->getCount() || $history->peek() !== $index) {
767
			$history->push($index);
768
		}
769
	}
770
771
	/**
772
	 * Indicates the wizard needs to recreate all child controls.
773
	 */
774
	protected function requiresControlsRecreation()
775
	{
776
		if ($this->getChildControlsCreated()) {
777
			$this->setChildControlsCreated(false);
778
		}
779
	}
780
781
	/**
782
	 * Renders the wizard.
783
	 * @param \Prado\Web\UI\THtmlWriter $writer
784
	 */
785
	public function render($writer)
786
	{
787
		$this->ensureChildControls();
788
		if ($this->getHasControls()) {
789
			if ($this->getUseDefaultLayout()) {
790
				$this->applyControlProperties();
791
				$this->renderBeginTag($writer);
792
				$writer->write("\n<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" height=\"100%\" width=\"100%\">\n<tr><td width=\"1\" valign=\"top\">\n");
793
				$this->_sideBar->renderControl($writer);
0 ignored issues
show
Bug introduced by
The method renderControl() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

793
				$this->_sideBar->/** @scrutinizer ignore-call */ 
794
                     renderControl($writer);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
794
				$writer->write("\n</td><td valign=\"top\">\n");
795
				$this->_header->renderControl($writer);
0 ignored issues
show
Bug introduced by
The method renderControl() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

795
				$this->_header->/** @scrutinizer ignore-call */ 
796
                    renderControl($writer);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
796
				$this->_stepContent->renderControl($writer);
0 ignored issues
show
Bug introduced by
The method renderControl() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

796
				$this->_stepContent->/** @scrutinizer ignore-call */ 
797
                         renderControl($writer);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
797
				$this->_navigation->renderControl($writer);
0 ignored issues
show
Bug introduced by
The method renderControl() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

797
				$this->_navigation->/** @scrutinizer ignore-call */ 
798
                        renderControl($writer);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
798
				$writer->write("\n</td></tr></table>\n");
799
				$this->renderEndTag($writer);
800
			} else {
801
				$this->applyControlProperties();
802
				$this->renderBeginTag($writer);
803
				$this->_sideBar->renderControl($writer);
804
				$this->_header->renderControl($writer);
805
				$this->_stepContent->renderControl($writer);
806
				$this->_navigation->renderControl($writer);
807
				$this->renderEndTag($writer);
808
			}
809
		}
810
	}
811
812
	/**
813
	 * Applies various properties to the components of wizard
814
	 */
815
	protected function applyControlProperties()
816
	{
817
		$this->applyHeaderProperties();
818
		$this->applySideBarProperties();
819
		$this->applyStepContentProperties();
820
		$this->applyNavigationProperties();
821
	}
822
823
	/**
824
	 * Applies properties to the wizard header
825
	 */
826
	protected function applyHeaderProperties()
827
	{
828
		if (($style = $this->getViewState('HeaderStyle', null)) !== null) {
829
			$this->_header->getStyle()->mergeWith($style);
830
		}
831
		if ($this->getHeaderTemplate() === null) {
832
			$this->_header->getControls()->clear();
833
			$this->_header->getControls()->add($this->getHeaderText());
834
		}
835
	}
836
837
	/**
838
	 * Applies properties to the wizard sidebar
839
	 */
840
	protected function applySideBarProperties()
841
	{
842
		$this->_sideBar->setVisible($this->getShowSideBar());
843
		if ($this->_sideBarDataList !== null && $this->getShowSideBar()) {
844
			$this->_sideBarDataList->setDataSource($this->getWizardSteps());
845
			$this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
846
			$this->_sideBarDataList->dataBind();
847
			if (($style = $this->getViewState('SideBarButtonStyle', null)) !== null) {
848
				foreach ($this->_sideBarDataList->getItems() as $item) {
849
					if (($button = $item->findControl('SideBarButton')) !== null) {
850
						$button->getStyle()->mergeWith($style);
851
					}
852
				}
853
			}
854
		}
855
		if (($style = $this->getViewState('SideBarStyle', null)) !== null) {
856
			$this->_sideBar->getStyle()->mergeWith($style);
857
		}
858
	}
859
860
	/**
861
	 * Applies properties to the wizard step content
862
	 */
863
	protected function applyStepContentProperties()
864
	{
865
		if (($style = $this->getViewState('StepStyle', null)) !== null) {
866
			$this->_stepContent->getStyle()->mergeWith($style);
867
		}
868
	}
869
870
	/**
871
	 * Apply properties to various navigation panels.
872
	 */
873
	protected function applyNavigationProperties()
874
	{
875
		$wizardSteps = $this->getWizardSteps();
876
		$activeStep = $this->getActiveStep();
877
		$activeStepIndex = $this->getActiveStepIndex();
878
879
		if (!$this->_navigation) {
880
			return;
881
		} elseif ($activeStepIndex < 0 || $activeStepIndex >= $wizardSteps->getCount()) {
882
			$this->_navigation->setVisible(false);
883
			return;
884
		}
885
886
		// set visibility of different types of navigation panel
887
		$showStandard = true;
888
		foreach ($wizardSteps as $step) {
889
			if (($step instanceof TTemplatedWizardStep) && ($container = $step->getNavigationContainer()) !== null) {
890
				if ($activeStep === $step) {
891
					$container->setVisible(true);
892
					$showStandard = false;
893
				} else {
894
					$container->setVisible(false);
895
				}
896
			}
897
		}
898
		$activeStepType = $this->getStepType($activeStep);
899
		if ($activeStepType === TWizardStepType::Complete) {
0 ignored issues
show
introduced by
The condition $activeStepType === Prad...izardStepType::Complete is always false.
Loading history...
900
			$this->_sideBar->setVisible(false);
901
			$this->_header->setVisible(false);
902
		}
903
		$this->_startNavigation->setVisible($showStandard && $activeStepType === TWizardStepType::Start);
0 ignored issues
show
Bug introduced by
The method setVisible() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

903
		$this->_startNavigation->/** @scrutinizer ignore-call */ 
904
                           setVisible($showStandard && $activeStepType === TWizardStepType::Start);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
904
		$this->_stepNavigation->setVisible($showStandard && $activeStepType === TWizardStepType::Step);
0 ignored issues
show
Bug introduced by
The method setVisible() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

904
		$this->_stepNavigation->/** @scrutinizer ignore-call */ 
905
                          setVisible($showStandard && $activeStepType === TWizardStepType::Step);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
905
		$this->_finishNavigation->setVisible($showStandard && $activeStepType === TWizardStepType::Finish);
0 ignored issues
show
Bug introduced by
The method setVisible() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

905
		$this->_finishNavigation->/** @scrutinizer ignore-call */ 
906
                            setVisible($showStandard && $activeStepType === TWizardStepType::Finish);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
906
907
		if (($navigationStyle = $this->getViewState('NavigationStyle', null)) !== null) {
908
			$this->_navigation->getStyle()->mergeWith($navigationStyle);
909
		}
910
911
		$displayCancelButton = $this->getShowCancelButton();
912
		$cancelButtonStyle = $this->getCancelButtonStyle();
913
		$buttonStyle = $this->getViewState('NavigationButtonStyle', null);
914
		if ($buttonStyle !== null) {
915
			$cancelButtonStyle->mergeWith($buttonStyle);
916
		}
917
918
		// apply styles to start navigation buttons
919
		if (($cancelButton = $this->_startNavigation->getCancelButton()) !== null) {
920
			$cancelButton->setVisible($displayCancelButton);
921
			$cancelButtonStyle->apply($cancelButton);
922
		}
923
		if (($button = $this->_startNavigation->getNextButton()) !== null) {
924
			$button->setVisible(true);
925
			$style = $this->getStartNextButtonStyle();
926
			if ($buttonStyle !== null) {
927
				$style->mergeWith($buttonStyle);
928
			}
929
			$style->apply($button);
930
			if ($activeStepType === TWizardStepType::Start) {
0 ignored issues
show
introduced by
The condition $activeStepType === Prad...\TWizardStepType::Start is always false.
Loading history...
931
				$this->getPage()->getClientScript()->registerDefaultButton($this, $button);
932
			}
933
		}
934
935
		// apply styles to finish navigation buttons
936
		if (($cancelButton = $this->_finishNavigation->getCancelButton()) !== null) {
937
			$cancelButton->setVisible($displayCancelButton);
938
			$cancelButtonStyle->apply($cancelButton);
939
		}
940
		if (($button = $this->_finishNavigation->getPreviousButton()) !== null) {
941
			$button->setVisible($this->allowNavigationToPreviousStep());
942
			$style = $this->getFinishPreviousButtonStyle();
943
			if ($buttonStyle !== null) {
944
				$style->mergeWith($buttonStyle);
945
			}
946
			$style->apply($button);
947
		}
948
		if (($button = $this->_finishNavigation->getCompleteButton()) !== null) {
949
			$button->setVisible(true);
950
			$style = $this->getFinishCompleteButtonStyle();
951
			if ($buttonStyle !== null) {
952
				$style->mergeWith($buttonStyle);
953
			}
954
			$style->apply($button);
955
			if ($activeStepType === TWizardStepType::Finish) {
0 ignored issues
show
introduced by
The condition $activeStepType === Prad...TWizardStepType::Finish is always false.
Loading history...
956
				$this->getPage()->getClientScript()->registerDefaultButton($this, $button);
957
			}
958
		}
959
960
		// apply styles to step navigation buttons
961
		if (($cancelButton = $this->_stepNavigation->getCancelButton()) !== null) {
962
			$cancelButton->setVisible($displayCancelButton);
963
			$cancelButtonStyle->apply($cancelButton);
964
		}
965
		if (($button = $this->_stepNavigation->getPreviousButton()) !== null) {
966
			$button->setVisible($this->allowNavigationToPreviousStep());
967
			$style = $this->getStepPreviousButtonStyle();
968
			if ($buttonStyle !== null) {
969
				$style->mergeWith($buttonStyle);
970
			}
971
			$style->apply($button);
972
		}
973
		if (($button = $this->_stepNavigation->getNextButton()) !== null) {
974
			$button->setVisible(true);
975
			$style = $this->getStepNextButtonStyle();
976
			if ($buttonStyle !== null) {
977
				$style->mergeWith($buttonStyle);
978
			}
979
			$style->apply($button);
980
			if ($activeStepType === TWizardStepType::Step) {
0 ignored issues
show
introduced by
The condition $activeStepType === Prad...s\TWizardStepType::Step is always false.
Loading history...
981
				$this->getPage()->getClientScript()->registerDefaultButton($this, $button);
982
			}
983
		}
984
	}
985
986
	/**
987
	 * @return TStack history containing step indexes that were navigated before
988
	 */
989
	protected function getHistory()
990
	{
991
		if (($history = $this->getControlState('History', null)) === null) {
992
			$history = new TStack();
993
			$this->setControlState('History', $history);
994
		}
995
		return $history;
996
	}
997
998
	/**
999
	 * Determines the type of the specified wizard step.
1000
	 * @param TWizardStep $wizardStep * @return TWizardStepType type of the step
1001
	 */
1002
	protected function getStepType($wizardStep)
1003
	{
1004
		if (($type = $wizardStep->getStepType()) === TWizardStepType::Auto) {
0 ignored issues
show
introduced by
The condition $type = $wizardStep->get...s\TWizardStepType::Auto is always false.
Loading history...
1005
			$steps = $this->getWizardSteps();
1006
			if (($index = $steps->indexOf($wizardStep)) >= 0) {
1007
				$stepCount = $steps->getCount();
1008
				if ($stepCount === 1 || ($index < $stepCount - 1 && $steps->itemAt($index + 1)->getStepType() === TWizardStepType::Complete)) {
1009
					return TWizardStepType::Finish;
1010
				} elseif ($index === 0) {
1011
					return TWizardStepType::Start;
1012
				} elseif ($index === $stepCount - 1) {
1013
					return TWizardStepType::Finish;
1014
				} else {
1015
					return TWizardStepType::Step;
1016
				}
1017
			} else {
1018
				return $type;
1019
			}
1020
		} else {
1021
			return $type;
1022
		}
1023
	}
1024
1025
	/**
1026
	 * Clears up everything within the wizard.
1027
	 */
1028
	protected function reset()
1029
	{
1030
		$this->getControls()->clear();
1031
		$this->_header = null;
1032
		$this->_stepContent = null;
1033
		$this->_sideBar = null;
1034
		$this->_sideBarDataList = null;
1035
		$this->_navigation = null;
1036
		$this->_startNavigation = null;
1037
		$this->_stepNavigation = null;
1038
		$this->_finishNavigation = null;
1039
	}
1040
1041
	/**
1042
	 * Creates child controls within the wizard
1043
	 */
1044
	public function createChildControls()
1045
	{
1046
		$this->reset();
1047
		$this->createSideBar();
1048
		$this->createHeader();
1049
		$this->createStepContent();
1050
		$this->createNavigation();
1051
	}
1052
1053
	/**
1054
	 * Creates the wizard header.
1055
	 */
1056
	protected function createHeader()
1057
	{
1058
		$this->_header = new TPanel();
1059
		if (($template = $this->getHeaderTemplate()) !== null) {
1060
			$template->instantiateIn($this->_header);
1061
		} else {
1062
			$this->_header->getControls()->add($this->getHeaderText());
1063
		}
1064
		$this->getControls()->add($this->_header);
1065
	}
1066
1067
	/**
1068
	 * Creates the wizard side bar
1069
	 */
1070
	protected function createSideBar()
1071
	{
1072
		if ($this->getShowSideBar()) {
1073
			if (($template = $this->getSideBarTemplate()) === null) {
1074
				$template = new TWizardSideBarTemplate();
1075
			}
1076
			$this->_sideBar = new TPanel();
1077
			$template->instantiateIn($this->_sideBar);
1078
			$this->getControls()->add($this->_sideBar);
1079
1080
			if (($this->_sideBarDataList = $this->_sideBar->findControl(self::ID_SIDEBAR_LIST)) !== null) {
1081
				$this->_sideBarDataList->attachEventHandler('OnItemCommand', [$this, 'dataListItemCommand']);
1082
				$this->_sideBarDataList->attachEventHandler('OnItemDataBound', [$this, 'dataListItemDataBound']);
1083
				$this->_sideBarDataList->setDataSource($this->getWizardSteps());
1084
				$this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
1085
				$this->_sideBarDataList->dataBind();
1086
			}
1087
		} else {
1088
			$this->_sideBar = new TPanel();
1089
			$this->getControls()->add($this->_sideBar);
1090
		}
1091
	}
1092
1093
	/**
1094
	 * Event handler for sidebar datalist's OnItemCommand event.
1095
	 * This method is used internally by wizard. It mainly
1096
	 * sets the active step index according to the button clicked in the sidebar.
1097
	 * @param mixed $sender sender of the event
1098
	 * @param TDataListCommandEventParameter $param
1099
	 */
1100
	public function dataListItemCommand($sender, $param)
1101
	{
1102
		$item = $param->getItem();
0 ignored issues
show
Unused Code introduced by
The assignment to $item is dead and can be removed.
Loading history...
1103
		if ($param->getCommandName() === self::CMD_MOVETO) {
1104
			$stepIndex = $this->getActiveStepIndex();
1105
			$newStepIndex = TPropertyValue::ensureInteger($param->getCommandParameter());
1106
			$navParam = new TWizardNavigationEventParameter($stepIndex);
1107
			$navParam->setNextStepIndex($newStepIndex);
1108
1109
			// if the button clicked causes validation which fails,
1110
			// by default we will cancel navigation to the new step
1111
			$button = $param->getCommandSource();
1112
			if (($button instanceof \Prado\Web\UI\IButtonControl) && $button->getCausesValidation() && ($page = $this->getPage()) !== null && !$page->getIsValid()) {
0 ignored issues
show
Bug introduced by
The method getCausesValidation() does not exist on Prado\Web\UI\TControl. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1112
			if (($button instanceof \Prado\Web\UI\IButtonControl) && $button->/** @scrutinizer ignore-call */ getCausesValidation() && ($page = $this->getPage()) !== null && !$page->getIsValid()) {
Loading history...
1113
				$navParam->setCancelNavigation(true);
1114
			}
1115
1116
			$this->_activeStepIndexSet = false;
1117
			$this->onSideBarButtonClick($navParam);
1118
			$this->_cancelNavigation = $navParam->getCancelNavigation();
1119
			if (!$this->_cancelNavigation) {
1120
				if (!$this->_activeStepIndexSet && $this->allowNavigationToStep($newStepIndex)) {
1121
					$this->setActiveStepIndex($newStepIndex);
1122
				}
1123
			} else {
1124
				$this->setActiveStepIndex($stepIndex);
1125
			}
1126
		}
1127
	}
1128
1129
	/**
1130
	 * Event handler for sidebar datalist's OnItemDataBound event.
1131
	 * This method is used internally by wizard. It mainly configures
1132
	 * the buttons in the sidebar datalist.
1133
	 * @param mixed $sender sender of the event
1134
	 * @param TDataListItemEventParameter $param
1135
	 */
1136
	public function dataListItemDataBound($sender, $param)
1137
	{
1138
		$item = $param->getItem();
1139
		$itemType = $item->getItemType();
0 ignored issues
show
Bug introduced by
The method getItemType() does not exist on Prado\Web\UI\TControl. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1139
		/** @scrutinizer ignore-call */ 
1140
  $itemType = $item->getItemType();
Loading history...
1140
		if ($itemType === 'Item' || $itemType === 'AlternatingItem' || $itemType === 'SelectedItem' || $itemType === 'EditItem') {
1141
			if (($button = $item->findControl(self::ID_SIDEBAR_BUTTON)) !== null) {
1142
				$step = $item->getData();
0 ignored issues
show
Bug introduced by
The method getData() does not exist on Prado\Web\UI\TControl. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1142
				/** @scrutinizer ignore-call */ 
1143
    $step = $item->getData();
Loading history...
1143
				if (($this->getStepType($step) === TWizardStepType::Complete)) {
0 ignored issues
show
introduced by
The condition $this->getStepType($step...izardStepType::Complete is always false.
Loading history...
1144
					$button->setEnabled(false);
1145
				}
1146
				if (($title = $step->getTitle()) !== '') {
1147
					$button->setText($title);
1148
				} else {
1149
					$button->setText($step->getID(false));
1150
				}
1151
				$index = $this->getWizardSteps()->indexOf($step);
1152
				$button->setCommandName(self::CMD_MOVETO);
1153
				$button->setCommandParameter("$index");
1154
			}
1155
		}
1156
	}
1157
1158
	/**
1159
	 * Creates wizard step content.
1160
	 */
1161
	protected function createStepContent()
1162
	{
1163
		foreach ($this->getWizardSteps() as $step) {
1164
			if ($step instanceof TTemplatedWizardStep) {
1165
				$step->ensureChildControls();
1166
			}
1167
		}
1168
		$multiView = $this->getMultiView();
1169
		$this->_stepContent = new TPanel();
1170
		$this->_stepContent->getControls()->add($multiView);
1171
		$this->getControls()->add($this->_stepContent);
1172
		if ($multiView->getViews()->getCount()) {
1173
			$multiView->setActiveViewIndex(0);
1174
		}
1175
	}
1176
1177
	/**
1178
	 * Creates navigation panel.
1179
	 */
1180
	protected function createNavigation()
1181
	{
1182
		$this->_navigation = new TPanel();
1183
		$this->getControls()->add($this->_navigation);
1184
		$controls = $this->_navigation->getControls();
1185
		foreach ($this->getWizardSteps() as $step) {
1186
			if ($step instanceof TTemplatedWizardStep) {
1187
				$step->instantiateNavigationTemplate();
1188
				if (($panel = $step->getNavigationContainer()) !== null) {
1189
					$controls->add($panel);
1190
				}
1191
			}
1192
		}
1193
		$this->_startNavigation = $this->createStartNavigation();
1194
		$controls->add($this->_startNavigation);
1195
		$this->_stepNavigation = $this->createStepNavigation();
1196
		$controls->add($this->_stepNavigation);
1197
		$this->_finishNavigation = $this->createFinishNavigation();
1198
		$controls->add($this->_finishNavigation);
1199
	}
1200
1201
	/**
1202
	 * Creates start navigation panel.
1203
	 */
1204
	protected function createStartNavigation()
1205
	{
1206
		if (($template = $this->getStartNavigationTemplate()) === null) {
1207
			$template = new TWizardStartNavigationTemplate($this);
1208
		}
1209
		$navigation = new TWizardNavigationContainer();
1210
		$template->instantiateIn($navigation);
1211
		return $navigation;
1212
	}
1213
1214
	/**
1215
	 * Creates step navigation panel.
1216
	 */
1217
	protected function createStepNavigation()
1218
	{
1219
		if (($template = $this->getStepNavigationTemplate()) === null) {
1220
			$template = new TWizardStepNavigationTemplate($this);
1221
		}
1222
		$navigation = new TWizardNavigationContainer();
1223
		$template->instantiateIn($navigation);
1224
		return $navigation;
1225
	}
1226
1227
	/**
1228
	 * Creates finish navigation panel.
1229
	 */
1230
	protected function createFinishNavigation()
1231
	{
1232
		if (($template = $this->getFinishNavigationTemplate()) === null) {
1233
			$template = new TWizardFinishNavigationTemplate($this);
1234
		}
1235
		$navigation = new TWizardNavigationContainer();
1236
		$template->instantiateIn($navigation);
1237
		return $navigation;
1238
	}
1239
1240
	/**
1241
	 * Updates the sidebar datalist if any.
1242
	 * This method is invoked when any wizard step is changed.
1243
	 */
1244
	public function wizardStepsChanged()
1245
	{
1246
		if ($this->_sideBarDataList !== null) {
1247
			$this->_sideBarDataList->setDataSource($this->getWizardSteps());
1248
			$this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
1249
			$this->_sideBarDataList->dataBind();
1250
		}
1251
	}
1252
1253
	/**
1254
	 * Determines the index of the previous step based on history.
1255
	 * @param bool $popStack whether the first item in the history stack should be popped
1256
	 * up after calling this method.
1257
	 */
1258
	protected function getPreviousStepIndex($popStack)
1259
	{
1260
		$history = $this->getHistory();
1261
		if ($history->getCount() >= 0) {
1262
			$activeStepIndex = $this->getActiveStepIndex();
1263
			$previousStepIndex = -1;
1264
			if ($popStack) {
1265
				$previousStepIndex = $history->pop();
1266
				if ($activeStepIndex === $previousStepIndex && $history->getCount() > 0) {
1267
					$previousStepIndex = $history->pop();
1268
				}
1269
			} else {
1270
				$previousStepIndex = $history->peek();
1271
				if ($activeStepIndex === $previousStepIndex && $history->getCount() > 1) {
1272
					$saveIndex = $history->pop();
1273
					$previousStepIndex = $history->peek();
1274
					$history->push($saveIndex);
1275
				}
1276
			}
1277
			return $activeStepIndex === $previousStepIndex ? -1 : $previousStepIndex;
1278
		} else {
1279
			return -1;
1280
		}
1281
	}
1282
1283
	/**
1284
	 * @return bool whether navigation to the previous step is allowed
1285
	 */
1286
	protected function allowNavigationToPreviousStep()
1287
	{
1288
		if (($index = $this->getPreviousStepIndex(false)) !== -1) {
1289
			return $this->getWizardSteps()->itemAt($index)->getAllowReturn();
1290
		} else {
1291
			return false;
1292
		}
1293
	}
1294
1295
	/**
1296
	 * @param int $index index of the step
1297
	 * @return bool whether navigation to the specified step is allowed
1298
	 */
1299
	protected function allowNavigationToStep($index)
1300
	{
1301
		if ($this->getHistory()->contains($index)) {
1302
			return $this->getWizardSteps()->itemAt($index)->getAllowReturn();
1303
		} else {
1304
			return true;
1305
		}
1306
	}
1307
1308
	/**
1309
	 * Handles bubbled events.
1310
	 * This method mainly translate certain command events into
1311
	 * wizard-specific events.
1312
	 * @param mixed $sender sender of the original command event
1313
	 * @param \Prado\TEventParameter $param event parameter
1314
	 * @throws TInvalidDataValueException if a navigation command is associated with an invalid parameter
1315
	 */
1316
	public function bubbleEvent($sender, $param)
1317
	{
1318
		if ($param instanceof \Prado\Web\UI\TCommandEventParameter) {
1319
			$command = $param->getCommandName();
1320
			if (strcasecmp($command, self::CMD_CANCEL) === 0) {
1321
				$this->onCancelButtonClick($param);
1322
				return true;
1323
			}
1324
1325
			$type = $this->getStepType($this->getActiveStep());
1326
			$index = $this->getActiveStepIndex();
1327
			$navParam = new TWizardNavigationEventParameter($index);
1328
			if (($sender instanceof \Prado\Web\UI\IButtonControl) && $sender->getCausesValidation() && ($page = $this->getPage()) !== null && !$page->getIsValid()) {
1329
				$navParam->setCancelNavigation(true);
1330
			}
1331
1332
			$handled = false;
1333
			$movePrev = false;
1334
			$this->_activeStepIndexSet = false;
1335
1336
			if (strcasecmp($command, self::CMD_NEXT) === 0) {
1337
				if ($type !== TWizardStepType::Start && $type !== TWizardStepType::Step) {
0 ignored issues
show
introduced by
The condition $type !== Prado\Web\UI\W...s\TWizardStepType::Step is always true.
Loading history...
1338
					throw new TInvalidDataValueException('wizard_command_invalid', self::CMD_NEXT);
1339
				}
1340
				if ($index < $this->getWizardSteps()->getCount() - 1) {
1341
					$navParam->setNextStepIndex($index + 1);
1342
				}
1343
				$this->onNextButtonClick($navParam);
1344
				$handled = true;
1345
			} elseif (strcasecmp($command, self::CMD_PREVIOUS) === 0) {
1346
				if ($type !== TWizardStepType::Finish && $type !== TWizardStepType::Step) {
0 ignored issues
show
introduced by
The condition $type !== Prado\Web\UI\W...s\TWizardStepType::Step is always true.
Loading history...
1347
					throw new TInvalidDataValueException('wizard_command_invalid', self::CMD_PREVIOUS);
1348
				}
1349
				$movePrev = true;
1350
				if (($prevIndex = $this->getPreviousStepIndex(false)) >= 0) {
1351
					$navParam->setNextStepIndex($prevIndex);
1352
				}
1353
				$this->onPreviousButtonClick($navParam);
1354
				$handled = true;
1355
			} elseif (strcasecmp($command, self::CMD_COMPLETE) === 0) {
1356
				if ($type !== TWizardStepType::Finish) {
0 ignored issues
show
introduced by
The condition $type !== Prado\Web\UI\W...TWizardStepType::Finish is always true.
Loading history...
1357
					throw new TInvalidDataValueException('wizard_command_invalid', self::CMD_COMPLETE);
1358
				}
1359
				if ($index < $this->getWizardSteps()->getCount() - 1) {
1360
					$navParam->setNextStepIndex($index + 1);
1361
				}
1362
				$this->onCompleteButtonClick($navParam);
1363
				$handled = true;
1364
			} elseif (strcasecmp($command, self::CMD_MOVETO) === 0) {
1365
				if ($this->_cancelNavigation) {  // may be set in onSideBarButtonClick
1366
					$navParam->setCancelNavigation(true);
1367
				}
1368
				$requestedStep = $param->getCommandParameter();
1369
				if (!is_numeric($requestedStep)) {
1370
					$requestedIndex = -1;
1371
					foreach ($this->getWizardSteps() as $index => $step) {
1372
						if ($step->getId() === $requestedStep) {
1373
							$requestedIndex = $index;
1374
							break;
1375
						}
1376
					}
1377
					if ($requestedIndex < 0) {
1378
						throw new TConfigurationException('wizard_step_invalid');
1379
					}
1380
				} else {
1381
					$requestedIndex = TPropertyValue::ensureInteger($requestedStep);
1382
				}
1383
				$navParam->setNextStepIndex($requestedIndex);
1384
				$handled = true;
1385
			}
1386
1387
			if ($handled) {
1388
				if (!$navParam->getCancelNavigation()) {
1389
					$nextStepIndex = $navParam->getNextStepIndex();
1390
					if (!$this->_activeStepIndexSet && $this->allowNavigationToStep($nextStepIndex)) {
1391
						if ($movePrev) {
0 ignored issues
show
introduced by
The condition $movePrev is always false.
Loading history...
1392
							$this->getPreviousStepIndex(true);
1393
						}  // pop out the previous move from history
1394
						$this->setActiveStepIndex($nextStepIndex);
1395
					}
1396
				} else {
1397
					$this->setActiveStepIndex($index);
1398
				}
1399
				return true;
1400
			}
1401
		}
1402
		return false;
1403
	}
1404
}
1405