Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TPage class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TPage.php 2523 2008-10-14 14:05:27Z mikl $
10
 * @package System.Web.UI
11
 */
12
 
13
Prado::using('System.Web.UI.WebControls.*');
14
Prado::using('System.Web.UI.TControl');
15
Prado::using('System.Web.UI.WebControls.TWebControl');
16
Prado::using('System.Web.UI.TCompositeControl');
17
Prado::using('System.Web.UI.TTemplateControl');
18
Prado::using('System.Web.UI.TForm');
19
Prado::using('System.Web.UI.TClientScriptManager');
20
 
21
/**
22
 * TPage class
23
 *
24
 * @author Qiang Xue <qiang.xue@gmail.com>
25
 * @version $Id: TPage.php 2523 2008-10-14 14:05:27Z mikl $
26
 * @package System.Web.UI
27
 * @since 3.0
28
 */
29
class TPage extends TTemplateControl
30
{
31
	/**
32
	 * system post fields
33
	 */
34
	const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET';
35
	const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER';
36
	const FIELD_LASTFOCUS='PRADO_LASTFOCUS';
37
	const FIELD_PAGESTATE='PRADO_PAGESTATE';
38
	const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET';
39
	const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER';
40
 
41
	/**
42
	 * @var array system post fields
43
	 */
44
	private static $_systemPostFields=array(
45
		'PRADO_POSTBACK_TARGET'=>true,
46
		'PRADO_POSTBACK_PARAMETER'=>true,
47
		'PRADO_LASTFOCUS'=>true,
48
		'PRADO_PAGESTATE'=>true,
49
		'PRADO_CALLBACK_TARGET'=>true,
50
		'PRADO_CALLBACK_PARAMETER'=>true
51
	);
52
	/**
53
	 * @var TForm form instance
54
	 */
55
	private $_form;
56
	/**
57
	 * @var THead head instance
58
	 */
59
	private $_head;
60
	/**
61
	 * @var array list of registered validators
62
	 */
63
	private $_validators=array();
64
	/**
65
	 * @var boolean if validation has been performed
66
	 */
67
	private $_validated=false;
68
	/**
69
	 * @var TTheme page theme
70
	 */
71
	private $_theme;
72
	/**
73
	 * @var string page title set when Head is not in page yet
74
	 */
75
	private $_title;
76
	/**
77
	 * @var TTheme page stylesheet theme
78
	 */
79
	private $_styleSheet;
80
	/**
81
	 * @var TClientScriptManager client script manager
82
	 */
83
	private $_clientScript;
84
	/**
85
	 * @var TMap data post back by user
86
	 */
87
	private $_postData;
88
	/**
89
	 * @var TMap postback data that is not handled during first invocation of LoadPostData.
90
	 */
91
	private $_restPostData;
92
	/**
93
	 * @var array list of controls whose data have been changed due to the postback
94
	 */
95
	private $_controlsPostDataChanged=array();
96
	/**
97
	 * @var array list of controls that need to load post data in the current request
98
	 */
99
	private $_controlsRequiringPostData=array();
100
	/**
101
	 * @var array list of controls that need to load post data in the next postback
102
	 */
103
	private $_controlsRegisteredForPostData=array();
104
	/**
105
	 * @var TControl control that needs to raise postback event
106
	 */
107
	private $_postBackEventTarget;
108
	/**
109
	 * @var string postback event parameter
110
	 */
111
	private $_postBackEventParameter;
112
	/**
113
	 * @var boolean whether the form has been rendered
114
	 */
115
	private $_formRendered=false;
116
	/**
117
	 * @var boolean whether the current rendering is within a form
118
	 */
119
	private $_inFormRender=false;
120
	/**
121
	 * @var TControl|string the control or the ID of the element on the page to be focused when the page is sent back to user
122
	 */
123
	private $_focus;
124
	/**
125
	 * @var string page path to this page
126
	 */
127
	private $_pagePath='';
128
	/**
129
	 * @var boolean whether page state should be HMAC validated
130
	 */
131
	private $_enableStateValidation=true;
132
	/**
133
	 * @var boolean whether page state should be encrypted
134
	 */
135
	private $_enableStateEncryption=false;
136
	/**
137
	 * @var string page state persister class name
138
	 */
139
	private $_statePersisterClass='System.Web.UI.TPageStatePersister';
140
	/**
141
	 * @var mixed page state persister
142
	 */
143
	private $_statePersister;
144
	/**
145
	 * @var TStack stack used to store currently active caching controls
146
	 */
147
	private $_cachingStack;
148
	/**
149
	 * @var string state string to be stored on the client side
150
	 */
151
	private $_clientState='';
152
	/**
153
	 * @var array post data loader IDs.
154
	 */
155
	private $_postDataLoaders=array();
156
	/**
157
	 * @var boolean true if loading post data.
158
	 */
159
	private $_isLoadingPostData=false;
160
	/**
161
	 * @var boolean whether client supports javascript
162
	 */
163
	private $_enableJavaScript=true;
164
 
165
	/**
166
	 * Constructor.
167
	 * Sets the page object to itself.
168
	 * Derived classes must call parent implementation.
169
	 */
170
	public function __construct()
171
	{
172
		parent::__construct();
173
		$this->setPage($this);
174
	}
175
 
176
	/**
177
	 * Runs through the page lifecycles.
178
	 * @param THtmlTextWriter the HTML writer
179
	 */
180
	public function run($writer)
181
	{
182
		Prado::trace("Running page life cycles",'System.Web.UI.TPage');
183
		$this->determinePostBackMode();
184
 
185
		if($this->getIsPostBack())
186
		{
187
			if($this->getIsCallback())
188
				$this->processCallbackRequest($writer);
189
			else
190
				$this->processPostBackRequest($writer);
191
		}
192
		else
193
			$this->processNormalRequest($writer);
194
	}
195
 
196
	protected function processNormalRequest($writer)
197
	{
198
		Prado::trace("Page onPreInit()",'System.Web.UI.TPage');
199
		$this->onPreInit(null);
200
 
201
		Prado::trace("Page initRecursive()",'System.Web.UI.TPage');
202
		$this->initRecursive();
203
 
204
		Prado::trace("Page onInitComplete()",'System.Web.UI.TPage');
205
		$this->onInitComplete(null);
206
 
207
		Prado::trace("Page onPreLoad()",'System.Web.UI.TPage');
208
		$this->onPreLoad(null);
209
		Prado::trace("Page loadRecursive()",'System.Web.UI.TPage');
210
		$this->loadRecursive();
211
		Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage');
212
		$this->onLoadComplete(null);
213
 
214
		Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage');
215
		$this->preRenderRecursive();
216
		Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage');
217
		$this->onPreRenderComplete(null);
218
 
219
		Prado::trace("Page savePageState()",'System.Web.UI.TPage');
220
		$this->savePageState();
221
		Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage');
222
		$this->onSaveStateComplete(null);
223
 
224
		Prado::trace("Page renderControl()",'System.Web.UI.TPage');
225
		$this->renderControl($writer);
226
		Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage');
227
		$this->unloadRecursive();
228
	}
229
 
230
	protected function processPostBackRequest($writer)
231
	{
232
		Prado::trace("Page onPreInit()",'System.Web.UI.TPage');
233
		$this->onPreInit(null);
234
 
235
		Prado::trace("Page initRecursive()",'System.Web.UI.TPage');
236
		$this->initRecursive();
237
 
238
		Prado::trace("Page onInitComplete()",'System.Web.UI.TPage');
239
		$this->onInitComplete(null);
240
 
241
		$this->_restPostData=new TMap;
242
		Prado::trace("Page loadPageState()",'System.Web.UI.TPage');
243
		$this->loadPageState();
244
		Prado::trace("Page processPostData()",'System.Web.UI.TPage');
245
		$this->processPostData($this->_postData,true);
246
		Prado::trace("Page onPreLoad()",'System.Web.UI.TPage');
247
		$this->onPreLoad(null);
248
		Prado::trace("Page loadRecursive()",'System.Web.UI.TPage');
249
		$this->loadRecursive();
250
		Prado::trace("Page processPostData()",'System.Web.UI.TPage');
251
		$this->processPostData($this->_restPostData,false);
252
		Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage');
253
		$this->raiseChangedEvents();
254
		Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage');
255
		$this->raisePostBackEvent();
256
		Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage');
257
		$this->onLoadComplete(null);
258
 
259
		Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage');
260
		$this->preRenderRecursive();
261
		Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage');
262
		$this->onPreRenderComplete(null);
263
 
264
		Prado::trace("Page savePageState()",'System.Web.UI.TPage');
265
		$this->savePageState();
266
		Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage');
267
		$this->onSaveStateComplete(null);
268
 
269
		Prado::trace("Page renderControl()",'System.Web.UI.TPage');
270
		$this->renderControl($writer);
271
		Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage');
272
		$this->unloadRecursive();
273
	}
274
 
275
	/**
276
	 * Sets Adapter to TActivePageAdapter and calls apter to process the
277
	 * callback request.
278
	 */
279
	protected function processCallbackRequest($writer)
280
	{
281
		Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter');
282
 
283
		$this->setAdapter(new TActivePageAdapter($this));
284
 
285
        // Decode Callback postData from UTF-8 to current Charset
286
        if (($g=$this->getApplication()->getGlobalization(false))!==null &&
287
            strtoupper($enc=$g->getCharset())!='UTF-8')
288
                foreach ($this->_postData as $k=>$v)
289
                    $this->_postData[$k]=iconv('UTF-8',$enc.'//IGNORE',$v);
290
 
291
		Prado::trace("Page onPreInit()",'System.Web.UI.TPage');
292
		$this->onPreInit(null);
293
 
294
		Prado::trace("Page initRecursive()",'System.Web.UI.TPage');
295
		$this->initRecursive();
296
 
297
		Prado::trace("Page onInitComplete()",'System.Web.UI.TPage');
298
		$this->onInitComplete(null);
299
 
300
		$this->_restPostData=new TMap;
301
		Prado::trace("Page loadPageState()",'System.Web.UI.TPage');
302
		$this->loadPageState();
303
		Prado::trace("Page processPostData()",'System.Web.UI.TPage');
304
		$this->processPostData($this->_postData,true);
305
		Prado::trace("Page onPreLoad()",'System.Web.UI.TPage');
306
		$this->onPreLoad(null);
307
		Prado::trace("Page loadRecursive()",'System.Web.UI.TPage');
308
		$this->loadRecursive();
309
 
310
		Prado::trace("Page processPostData()",'System.Web.UI.TPage');
311
		$this->processPostData($this->_restPostData,false);
312
 
313
		Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage');
314
		$this->raiseChangedEvents();
315
 
316
 
317
		$this->getAdapter()->processCallbackEvent($writer);
318
 
319
/*
320
		Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage');
321
		$this->raisePostBackEvent();
322
*/
323
		Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage');
324
		$this->onLoadComplete(null);
325
 
326
		Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage');
327
		$this->preRenderRecursive();
328
		Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage');
329
		$this->onPreRenderComplete(null);
330
 
331
		Prado::trace("Page savePageState()",'System.Web.UI.TPage');
332
		$this->savePageState();
333
		Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage');
334
		$this->onSaveStateComplete(null);
335
 
336
/*
337
		Prado::trace("Page renderControl()",'System.Web.UI.TPage');
338
		$this->renderControl($writer);
339
*/
340
		$this->getAdapter()->renderCallbackResponse($writer);
341
 
342
		Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage');
343
		$this->unloadRecursive();
344
	}
345
 
346
	/**
347
	 * Gets the callback client script handler that allows javascript functions
348
	 * to be executed during the callback response.
349
	 * @return TCallbackClientScript interface to client-side javascript code.
350
	 */
351
	public function getCallbackClient()
352
	{
353
		if($this->getAdapter() !== null)
354
			return $this->getAdapter()->getCallbackClientHandler();
355
		else
356
			return new TCallbackClientScript();
357
	}
358
 
359
	/**
360
	 * Set a new callback client handler.
361
	 * @param TCallbackClientScript new callback client script handler.
362
	 */
363
	public function setCallbackClient($client)
364
	{
365
		$this->getAdapter()->setCallbackClientHandler($client);
366
	}
367
 
368
	/**
369
	 * @return TControl the control responsible for the current callback event,
370
	 * null if nonexistent
371
	 */
372
	public function getCallbackEventTarget()
373
	{
374
		return $this->getAdapter()->getCallbackEventTarget();
375
	}
376
 
377
	/**
378
	 * Registers a control to raise callback event in the current request.
379
	 * @param TControl control registered to raise callback event.
380
	 */
381
	public function setCallbackEventTarget(TControl $control)
382
	{
383
		$this->getAdapter()->setCallbackEventTarget($control);
384
	}
385
 
386
	/**
387
	 * Callback parameter is decoded assuming JSON encoding.
388
	 * @return string callback event parameter
389
	 */
390
	public function getCallbackEventParameter()
391
	{
392
		return $this->getAdapter()->getCallbackEventParameter();
393
	}
394
 
395
	/**
396
	 * @param mixed callback event parameter
397
	 */
398
	public function setCallbackEventParameter($value)
399
	{
400
		$this->getAdapter()->setCallbackEventParameter($value);
401
	}
402
 
403
	/**
404
	 * Register post data loaders for Callback to collect post data.
405
	 * This method should only be called by framework developers.
406
	 * @param TControl control that requires post data.
407
	 * @see TControl::preRenderRecursive();
408
	 */
409
	public function registerPostDataLoader($control)
410
	{
411
		$id=is_string($control)?$control:$control->getUniqueID();
412
		$this->_postDataLoaders[$id] = true;
413
	}
414
 
415
	/**
416
	 * Get a list of IDs of controls that are enabled and require post data.
417
	 * @return array list of IDs implementing IPostBackDataHandler
418
	 */
419
	public function getPostDataLoaders()
420
	{
421
		return array_keys($this->_postDataLoaders);
422
	}
423
 
424
	/**
425
	 * @return TForm the form on the page
426
	 */
427
	public function getForm()
428
	{
429
		return $this->_form;
430
	}
431
 
432
	/**
433
	 * Registers a TForm instance to the page.
434
	 * Note, a page can contain at most one TForm instance.
435
	 * @param TForm the form on the page
436
	 * @throws TInvalidOperationException if this method is invoked twice or more.
437
	 */
438
	public function setForm(TForm $form)
439
	{
440
		if($this->_form===null)
441
			$this->_form=$form;
442
		else
443
			throw new TInvalidOperationException('page_form_duplicated');
444
	}
445
 
446
	/**
447
	 * Returns a list of registered validators.
448
	 * If validation group is specified, only the validators in that group will be returned.
449
	 * @param string validation group
450
	 * @return TList registered validators in the requested group. If the group is null, all validators will be returned.
451
	 */
452
	public function getValidators($validationGroup=null)
453
	{
454
		if(!$this->_validators)
455
			$this->_validators=new TList;
456
		if($validationGroup===null)
457
			return $this->_validators;
458
		else
459
		{
460
			$list=new TList;
461
			foreach($this->_validators as $validator)
462
				if($validator->getValidationGroup()===$validationGroup)
463
					$list->add($validator);
464
			return $list;
465
		}
466
	}
467
 
468
	/**
469
	 * Performs input validation.
470
	 * This method will invoke the registered validators to perform the actual validation.
471
	 * If validation group is specified, only the validators in that group will be invoked.
472
	 * @param string validation group. If null, all validators will perform validation.
473
	 */
474
	public function validate($validationGroup=null)
475
	{
476
		Prado::trace("Page validate()",'System.Web.UI.TPage');
477
		$this->_validated=true;
478
		if($this->_validators && $this->_validators->getCount())
479
		{
480
			if($validationGroup===null)
481
			{
482
				foreach($this->_validators as $validator)
483
					$validator->validate();
484
			}
485
			else
486
			{
487
				foreach($this->_validators as $validator)
488
				{
489
					if($validator->getValidationGroup()===$validationGroup)
490
						$validator->validate();
491
				}
492
			}
493
		}
494
	}
495
 
496
	/**
497
	 * Returns whether user input is valid or not.
498
	 * This method must be invoked after {@link validate} is called.
499
	 * @return boolean whether the user input is valid or not.
500
	 * @throws TInvalidOperationException if {@link validate} is not invoked yet.
501
	 */
502
	public function getIsValid()
503
	{
504
		if($this->_validated)
505
		{
506
			if($this->_validators && $this->_validators->getCount())
507
			{
508
				foreach($this->_validators as $validator)
509
					if(!$validator->getIsValid())
510
						return false;
511
			}
512
			return true;
513
		}
514
		else
515
			throw new TInvalidOperationException('page_isvalid_unknown');
516
	}
517
 
518
	/**
519
	 * @return TTheme the theme used for the page. Defaults to null.
520
	 */
521
	public function getTheme()
522
	{
523
		if(is_string($this->_theme))
524
			$this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme);
525
		return $this->_theme;
526
	}
