root/trunk/framework/Web/UI/TPage.php

Revision 2523, 36.3 kB (checked in by mikl, 5 weeks ago)

Fixed #938

  • Property svn:keywords set to Id
Line 
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$
10 * @package System.Web.UI
11 */
12
13Prado::using('System.Web.UI.WebControls.*');
14Prado::using('System.Web.UI.TControl');
15Prado::using('System.Web.UI.WebControls.TWebControl');
16Prado::using('System.Web.UI.TCompositeControl');
17Prado::using('System.Web.UI.TTemplateControl');
18Prado::using('System.Web.UI.TForm');
19Prado::using('System.Web.UI.TClientScriptManager');
20
21/**
22 * TPage class
23 *
24 * @author Qiang Xue <qiang.xue@gmail.com>
25 * @version $Id$
26 * @package System.Web.UI
27 * @since 3.0
28 */
29class 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