527
 
528
	/**
529
	 * Sets the theme to be used for the page.
530
	 * @param string|TTheme the theme name or the theme object to be used for the page.
531
	 */
532
	public function setTheme($value)
533
	{
534
		$this->_theme=empty($value)?null:$value;
535
	}
536
 
537
 
538
	/**
539
	 * @return TTheme the stylesheet theme used for the page. Defaults to null.
540
	 */
541
	public function getStyleSheetTheme()
542
	{
543
		if(is_string($this->_styleSheet))
544
			$this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet);
545
		return $this->_styleSheet;
546
	}
547
 
548
	/**
549
	 * Sets the stylesheet theme to be used for the page.
550
	 * @param string|TTheme the stylesheet theme name or the stylesheet theme object to be used for the page.
551
	 */
552
	public function setStyleSheetTheme($value)
553
	{
554
		$this->_styleSheet=empty($value)?null:$value;
555
	}
556
 
557
	/**
558
	 * Applies a skin in the current theme to a control.
559
	 * This method should only be used by framework developers.
560
	 * @param TControl a control to be applied skin with
561
	 */
562
	public function applyControlSkin($control)
563
	{
564
		if(($theme=$this->getTheme())!==null)
565
			$theme->applySkin($control);
566
	}
567
 
568
	/**
569
	 * Applies a stylesheet skin in the current theme to a control.
570
	 * This method should only be used by framework developers.
571
	 * @param TControl a control to be applied stylesheet skin with
572
	 */
573
	public function applyControlStyleSheet($control)
574
	{
575
		if(($theme=$this->getStyleSheetTheme())!==null)
576
			$theme->applySkin($control);
577
	}
578
 
579
	/**
580
	 * @return TClientScriptManager client script manager
581
	 */
582
	public function getClientScript()
583
	{
584
		if(!$this->_clientScript)
585
			$this->_clientScript=new TClientScriptManager($this);
586
		return $this->_clientScript;
587
	}
588
 
589
	/**
590
	 * Raises OnPreInit event.
591
	 * This method is invoked right before {@link onInit OnInit} stage.
592
	 * You may override this method to provide additional initialization that
593
	 * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or
594
	 * {@link setStyleSheetTheme StyleSheetTheme}).
595
	 * Remember to call the parent implementation to ensure OnPreInit event is raised.
596
	 * @param mixed event parameter
597
	 */
598
	public function onPreInit($param)
599
	{
600
		$this->raiseEvent('OnPreInit',$this,$param);
601
	}
602
 
603
	/**
604
	 * Raises OnInitComplete event.
605
	 * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage.
606
	 * You may override this method to provide additional initialization that
607
	 * should be done after {@link onInit OnInit}.
608
	 * Remember to call the parent implementation to ensure OnInitComplete event is raised.
609
	 * @param mixed event parameter
610
	 */
611
	public function onInitComplete($param)
612
	{
613
		$this->raiseEvent('OnInitComplete',$this,$param);
614
	}
615
 
616
	/**
617
	 * Raises OnPreLoad event.
618
	 * This method is invoked right before {@link onLoad OnLoad} stage.
619
	 * You may override this method to provide additional page loading logic that
620
	 * should be done before {@link onLoad OnLoad}.
621
	 * Remember to call the parent implementation to ensure OnPreLoad event is raised.
622
	 * @param mixed event parameter
623
	 */
624
	public function onPreLoad($param)
625
	{
626
		$this->raiseEvent('OnPreLoad',$this,$param);
627
	}
628
 
629
	/**
630
	 * Raises OnLoadComplete event.
631
	 * This method is invoked right after {@link onLoad OnLoad} stage.
632
	 * You may override this method to provide additional page loading logic that
633
	 * should be done after {@link onLoad OnLoad}.
634
	 * Remember to call the parent implementation to ensure OnLoadComplete event is raised.
635
	 * @param mixed event parameter
636
	 */
637
	public function onLoadComplete($param)
638
	{
639
		$this->raiseEvent('OnLoadComplete',$this,$param);
640
	}
641
 
642
	/**
643
	 * Raises OnPreRenderComplete event.
644
	 * This method is invoked right after {@link onPreRender OnPreRender} stage.
645
	 * You may override this method to provide additional preparation for page rendering
646
	 * that should be done after {@link onPreRender OnPreRender}.
647
	 * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised.
648
	 * @param mixed event parameter
649
	 */
650
	public function onPreRenderComplete($param)
651
	{
652
		$this->raiseEvent('OnPreRenderComplete',$this,$param);
653
		$cs=$this->getClientScript();
654
		$theme=$this->getTheme();
655
		if($theme instanceof ITheme)
656
		{
657
			foreach($theme->getStyleSheetFiles() as $url)
658
				$cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url));
659
			foreach($theme->getJavaScriptFiles() as $url)
660
				$cs->registerHeadScriptFile($url,$url);
661
		}
662
		$styleSheet=$this->getStyleSheetTheme();
663
		if($styleSheet instanceof ITheme)
664
		{
665
			foreach($styleSheet->getStyleSheetFiles() as $url)
666
				$cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url));
667
			foreach($styleSheet->getJavaScriptFiles() as $url)
668
				$cs->registerHeadScriptFile($url,$url);
669
		}
670
 
671
		if($cs->getRequiresHead() && $this->getHead()===null)
672
			throw new TConfigurationException('page_head_required');
673
	}
674
 
675
	/**
676
	 * Determines the media type of the CSS file.
677
	 * The media type is determined according to the following file name pattern:
678
	 *        xxx.media-type.extension
679
	 * For example, 'mystyle.print.css' means its media type is 'print'.
680
	 * @param string CSS URL
681
	 * @return string media type of the CSS file
682
	 */
683
	private function getCssMediaType($url)
684
	{
685
		$segs=explode('.',basename($url));
686
		if(isset($segs[2]))
687
			return $segs[count($segs)-2];
688
		else
689
			return '';
690
	}
691
 
692
	/**
693
	 * Raises OnSaveStateComplete event.
694
	 * This method is invoked right after {@link onSaveState OnSaveState} stage.
695
	 * You may override this method to provide additional logic after page state is saved.
696
	 * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised.
697
	 * @param mixed event parameter
698
	 */
699
	public function onSaveStateComplete($param)
700
	{
701
		$this->raiseEvent('OnSaveStateComplete',$this,$param);
702
	}
703
 
704
	/**
705
	 * Determines whether the current page request is a postback.
706
	 * Call {@link getIsPostBack} to get the result.
707
	 */
708
	private function determinePostBackMode()
709
	{
710
		$postData=$this->getRequest();
711
		if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET))
712
			$this->_postData=$postData;
713
	}
714
 
715
	/**
716
	 * @return boolean whether the current page request is a postback
717
	 */
718
	public function getIsPostBack()
719
	{
720
		return $this->_postData!==null;
721
	}
722
 
723
	/**
724
	 * @return boolean whether this is a callback request
725
	 */
726
	public function getIsCallback()
727
	{
728
		return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET);
729
	}
730
 
731
	/**
732
	 * This method is invoked when control state is to be saved.
733
	 * You can override this method to do last step state saving.
734
	 * Parent implementation must be invoked.
735
	 */
736
	public function saveState()
737
	{
738
		parent::saveState();
739
		$this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array());
740
	}
741
 
742
	/**
743
	 * This method is invoked right after the control has loaded its state.
744
	 * You can override this method to initialize data from the control state.
745
	 * Parent implementation must be invoked.
746
	 */
747
	public function loadState()
748
	{
749
		parent::loadState();
750
		$this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array());
751
	}
752
 
753
	/**
754
	 * Loads page state from persistent storage.
755
	 */
756
	protected function loadPageState()
757
	{
758
		Prado::trace("Loading state",'System.Web.UI.TPage');
759
		$state=$this->getStatePersister()->load();
760
		$this->loadStateRecursive($state,$this->getEnableViewState());
761
	}
762
 
763
	/**
764
	 * Saves page state from persistent storage.
765
	 */
766
	protected function savePageState()
767
	{
768
		Prado::trace("Saving state",'System.Web.UI.TPage');
769
		$state=&$this->saveStateRecursive($this->getEnableViewState());
770
		$this->getStatePersister()->save($state);
771
	}
772
 
773
	/**
774
	 * @param string the field name
775
	 * @return boolean whether the specified field is a system field in postback data
776
	 */
777
	protected function isSystemPostField($field)
778
	{
779
		return isset(self::$_systemPostFields[$field]);
780
	}
781
 
782
	/**
783
	 * Registers a control for loading post data in the next postback.
784
	 * This method needs to be invoked if the control to load post data
785
	 * may not have a post variable in some cases. For example, a checkbox,
786
	 * if not checked, will not have a post value.
787
	 * @param TControl control registered for loading post data
788
	 */
789
	public function registerRequiresPostData($control)
790
	{
791
		$id=is_string($control)?$control:$control->getUniqueID();
792
		$this->_controlsRegisteredForPostData[$id]=true;
793
		$this->registerPostDataLoader($id);
794
		$params=func_get_args();
795
		foreach($this->getCachingStack() as $item)
796
			$item->registerAction('Page','registerRequiresPostData',$id);
797
	}
798
 
799
	/**
800
	 * @return TControl the control responsible for the current postback event, null if nonexistent
801
	 */
802
	public function getPostBackEventTarget()
803
	{
804
		if($this->_postBackEventTarget===null && $this->_postData!==null)
805
		{
806
			$eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET);
807
			if(!empty($eventTarget))
808
				$this->_postBackEventTarget=$this->findControl($eventTarget);
809
		}
810
		return $this->_postBackEventTarget;
811
	}
812
 
813
	/**
814
	 * Registers a control to raise postback event in the current request.
815
	 * @param TControl control registered to raise postback event.
816
	 */
817
	public function setPostBackEventTarget(TControl $control)
818
	{
819
		$this->_postBackEventTarget=$control;
820
	}
821
 
822
	/**
823
	 * @return string postback event parameter
824
	 */
825
	public function getPostBackEventParameter()
826
	{
827
		if($this->_postBackEventParameter===null && $this->_postData!==null)
828
		{
829
			if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null)
830
				$this->_postBackEventParameter='';
831
		}
832
		return $this->_postBackEventParameter;
833
	}
834
 
835
	/**
836
	 * @param string postback event parameter
837
	 */
838
	public function setPostBackEventParameter($value)
839
	{
840
		$this->_postBackEventParameter=$value;
841
	}
842
 
843
	/**
844
	 * Processes post data.
845
	 * @param TMap post data to be processed
846
	 * @param boolean whether this method is invoked before {@link onLoad OnLoad}.
847
	 */
848
	protected function processPostData($postData,$beforeLoad)
849
	{
850
		$this->_isLoadingPostData=true;
851
		if($beforeLoad)
852
			$this->_restPostData=new TMap;
853
		foreach($postData as $key=>$value)
854
		{
855
			if($this->isSystemPostField($key))
856
				continue;
857
			else if($control=$this->findControl($key))
858
			{
859
				if($control instanceof IPostBackDataHandler)
860
				{
861
					if($control->loadPostData($key,$postData))
862
						$this->_controlsPostDataChanged[]=$control;
863
				}
864
				else if($control instanceof IPostBackEventHandler &&
865
					empty($this->_postData[self::FIELD_POSTBACK_TARGET]))
866
				{
867
					$this->_postData->add(self::FIELD_POSTBACK_TARGET,$key);  // not calling setPostBackEventTarget() because the control may be removed later
868
				}
869
				unset($this->_controlsRequiringPostData[$key]);
870
			}
871
			else if($beforeLoad)
872
				$this->_restPostData->add($key,$value);
873
		}
874
 
875
		foreach($this->_controlsRequiringPostData as $key=>$value)
876
		{
877
			if($control=$this->findControl($key))
878
			{
879
				if($control instanceof IPostBackDataHandler)
880
				{
881
					if($control->loadPostData($key,$this->_postData))
882
						$this->_controlsPostDataChanged[]=$control;
883
				}
884
				else
885
					throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key);
886
				unset($this->_controlsRequiringPostData[$key]);
887
			}
888
		}
889
		$this->_isLoadingPostData=false;
890
	}
891
 
892
	/**
893
	 * @return boolean true if loading post data.
894
	 */
895
	public function getIsLoadingPostData()
896
	{
897
		return $this->_isLoadingPostData;
898
	}
899
 
900
	/**
901
	 * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback.
902
	 */
903
	private function raiseChangedEvents()
904
	{
905
		foreach($this->_controlsPostDataChanged as $control)
906
			$control->raisePostDataChangedEvent();
907
	}
908
 
909
	/**
910
	 * Raises PostBack event.
911
	 */
912
	private function raisePostBackEvent()
913
	{
914
		if(($postBackHandler=$this->getPostBackEventTarget())===null)
915
			$this->validate();
916
		else if($postBackHandler instanceof IPostBackEventHandler)
917
			$postBackHandler->raisePostBackEvent($this->getPostBackEventParameter());
918
	}
919
 
920
	/**
921
	 * Ensures the control is rendered within a form.
922
	 * @param TControl the control to be rendered
923
	 * @throws TConfigurationException if the control is outside of the form
924
	 */
925
	public function ensureRenderInForm($control)
926
	{
927
		if(!$this->getIsCallback() && !$this->_inFormRender)
928
			throw new TConfigurationException('page_control_outofform',get_class($control),$control->getUniqueID());
929
	}
930
 
931
	/**
932
	 * @internal This method is invoked by TForm at the beginning of its rendering
933
	 */
934
	public function beginFormRender($writer)
935
	{
936
		if($this->_formRendered)
937
			throw new TConfigurationException('page_form_duplicated');
938
		$this->_formRendered=true;
939
		$this->_inFormRender=true;
940
		$this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState());
941
	}
942
 
943
	/**
944
	 * @internal This method is invoked by TForm  at the end of its rendering
945
	 */
946
	public function endFormRender($writer)
947
	{
948
		if($this->_focus)
949
		{
950
			if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true))
951
				$focus=$this->_focus->getClientID();
952
			else
953
				$focus=$this->_focus;
954
			$this->getClientScript()->registerFocusControl($focus);
955
		}
956
		else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null)
957
			$this->getClientScript()->registerFocusControl($lastFocus);
958
		$this->_inFormRender=false;
959
	}
960
 
961
	/**
962
	 * Sets input focus on a control after the page is rendered to users.
963
	 * @param TControl|string control to receive focus, or the ID of the element on the page to receive focus
964
	 */
965
	public function setFocus($value)
966
	{
967
		$this->_focus=$value;
968
	}
969
 
970
	/**
971
	 * @return boolean whether client supports javascript. Defaults to true.
972
	 */
973
	public function getClientSupportsJavaScript()
974
	{
975
		return $this->_enableJavaScript;
976
	}
977
 
978
	/**
979
	 * @param boolean whether client supports javascript. If false, javascript will not be generated for controls.
980
	 */
981
	public function setClientSupportsJavaScript($value)
982
	{
983
		$this->_enableJavaScript=TPropertyValue::ensureBoolean($value);
984
	}
985
 
986
	/**
987
	 * @return THead page head, null if not available
988
	 */
989
	public function getHead()
990
	{
991
		return $this->_head;
992
	}
993
 
994
	/**
995
	 * @param THead page head
996
	 * @throws TInvalidOperationException if a head already exists
997
	 */
998
	public function setHead(THead $value)
999
	{
1000
		if($this->_head)
1001
			throw new TInvalidOperationException('page_head_duplicated');
1002
		$this->_head=$value;
1003
		if($this->_title!==null)
1004
		{
1005
			$this->_head->setTitle($this->_title);
1006
			$this->_title=null;
1007
		}
1008
	}
1009
 
1010
	/**
1011
	 * @return string page title.
1012
	 */
1013
	public function getTitle()
1014
	{
1015
		if($this->_head)
1016
			return $this->_head->getTitle();
1017
		else
1018
			return $this->_title===null ? '' : $this->_title;
1019
	}
1020
 
1021
	/**
1022
	 * Sets the page title.
1023
	 * Note, a {@link THead} control needs to place on the page
1024
	 * in order that this title be rendered.
1025
	 * @param string page title. This will override the title set in {@link getHead Head}.
1026
	 */
1027
	public function setTitle($value)
1028
	{
1029
		if($this->_head)
1030
			$this->_head->setTitle($value);
1031
		else
1032
			$this->_title=$value;
1033
	}
1034
 
1035
	/**
1036
	 * Returns the state to be stored on the client side.
1037
	 * This method should only be used by framework and control developers.
1038
	 * @return string the state to be stored on the client side
1039
	 */
1040
	public function getClientState()
1041
	{
1042
		return $this->_clientState;
1043
	}
1044
 
1045
	/**
1046
	 * Sets the state to be stored on the client side.
1047
	 * This method should only be used by framework and control developers.
1048
	 * @param string the state to be stored on the client side
1049
	 */
1050
	public function setClientState($state)
1051
	{
1052
		$this->_clientState=$state;
1053
	}
1054
 
1055
	/**
1056
	 * @return string the state postback from client side
1057
	 */
1058
	public function getRequestClientState()
1059
	{
1060
		return $this->getRequest()->itemAt(self::FIELD_PAGESTATE);
1061
	}
1062
 
1063
	/**
1064
	 * @return string class name of the page state persister. Defaults to TPageStatePersister.
1065
	 */
1066
	public function getStatePersisterClass()
1067
	{
1068
		return $this->_statePersisterClass;
1069
	}
1070
 
1071
	/**
1072
	 * @param string class name of the page state persister.
1073
	 */
1074
	public function setStatePersisterClass($value)
1075
	{
1076
		$this->_statePersisterClass=$value;
1077
	}
1078
 
1079
	/**
1080
	 * @return IPageStatePersister page state persister
1081
	 */
1082
	public function getStatePersister()
1083
	{
1084
		if($this->_statePersister===null)
1085
		{
1086
			$this->_statePersister=Prado::createComponent($this->_statePersisterClass);
1087
			if(!($this->_statePersister instanceof IPageStatePersister))
1088
				throw new TInvalidDataTypeException('page_statepersister_invalid');
1089
			$this->_statePersister->setPage($this);
1090
		}
1091
		return $this->_statePersister;
1092
	}
1093
 
1094
	/**
1095
	 * @return boolean whether page state should be HMAC validated. Defaults to true.
1096
	 */
1097
	public function getEnableStateValidation()
1098
	{
1099
		return $this->_enableStateValidation;
1100
	}
1101
 
1102
	/**
1103
	 * @param boolean whether page state should be HMAC validated.
1104
	 */
1105
	public function setEnableStateValidation($value)
1106
	{
1107
		$this->_enableStateValidation=TPropertyValue::ensureBoolean($value);
1108
	}
1109
 
1110
	/**
1111
	 * @return boolean whether page state should be encrypted. Defaults to false.
1112
	 */
1113
	public function getEnableStateEncryption()
1114
	{
1115
		return $this->_enableStateEncryption;
1116
	}
1117
 
1118
	/**
1119
	 * @param boolean whether page state should be encrypted.
1120
	 */
1121
	public function setEnableStateEncryption($value)
1122
	{
1123
		$this->_enableStateEncryption=TPropertyValue::ensureBoolean($value);
1124
	}
1125
 
1126
	/**
1127
	 * @return string the requested page path for this page
1128
	 */
1129
	public function getPagePath()
1130
	{
1131
		return $this->_pagePath;
1132
	}
1133
 
1134
	/**
1135
	 * @param string the requested page path for this page
1136
	 */
1137
	public function setPagePath($value)
1138
	{
1139
		$this->_pagePath=$value;
1140
	}
1141
 
1142
	/**
1143
	 * Registers an action associated with the content being cached.
1144
	 * The registered action will be replayed if the content stored
1145
	 * in the cache is served to end-users.
1146
	 * @param string context of the action method. This is a property-path
1147
	 * referring to the context object (e.g. Page, Page.ClientScript).
1148
	 * @param string method name of the context object
1149
	 * @param array list of parameters to be passed to the action method
1150
	 */
1151
	public function registerCachingAction($context,$funcName,$funcParams)
1152
	{
1153
		if($this->_cachingStack)
1154
		{
1155
			foreach($this->_cachingStack as $cache)
1156
				$cache->registerAction($context,$funcName,$funcParams);
1157
		}
1158
	}
1159
 
1160
	/**
1161
	 * @return TStack stack of {@link TOutputCache} objects
1162
	 */
1163
	public function getCachingStack()
1164
	{
1165
		if(!$this->_cachingStack)
1166
			$this->_cachingStack=new TStack;
1167
		return $this->_cachingStack;
1168
	}
1169
}
1170
 
1171
/**
1172
 * IPageStatePersister interface.
1173
 *
1174
 * IPageStatePersister interface is required for all page state persister
1175
 * classes.
1176
 *
1177
 * @author Qiang Xue <qiang.xue@gmail.com>
1178
 * @version $Id: TPage.php 2523 2008-10-14 14:05:27Z mikl $
1179
 * @package System.Web.UI
1180
 * @since 3.1
1181
 */
1182
interface IPageStatePersister
1183
{
1184
	/**
1185
	 * @param TPage the page that this persister works for
1186
	 */
1187
	public function getPage();
1188
	/**
1189
	 * @param TPage the page that this persister works for
1190
	 */
1191
	public function setPage(TPage $page);
1192
	/**
1193
	 * Saves state to persistent storage.
1194
	 * @param mixed state to be stored
1195
	 */
1196
	public function save($state);
1197
	/**
1198
	 * Loads page state from persistent storage
1199
	 * @return mixed the restored state
1200
	 */
1201
	public function load();
1202
}
1203
 
1204
 
1205
/**
1206
 * TPageStateFormatter class.
1207
 *
1208
 * TPageStateFormatter is a utility class to transform the page state
1209
 * into and from a string that can be properly saved in persistent storage.
1210
 *
1211
 * Depending on the {@link TPage::getEnableStateValidation() EnableStateValidation}
1212
 * and {@link TPage::getEnableStateEncryption() EnableStateEncryption},
1213
 * TPageStateFormatter may do HMAC validation and encryption to prevent
1214
 * the state data from being tampered or viewed.
1215
 * The private keys and hashing/encryption methods are determined by
1216
 * {@link TApplication::getSecurityManager() SecurityManager}.
1217
 *
1218
 * @author Qiang Xue <qiang.xue@gmail.com>
1219
 * @version $Revision: $  $Date: $
1220
 * @package System.Web.UI
1221
 * @since 3.1
1222
 */
1223
class TPageStateFormatter
1224
{
1225
	/**
1226
	 * @param TPage
1227
	 * @param mixed state data
1228
	 * @return string serialized data
1229
	 */
1230
	public static function serialize($page,$data)
1231
	{
1232
		$sm=$page->getApplication()->getSecurityManager();
1233
		if($page->getEnableStateValidation())
1234
			$str=$sm->hashData(Prado::serialize($data));
1235
		else
1236
			$str=Prado::serialize($data);
1237
		if(extension_loaded('zlib'))
1238
			$str=gzcompress($str);
1239
		if($page->getEnableStateEncryption())
1240
			$str=$sm->encrypt($str);
1241
		return base64_encode($str);
1242
	}
1243
 
1244
	/**
1245
	 * @param TPage
1246
	 * @param string serialized data
1247
	 * @return mixed unserialized state data, null if data is corrupted
1248
	 */
1249
	public static function unserialize($page,$data)
1250
	{
1251
		$str=base64_decode($data);
1252
		if($str==='')
1253
			return null;
1254
		if($str!==false)
1255
		{
1256
			$sm=$page->getApplication()->getSecurityManager();
1257
			if($page->getEnableStateEncryption())
1258
				$str=$sm->decrypt($str);
1259
			if(extension_loaded('zlib'))
1260
				$str=@gzuncompress($str);
1261
			if($page->getEnableStateValidation())
1262
			{
1263
				if(($str=$sm->validateData($str))!==false)
1264
					return Prado::unserialize($str);
1265
			}
1266
			else
1267
				return $str;
1268
		}
1269
		return null;
1270
	}
1271
}
1272
 
1273
?>