From 22a864832abfecfba95e3a14645efa537d9405e6 Mon Sep 17 00:00:00 2001 From: usrcampusoft07 Date: Fri, 17 Oct 2014 17:49:10 -0500 Subject: Adding functions to update view controls and AR objects automagicaly... --- framework/Web/UI/TPage.php | 2650 ++++++++++++++++++++++---------------------- 1 file changed, 1334 insertions(+), 1316 deletions(-) (limited to 'framework/Web/UI/TPage.php') diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index 2dc1cac3..fa06cd03 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -1,1316 +1,1334 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2014 PradoSoft - * @license http://www.pradosoft.com/license/ - * @package System.Web.UI - */ - -Prado::using('System.Web.UI.WebControls.*'); -Prado::using('System.Web.UI.TControl'); -Prado::using('System.Web.UI.WebControls.TWebControl'); -Prado::using('System.Web.UI.TCompositeControl'); -Prado::using('System.Web.UI.TTemplateControl'); -Prado::using('System.Web.UI.TForm'); -Prado::using('System.Web.UI.TClientScriptManager'); - -/** - * TPage class - * - * @author Qiang Xue - * @package System.Web.UI - * @since 3.0 - */ -class TPage extends TTemplateControl -{ - /** - * system post fields - */ - const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; - const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; - const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; - const FIELD_PAGESTATE='PRADO_PAGESTATE'; - const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; - const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; - - /** - * @var array system post fields - */ - private static $_systemPostFields=array( - 'PRADO_POSTBACK_TARGET'=>true, - 'PRADO_POSTBACK_PARAMETER'=>true, - 'PRADO_LASTFOCUS'=>true, - 'PRADO_PAGESTATE'=>true, - 'PRADO_CALLBACK_TARGET'=>true, - 'PRADO_CALLBACK_PARAMETER'=>true - ); - /** - * @var TForm form instance - */ - private $_form; - /** - * @var THead head instance - */ - private $_head; - /** - * @var array list of registered validators - */ - private $_validators=array(); - /** - * @var boolean if validation has been performed - */ - private $_validated=false; - /** - * @var TTheme page theme - */ - private $_theme; - /** - * @var string page title set when Head is not in page yet - */ - private $_title; - /** - * @var TTheme page stylesheet theme - */ - private $_styleSheet; - /** - * @var TClientScriptManager client script manager - */ - private $_clientScript; - /** - * @var TMap data post back by user - */ - protected $_postData; - /** - * @var TMap postback data that is not handled during first invocation of LoadPostData. - */ - protected $_restPostData; - /** - * @var array list of controls whose data have been changed due to the postback - */ - protected $_controlsPostDataChanged=array(); - /** - * @var array list of controls that need to load post data in the current request - */ - protected $_controlsRequiringPostData=array(); - /** - * @var array list of controls that need to load post data in the next postback - */ - protected $_controlsRegisteredForPostData=array(); - /** - * @var TControl control that needs to raise postback event - */ - private $_postBackEventTarget; - /** - * @var string postback event parameter - */ - private $_postBackEventParameter; - /** - * @var boolean whether the form has been rendered - */ - protected $_formRendered=false; - /** - * @var boolean whether the current rendering is within a form - */ - protected $_inFormRender=false; - /** - * @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 - */ - private $_focus; - /** - * @var string page path to this page - */ - private $_pagePath=''; - /** - * @var boolean whether page state should be HMAC validated - */ - private $_enableStateValidation=true; - /** - * @var boolean whether page state should be encrypted - */ - private $_enableStateEncryption=false; - /** - * @var boolean whether page state should be compressed - * @since 3.1.6 - */ - private $_enableStateCompression=true; - /** - * @var string page state persister class name - */ - private $_statePersisterClass='System.Web.UI.TPageStatePersister'; - /** - * @var mixed page state persister - */ - private $_statePersister; - /** - * @var TStack stack used to store currently active caching controls - */ - private $_cachingStack; - /** - * @var string state string to be stored on the client side - */ - private $_clientState=''; - /** - * @var boolean true if loading post data. - */ - protected $_isLoadingPostData=false; - /** - * @var boolean whether client supports javascript - */ - private $_enableJavaScript=true; - /** - * @var THtmlWriter current html render writer - */ - private $_writer; - - /** - * Constructor. - * Sets the page object to itself. - * Derived classes must call parent implementation. - */ - public function __construct() - { - $this->setPage($this); - } - - /** - * Runs through the page lifecycles. - * @param THtmlTextWriter the HTML writer - */ - public function run($writer) - { - Prado::trace("Running page life cycles",'System.Web.UI.TPage'); - $this->_writer = $writer; - - $this->determinePostBackMode(); - - if($this->getIsPostBack()) - { - if($this->getIsCallback()) - $this->processCallbackRequest($writer); - else - $this->processPostBackRequest($writer); - } - else - $this->processNormalRequest($writer); - - $this->_writer = null; - } - - protected function processNormalRequest($writer) - { - Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); - $this->onPreInit(null); - - Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); - $this->initRecursive(); - - Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); - $this->onInitComplete(null); - - Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); - $this->onPreLoad(null); - Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); - $this->loadRecursive(); - Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); - $this->onLoadComplete(null); - - Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); - $this->preRenderRecursive(); - Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); - $this->onPreRenderComplete(null); - - Prado::trace("Page savePageState()",'System.Web.UI.TPage'); - $this->savePageState(); - Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); - $this->onSaveStateComplete(null); - - Prado::trace("Page renderControl()",'System.Web.UI.TPage'); - $this->renderControl($writer); - Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); - $this->unloadRecursive(); - } - - protected function processPostBackRequest($writer) - { - Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); - $this->onPreInit(null); - - Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); - $this->initRecursive(); - - Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); - $this->onInitComplete(null); - - $this->_restPostData=new TMap; - Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); - $this->loadPageState(); - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_postData,true); - Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); - $this->onPreLoad(null); - Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); - $this->loadRecursive(); - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_restPostData,false); - Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); - $this->raiseChangedEvents(); - Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); - $this->raisePostBackEvent(); - Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); - $this->onLoadComplete(null); - - Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); - $this->preRenderRecursive(); - Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); - $this->onPreRenderComplete(null); - - Prado::trace("Page savePageState()",'System.Web.UI.TPage'); - $this->savePageState(); - Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); - $this->onSaveStateComplete(null); - - Prado::trace("Page renderControl()",'System.Web.UI.TPage'); - $this->renderControl($writer); - Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); - $this->unloadRecursive(); - } - - protected static function decodeUTF8($data, $enc) - { - if(is_array($data)) - { - foreach($data as $k=>$v) - $data[$k]=self::decodeUTF8($v, $enc); - return $data; - } elseif(is_string($data)) { - return iconv('UTF-8',$enc.'//IGNORE',$data); - } else { - return $data; - } - } - - /** - * Sets Adapter to TActivePageAdapter and calls apter to process the - * callback request. - */ - protected function processCallbackRequest($writer) - { - Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); - - $this->setAdapter(new TActivePageAdapter($this)); - - $callbackEventParameter = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); - if(strlen($callbackEventParameter) > 0) - $this->_postData[TPage::FIELD_CALLBACK_PARAMETER]=TJavaScript::jsonDecode((string)$callbackEventParameter); - - // Decode Callback postData from UTF-8 to current Charset - if (($g=$this->getApplication()->getGlobalization(false))!==null && - strtoupper($enc=$g->getCharset())!='UTF-8') - foreach ($this->_postData as $k=>$v) - $this->_postData[$k]=self::decodeUTF8($v, $enc); - - Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); - $this->onPreInit(null); - - Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); - $this->initRecursive(); - - Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); - $this->onInitComplete(null); - - $this->_restPostData=new TMap; - Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); - $this->loadPageState(); - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_postData,true); - Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); - $this->onPreLoad(null); - Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); - $this->loadRecursive(); - - Prado::trace("Page processPostData()",'System.Web.UI.TPage'); - $this->processPostData($this->_restPostData,false); - - Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); - $this->raiseChangedEvents(); - - - $this->getAdapter()->processCallbackEvent($writer); - -/* - Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); - $this->raisePostBackEvent(); -*/ - Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); - $this->onLoadComplete(null); - - Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); - $this->preRenderRecursive(); - Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); - $this->onPreRenderComplete(null); - - Prado::trace("Page savePageState()",'System.Web.UI.TPage'); - $this->savePageState(); - Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); - $this->onSaveStateComplete(null); - -/* - Prado::trace("Page renderControl()",'System.Web.UI.TPage'); - $this->renderControl($writer); -*/ - $this->getAdapter()->renderCallbackResponse($writer); - - Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); - $this->unloadRecursive(); - } - - /** - * Gets the callback client script handler that allows javascript functions - * to be executed during the callback response. - * @return TCallbackClientScript interface to client-side javascript code. - */ - public function getCallbackClient() - { - if($this->getAdapter() !== null) - return $this->getAdapter()->getCallbackClientHandler(); - else - return new TCallbackClientScript(); - } - - /** - * Set a new callback client handler. - * @param TCallbackClientScript new callback client script handler. - */ - public function setCallbackClient($client) - { - $this->getAdapter()->setCallbackClientHandler($client); - } - - /** - * @return TControl the control responsible for the current callback event, - * null if nonexistent - */ - public function getCallbackEventTarget() - { - return $this->getAdapter()->getCallbackEventTarget(); - } - - /** - * Registers a control to raise callback event in the current request. - * @param TControl control registered to raise callback event. - */ - public function setCallbackEventTarget(TControl $control) - { - $this->getAdapter()->setCallbackEventTarget($control); - } - - /** - * Callback parameter is decoded assuming JSON encoding. - * @return string callback event parameter - */ - public function getCallbackEventParameter() - { - return $this->getAdapter()->getCallbackEventParameter(); - } - - /** - * @param mixed callback event parameter - */ - public function setCallbackEventParameter($value) - { - $this->getAdapter()->setCallbackEventParameter($value); - } - - /** - * @return TForm the form on the page - */ - public function getForm() - { - return $this->_form; - } - - /** - * Registers a TForm instance to the page. - * Note, a page can contain at most one TForm instance. - * @param TForm the form on the page - * @throws TInvalidOperationException if this method is invoked twice or more. - */ - public function setForm(TForm $form) - { - if($this->_form===null) - $this->_form=$form; - else - throw new TInvalidOperationException('page_form_duplicated'); - } - - /** - * Returns a list of registered validators. - * If validation group is specified, only the validators in that group will be returned. - * @param string validation group - * @return TList registered validators in the requested group. If the group is null, all validators will be returned. - */ - public function getValidators($validationGroup=null) - { - if(!$this->_validators) - $this->_validators=new TList; - if(empty($validationGroup) === true) - return $this->_validators; - else - { - $list=new TList; - foreach($this->_validators as $validator) - if($validator->getValidationGroup()===$validationGroup) - $list->add($validator); - return $list; - } - } - - /** - * Performs input validation. - * This method will invoke the registered validators to perform the actual validation. - * If validation group is specified, only the validators in that group will be invoked. - * @param string validation group. If null, all validators will perform validation. - */ - public function validate($validationGroup=null) - { - Prado::trace("Page validate()",'System.Web.UI.TPage'); - $this->_validated=true; - if($this->_validators && $this->_validators->getCount()) - { - if($validationGroup===null) - { - foreach($this->_validators as $validator) - $validator->validate(); - } - else - { - foreach($this->_validators as $validator) - { - if($validator->getValidationGroup()===$validationGroup) - $validator->validate(); - } - } - } - } - - /** - * Returns whether user input is valid or not. - * This method must be invoked after {@link validate} is called. - * @return boolean whether the user input is valid or not. - * @throws TInvalidOperationException if {@link validate} is not invoked yet. - */ - public function getIsValid() - { - if($this->_validated) - { - if($this->_validators && $this->_validators->getCount()) - { - foreach($this->_validators as $validator) - if(!$validator->getIsValid()) - return false; - } - return true; - } - else - throw new TInvalidOperationException('page_isvalid_unknown'); - } - - /** - * @return TTheme the theme used for the page. Defaults to null. - */ - public function getTheme() - { - if(is_string($this->_theme)) - $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); - return $this->_theme; - } - - /** - * Sets the theme to be used for the page. - * @param string|TTheme the theme name or the theme object to be used for the page. - */ - public function setTheme($value) - { - $this->_theme=empty($value)?null:$value; - } - - - /** - * @return TTheme the stylesheet theme used for the page. Defaults to null. - */ - public function getStyleSheetTheme() - { - if(is_string($this->_styleSheet)) - $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); - return $this->_styleSheet; - } - - /** - * Sets the stylesheet theme to be used for the page. - * @param string|TTheme the stylesheet theme name or the stylesheet theme object to be used for the page. - */ - public function setStyleSheetTheme($value) - { - $this->_styleSheet=empty($value)?null:$value; - } - - /** - * Applies a skin in the current theme to a control. - * This method should only be used by framework developers. - * @param TControl a control to be applied skin with - */ - public function applyControlSkin($control) - { - if(($theme=$this->getTheme())!==null) - $theme->applySkin($control); - } - - /** - * Applies a stylesheet skin in the current theme to a control. - * This method should only be used by framework developers. - * @param TControl a control to be applied stylesheet skin with - */ - public function applyControlStyleSheet($control) - { - if(($theme=$this->getStyleSheetTheme())!==null) - $theme->applySkin($control); - } - - /** - * @return TClientScriptManager client script manager - */ - public function getClientScript() - { - if(!$this->_clientScript) { - $className = $classPath = $this->getService()->getClientScriptManagerClass(); - Prado::using($className); - if(($pos=strrpos($className,'.'))!==false) - $className=substr($className,$pos+1); - - if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) - throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); - - $this->_clientScript=new $className($this); - } - return $this->_clientScript; - } - - /** - * Raises OnPreInit event. - * This method is invoked right before {@link onInit OnInit} stage. - * You may override this method to provide additional initialization that - * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or - * {@link setStyleSheetTheme StyleSheetTheme}). - * Remember to call the parent implementation to ensure OnPreInit event is raised. - * @param mixed event parameter - */ - public function onPreInit($param) - { - $this->raiseEvent('OnPreInit',$this,$param); - } - - /** - * Raises OnInitComplete event. - * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage. - * You may override this method to provide additional initialization that - * should be done after {@link onInit OnInit}. - * Remember to call the parent implementation to ensure OnInitComplete event is raised. - * @param mixed event parameter - */ - public function onInitComplete($param) - { - $this->raiseEvent('OnInitComplete',$this,$param); - } - - /** - * Raises OnPreLoad event. - * This method is invoked right before {@link onLoad OnLoad} stage. - * You may override this method to provide additional page loading logic that - * should be done before {@link onLoad OnLoad}. - * Remember to call the parent implementation to ensure OnPreLoad event is raised. - * @param mixed event parameter - */ - public function onPreLoad($param) - { - $this->raiseEvent('OnPreLoad',$this,$param); - } - - /** - * Raises OnLoadComplete event. - * This method is invoked right after {@link onLoad OnLoad} stage. - * You may override this method to provide additional page loading logic that - * should be done after {@link onLoad OnLoad}. - * Remember to call the parent implementation to ensure OnLoadComplete event is raised. - * @param mixed event parameter - */ - public function onLoadComplete($param) - { - $this->raiseEvent('OnLoadComplete',$this,$param); - } - - /** - * Raises OnPreRenderComplete event. - * This method is invoked right after {@link onPreRender OnPreRender} stage. - * You may override this method to provide additional preparation for page rendering - * that should be done after {@link onPreRender OnPreRender}. - * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised. - * @param mixed event parameter - */ - public function onPreRenderComplete($param) - { - $this->raiseEvent('OnPreRenderComplete',$this,$param); - $cs=$this->getClientScript(); - $theme=$this->getTheme(); - if($theme instanceof ITheme) - { - foreach($theme->getStyleSheetFiles() as $url) - $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); - foreach($theme->getJavaScriptFiles() as $url) - $cs->registerHeadScriptFile($url,$url); - } - $styleSheet=$this->getStyleSheetTheme(); - if($styleSheet instanceof ITheme) - { - foreach($styleSheet->getStyleSheetFiles() as $url) - $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); - foreach($styleSheet->getJavaScriptFiles() as $url) - $cs->registerHeadScriptFile($url,$url); - } - - if($cs->getRequiresHead() && $this->getHead()===null) - throw new TConfigurationException('page_head_required'); - } - - /** - * Determines the media type of the CSS file. - * The media type is determined according to the following file name pattern: - * xxx.media-type.extension - * For example, 'mystyle.print.css' means its media type is 'print'. - * @param string CSS URL - * @return string media type of the CSS file - */ - private function getCssMediaType($url) - { - $segs=explode('.',basename($url)); - if(isset($segs[2])) - return $segs[count($segs)-2]; - else - return ''; - } - - /** - * Raises OnSaveStateComplete event. - * This method is invoked right after {@link onSaveState OnSaveState} stage. - * You may override this method to provide additional logic after page state is saved. - * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised. - * @param mixed event parameter - */ - public function onSaveStateComplete($param) - { - $this->raiseEvent('OnSaveStateComplete',$this,$param); - } - - /** - * Determines whether the current page request is a postback. - * Call {@link getIsPostBack} to get the result. - */ - private function determinePostBackMode() - { - $postData=$this->getRequest(); - if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) - $this->_postData=$postData; - } - - /** - * @return boolean whether the current page request is a postback - */ - public function getIsPostBack() - { - return $this->_postData!==null; - } - - /** - * @return boolean whether this is a callback request - */ - public function getIsCallback() - { - return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); - } - - /** - * This method is invoked when control state is to be saved. - * You can override this method to do last step state saving. - * Parent implementation must be invoked. - */ - public function saveState() - { - parent::saveState(); - $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); - } - - /** - * This method is invoked right after the control has loaded its state. - * You can override this method to initialize data from the control state. - * Parent implementation must be invoked. - */ - public function loadState() - { - parent::loadState(); - $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); - } - - /** - * Loads page state from persistent storage. - */ - protected function loadPageState() - { - Prado::trace("Loading state",'System.Web.UI.TPage'); - $state=$this->getStatePersister()->load(); - $this->loadStateRecursive($state,$this->getEnableViewState()); - } - - /** - * Saves page state from persistent storage. - */ - protected function savePageState() - { - Prado::trace("Saving state",'System.Web.UI.TPage'); - $state=&$this->saveStateRecursive($this->getEnableViewState()); - $this->getStatePersister()->save($state); - } - - /** - * @param string the field name - * @return boolean whether the specified field is a system field in postback data - */ - protected function isSystemPostField($field) - { - return isset(self::$_systemPostFields[$field]); - } - - /** - * Registers a control for loading post data in the next postback. - * This method needs to be invoked if the control to load post data - * may not have a post variable in some cases. For example, a checkbox, - * if not checked, will not have a post value. - * @param TControl control registered for loading post data - */ - public function registerRequiresPostData($control) - { - $id=is_string($control)?$control:$control->getUniqueID(); - $this->_controlsRegisteredForPostData[$id]=true; - $params=func_get_args(); - foreach($this->getCachingStack() as $item) - $item->registerAction('Page','registerRequiresPostData',array($id)); - } - - /** - * @return TControl the control responsible for the current postback event, null if nonexistent - */ - public function getPostBackEventTarget() - { - if($this->_postBackEventTarget===null && $this->_postData!==null) - { - $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); - if(!empty($eventTarget)) - $this->_postBackEventTarget=$this->findControl($eventTarget); - } - return $this->_postBackEventTarget; - } - - /** - * Registers a control to raise postback event in the current request. - * @param TControl control registered to raise postback event. - */ - public function setPostBackEventTarget(TControl $control) - { - $this->_postBackEventTarget=$control; - } - - /** - * @return string postback event parameter - */ - public function getPostBackEventParameter() - { - if($this->_postBackEventParameter===null && $this->_postData!==null) - { - if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) - $this->_postBackEventParameter=''; - } - return $this->_postBackEventParameter; - } - - /** - * @param string postback event parameter - */ - public function setPostBackEventParameter($value) - { - $this->_postBackEventParameter=$value; - } - - /** - * Processes post data. - * @param TMap post data to be processed - * @param boolean whether this method is invoked before {@link onLoad OnLoad}. - */ - protected function processPostData($postData,$beforeLoad) - { - $this->_isLoadingPostData=true; - if($beforeLoad) - $this->_restPostData=new TMap; - foreach($postData as $key=>$value) - { - if($this->isSystemPostField($key)) - continue; - else if($control=$this->findControl($key)) - { - if($control instanceof IPostBackDataHandler) - { - if($control->loadPostData($key,$postData)) - $this->_controlsPostDataChanged[]=$control; - } - else if($control instanceof IPostBackEventHandler && - empty($this->_postData[self::FIELD_POSTBACK_TARGET])) - { - $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); // not calling setPostBackEventTarget() because the control may be removed later - } - unset($this->_controlsRequiringPostData[$key]); - } - else if($beforeLoad) - $this->_restPostData->add($key,$value); - } - - foreach($this->_controlsRequiringPostData as $key=>$value) - { - if($control=$this->findControl($key)) - { - if($control instanceof IPostBackDataHandler) - { - if($control->loadPostData($key,$this->_postData)) - $this->_controlsPostDataChanged[]=$control; - } - else - throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); - unset($this->_controlsRequiringPostData[$key]); - } - } - $this->_isLoadingPostData=false; - } - - /** - * @return boolean true if loading post data. - */ - public function getIsLoadingPostData() - { - return $this->_isLoadingPostData; - } - - /** - * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback. - */ - protected function raiseChangedEvents() - { - foreach($this->_controlsPostDataChanged as $control) - $control->raisePostDataChangedEvent(); - } - - /** - * Raises PostBack event. - */ - protected function raisePostBackEvent() - { - if(($postBackHandler=$this->getPostBackEventTarget())===null) - $this->validate(); - else if($postBackHandler instanceof IPostBackEventHandler) - $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); - } - - /** - * @return boolean Whether form rendering is in progress - */ - public function getInFormRender() - { - return $this->_inFormRender; - } - - /** - * Ensures the control is rendered within a form. - * @param TControl the control to be rendered - * @throws TConfigurationException if the control is outside of the form - */ - public function ensureRenderInForm($control) - { - if(!$this->getIsCallback() && !$this->_inFormRender) - throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); - } - - /** - * @internal This method is invoked by TForm at the beginning of its rendering - */ - public function beginFormRender($writer) - { - if($this->_formRendered) - throw new TConfigurationException('page_form_duplicated'); - $this->_formRendered=true; - $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); - $this->_inFormRender=true; - } - - /** - * @internal This method is invoked by TForm at the end of its rendering - */ - public function endFormRender($writer) - { - if($this->_focus) - { - if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) - $focus=$this->_focus->getClientID(); - else - $focus=$this->_focus; - $this->getClientScript()->registerFocusControl($focus); - } - else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) - $this->getClientScript()->registerFocusControl($lastFocus); - $this->_inFormRender=false; - } - - /** - * Sets input focus on a control after the page is rendered to users. - * @param TControl|string control to receive focus, or the ID of the element on the page to receive focus - */ - public function setFocus($value) - { - $this->_focus=$value; - } - - /** - * @return boolean whether client supports javascript. Defaults to true. - */ - public function getClientSupportsJavaScript() - { - return $this->_enableJavaScript; - } - - /** - * @param boolean whether client supports javascript. If false, javascript will not be generated for controls. - */ - public function setClientSupportsJavaScript($value) - { - $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); - } - - /** - * @return THead page head, null if not available - */ - public function getHead() - { - return $this->_head; - } - - /** - * @param THead page head - * @throws TInvalidOperationException if a head already exists - */ - public function setHead(THead $value) - { - if($this->_head) - throw new TInvalidOperationException('page_head_duplicated'); - $this->_head=$value; - if($this->_title!==null) - { - $this->_head->setTitle($this->_title); - $this->_title=null; - } - } - - /** - * @return string page title. - */ - public function getTitle() - { - if($this->_head) - return $this->_head->getTitle(); - else - return $this->_title===null ? '' : $this->_title; - } - - /** - * Sets the page title. - * Note, a {@link THead} control needs to place on the page - * in order that this title be rendered. - * @param string page title. This will override the title set in {@link getHead Head}. - */ - public function setTitle($value) - { - if($this->_head) - $this->_head->setTitle($value); - else - $this->_title=$value; - } - - /** - * Returns the state to be stored on the client side. - * This method should only be used by framework and control developers. - * @return string the state to be stored on the client side - */ - public function getClientState() - { - return $this->_clientState; - } - - /** - * Sets the state to be stored on the client side. - * This method should only be used by framework and control developers. - * @param string the state to be stored on the client side - */ - public function setClientState($state) - { - $this->_clientState=$state; - } - - /** - * @return string the state postback from client side - */ - public function getRequestClientState() - { - return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); - } - - /** - * @return string class name of the page state persister. Defaults to TPageStatePersister. - */ - public function getStatePersisterClass() - { - return $this->_statePersisterClass; - } - - /** - * @param string class name of the page state persister. - */ - public function setStatePersisterClass($value) - { - $this->_statePersisterClass=$value; - } - - /** - * @return IPageStatePersister page state persister - */ - public function getStatePersister() - { - if($this->_statePersister===null) - { - $this->_statePersister=Prado::createComponent($this->_statePersisterClass); - if(!($this->_statePersister instanceof IPageStatePersister)) - throw new TInvalidDataTypeException('page_statepersister_invalid'); - $this->_statePersister->setPage($this); - } - return $this->_statePersister; - } - - /** - * @return boolean whether page state should be HMAC validated. Defaults to true. - */ - public function getEnableStateValidation() - { - return $this->_enableStateValidation; - } - - /** - * @param boolean whether page state should be HMAC validated. - */ - public function setEnableStateValidation($value) - { - $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether page state should be encrypted. Defaults to false. - */ - public function getEnableStateEncryption() - { - return $this->_enableStateEncryption; - } - - /** - * @param boolean whether page state should be encrypted. - */ - public function setEnableStateEncryption($value) - { - $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean whether page state should be compressed. Defaults to true. - * @since 3.1.6 - */ - public function getEnableStateCompression() - { - return $this->_enableStateCompression; - } - - /** - * @param boolean whether page state should be compressed. - * @since 3.1.6 - */ - public function setEnableStateCompression($value) - { - $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); - } - - /** - * @return string the requested page path for this page - */ - public function getPagePath() - { - return $this->_pagePath; - } - - /** - * @param string the requested page path for this page - */ - public function setPagePath($value) - { - $this->_pagePath=$value; - } - - /** - * Registers an action associated with the content being cached. - * The registered action will be replayed if the content stored - * in the cache is served to end-users. - * @param string context of the action method. This is a property-path - * referring to the context object (e.g. Page, Page.ClientScript). - * @param string method name of the context object - * @param array list of parameters to be passed to the action method - */ - public function registerCachingAction($context,$funcName,$funcParams) - { - if($this->_cachingStack) - { - foreach($this->_cachingStack as $cache) - $cache->registerAction($context,$funcName,$funcParams); - } - } - - /** - * @return TStack stack of {@link TOutputCache} objects - */ - public function getCachingStack() - { - if(!$this->_cachingStack) - $this->_cachingStack=new TStack; - return $this->_cachingStack; - } - - /** - * Flushes output - */ - public function flushWriter() - { - if ($this->_writer) - $this->Response->write($this->_writer->flush()); - } -} - -/** - * IPageStatePersister interface. - * - * IPageStatePersister interface is required for all page state persister - * classes. - * - * @author Qiang Xue - * @package System.Web.UI - * @since 3.1 - */ -interface IPageStatePersister -{ - /** - * @param TPage the page that this persister works for - */ - public function getPage(); - /** - * @param TPage the page that this persister works for - */ - public function setPage(TPage $page); - /** - * Saves state to persistent storage. - * @param mixed state to be stored - */ - public function save($state); - /** - * Loads page state from persistent storage - * @return mixed the restored state - */ - public function load(); -} - - -/** - * TPageStateFormatter class. - * - * TPageStateFormatter is a utility class to transform the page state - * into and from a string that can be properly saved in persistent storage. - * - * Depending on the {@link TPage::getEnableStateValidation() EnableStateValidation} - * and {@link TPage::getEnableStateEncryption() EnableStateEncryption}, - * TPageStateFormatter may do HMAC validation and encryption to prevent - * the state data from being tampered or viewed. - * The private keys and hashing/encryption methods are determined by - * {@link TApplication::getSecurityManager() SecurityManager}. - * - * @author Qiang Xue - * @version $Revision: $ $Date: $ - * @package System.Web.UI - * @since 3.1 - */ -class TPageStateFormatter -{ - /** - * @param TPage - * @param mixed state data - * @return string serialized data - */ - public static function serialize($page,$data) - { - $sm=$page->getApplication()->getSecurityManager(); - if($page->getEnableStateValidation()) - $str=$sm->hashData(serialize($data)); - else - $str=serialize($data); - if($page->getEnableStateCompression() && extension_loaded('zlib')) - $str=gzcompress($str); - if($page->getEnableStateEncryption()) - $str=$sm->encrypt($str); - return base64_encode($str); - } - - /** - * @param TPage - * @param string serialized data - * @return mixed unserialized state data, null if data is corrupted - */ - public static function unserialize($page,$data) - { - $str=base64_decode($data); - if($str==='') - return null; - if($str!==false) - { - $sm=$page->getApplication()->getSecurityManager(); - if($page->getEnableStateEncryption()) - $str=$sm->decrypt($str); - if($page->getEnableStateCompression() && extension_loaded('zlib')) - $str=@gzuncompress($str); - if($page->getEnableStateValidation()) - { - if(($str=$sm->validateData($str))!==false) - return unserialize($str); - } - else - return unserialize($str); - } - return null; - } -} + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2014 PradoSoft + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI + */ + +Prado::using('System.Web.UI.WebControls.*'); +Prado::using('System.Web.UI.TControl'); +Prado::using('System.Web.UI.WebControls.TWebControl'); +Prado::using('System.Web.UI.TCompositeControl'); +Prado::using('System.Web.UI.TTemplateControl'); +Prado::using('System.Web.UI.TForm'); +Prado::using('System.Web.UI.TClientScriptManager'); + +/** + * TPage class + * + * @author Qiang Xue + * @package System.Web.UI + * @since 3.0 + */ +class TPage extends TTemplateControl +{ + /** + * system post fields + */ + const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; + const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; + const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; + const FIELD_PAGESTATE='PRADO_PAGESTATE'; + const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; + const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; + + /** + * @var array system post fields + */ + private static $_systemPostFields=array( + 'PRADO_POSTBACK_TARGET'=>true, + 'PRADO_POSTBACK_PARAMETER'=>true, + 'PRADO_LASTFOCUS'=>true, + 'PRADO_PAGESTATE'=>true, + 'PRADO_CALLBACK_TARGET'=>true, + 'PRADO_CALLBACK_PARAMETER'=>true + ); + /** + * @var TForm form instance + */ + private $_form; + /** + * @var THead head instance + */ + private $_head; + /** + * @var array list of registered validators + */ + private $_validators=array(); + /** + * @var boolean if validation has been performed + */ + private $_validated=false; + /** + * @var TTheme page theme + */ + private $_theme; + /** + * @var string page title set when Head is not in page yet + */ + private $_title; + /** + * @var TTheme page stylesheet theme + */ + private $_styleSheet; + /** + * @var TClientScriptManager client script manager + */ + private $_clientScript; + /** + * @var TMap data post back by user + */ + protected $_postData; + /** + * @var TMap postback data that is not handled during first invocation of LoadPostData. + */ + protected $_restPostData; + /** + * @var array list of controls whose data have been changed due to the postback + */ + protected $_controlsPostDataChanged=array(); + /** + * @var array list of controls that need to load post data in the current request + */ + protected $_controlsRequiringPostData=array(); + /** + * @var array list of controls that need to load post data in the next postback + */ + protected $_controlsRegisteredForPostData=array(); + /** + * @var TControl control that needs to raise postback event + */ + private $_postBackEventTarget; + /** + * @var string postback event parameter + */ + private $_postBackEventParameter; + /** + * @var boolean whether the form has been rendered + */ + protected $_formRendered=false; + /** + * @var boolean whether the current rendering is within a form + */ + protected $_inFormRender=false; + /** + * @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 + */ + private $_focus; + /** + * @var string page path to this page + */ + private $_pagePath=''; + /** + * @var boolean whether page state should be HMAC validated + */ + private $_enableStateValidation=true; + /** + * @var boolean whether page state should be encrypted + */ + private $_enableStateEncryption=false; + /** + * @var boolean whether page state should be compressed + * @since 3.1.6 + */ + private $_enableStateCompression=true; + /** + * @var string page state persister class name + */ + private $_statePersisterClass='System.Web.UI.TPageStatePersister'; + /** + * @var mixed page state persister + */ + private $_statePersister; + /** + * @var TStack stack used to store currently active caching controls + */ + private $_cachingStack; + /** + * @var string state string to be stored on the client side + */ + private $_clientState=''; + /** + * @var boolean true if loading post data. + */ + protected $_isLoadingPostData=false; + /** + * @var boolean whether client supports javascript + */ + private $_enableJavaScript=true; + /** + * @var THtmlWriter current html render writer + */ + private $_writer; + + /** + * Constructor. + * Sets the page object to itself. + * Derived classes must call parent implementation. + */ + public function __construct() + { + $this->setPage($this); + } + + /** + * Runs through the page lifecycles. + * @param THtmlTextWriter the HTML writer + */ + public function run($writer) + { + Prado::trace("Running page life cycles",'System.Web.UI.TPage'); + $this->_writer = $writer; + + $this->determinePostBackMode(); + + if($this->getIsPostBack()) + { + if($this->getIsCallback()) + $this->processCallbackRequest($writer); + else + $this->processPostBackRequest($writer); + } + else + $this->processNormalRequest($writer); + + $this->_writer = null; + } + + protected function processNormalRequest($writer) + { + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + protected function processPostBackRequest($writer) + { + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + $this->_restPostData=new TMap; + Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); + $this->loadPageState(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_postData,true); + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_restPostData,false); + Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); + $this->raiseChangedEvents(); + Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); + $this->raisePostBackEvent(); + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + protected static function decodeUTF8($data, $enc) + { + if(is_array($data)) + { + foreach($data as $k=>$v) + $data[$k]=self::decodeUTF8($v, $enc); + return $data; + } elseif(is_string($data)) { + return iconv('UTF-8',$enc.'//IGNORE',$data); + } else { + return $data; + } + } + + /** + * Sets Adapter to TActivePageAdapter and calls apter to process the + * callback request. + */ + protected function processCallbackRequest($writer) + { + Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); + + $this->setAdapter(new TActivePageAdapter($this)); + + $callbackEventParameter = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); + if(strlen($callbackEventParameter) > 0) + $this->_postData[TPage::FIELD_CALLBACK_PARAMETER]=TJavaScript::jsonDecode((string)$callbackEventParameter); + + // Decode Callback postData from UTF-8 to current Charset + if (($g=$this->getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + foreach ($this->_postData as $k=>$v) + $this->_postData[$k]=self::decodeUTF8($v, $enc); + + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + $this->_restPostData=new TMap; + Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); + $this->loadPageState(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_postData,true); + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_restPostData,false); + + Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); + $this->raiseChangedEvents(); + + + $this->getAdapter()->processCallbackEvent($writer); + +/* + Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); + $this->raisePostBackEvent(); +*/ + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + +/* + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); +*/ + $this->getAdapter()->renderCallbackResponse($writer); + + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + /** + * Gets the callback client script handler that allows javascript functions + * to be executed during the callback response. + * @return TCallbackClientScript interface to client-side javascript code. + */ + public function getCallbackClient() + { + if($this->getAdapter() !== null) + return $this->getAdapter()->getCallbackClientHandler(); + else + return new TCallbackClientScript(); + } + + /** + * Set a new callback client handler. + * @param TCallbackClientScript new callback client script handler. + */ + public function setCallbackClient($client) + { + $this->getAdapter()->setCallbackClientHandler($client); + } + + /** + * @return TControl the control responsible for the current callback event, + * null if nonexistent + */ + public function getCallbackEventTarget() + { + return $this->getAdapter()->getCallbackEventTarget(); + } + + /** + * Registers a control to raise callback event in the current request. + * @param TControl control registered to raise callback event. + */ + public function setCallbackEventTarget(TControl $control) + { + $this->getAdapter()->setCallbackEventTarget($control); + } + + /** + * Callback parameter is decoded assuming JSON encoding. + * @return string callback event parameter + */ + public function getCallbackEventParameter() + { + return $this->getAdapter()->getCallbackEventParameter(); + } + + /** + * @param mixed callback event parameter + */ + public function setCallbackEventParameter($value) + { + $this->getAdapter()->setCallbackEventParameter($value); + } + + /** + * @return TForm the form on the page + */ + public function getForm() + { + return $this->_form; + } + + /** + * Registers a TForm instance to the page. + * Note, a page can contain at most one TForm instance. + * @param TForm the form on the page + * @throws TInvalidOperationException if this method is invoked twice or more. + */ + public function setForm(TForm $form) + { + if($this->_form===null) + $this->_form=$form; + else + throw new TInvalidOperationException('page_form_duplicated'); + } + + /** + * Returns a list of registered validators. + * If validation group is specified, only the validators in that group will be returned. + * @param string validation group + * @return TList registered validators in the requested group. If the group is null, all validators will be returned. + */ + public function getValidators($validationGroup=null) + { + if(!$this->_validators) + $this->_validators=new TList; + if(empty($validationGroup) === true) + return $this->_validators; + else + { + $list=new TList; + foreach($this->_validators as $validator) + if($validator->getValidationGroup()===$validationGroup) + $list->add($validator); + return $list; + } + } + + /** + * Performs input validation. + * This method will invoke the registered validators to perform the actual validation. + * If validation group is specified, only the validators in that group will be invoked. + * @param string validation group. If null, all validators will perform validation. + */ + public function validate($validationGroup=null) + { + Prado::trace("Page validate()",'System.Web.UI.TPage'); + $this->_validated=true; + if($this->_validators && $this->_validators->getCount()) + { + if($validationGroup===null) + { + foreach($this->_validators as $validator) + $validator->validate(); + } + else + { + foreach($this->_validators as $validator) + { + if($validator->getValidationGroup()===$validationGroup) + $validator->validate(); + } + } + } + } + + /** + * Returns whether user input is valid or not. + * This method must be invoked after {@link validate} is called. + * @return boolean whether the user input is valid or not. + * @throws TInvalidOperationException if {@link validate} is not invoked yet. + */ + public function getIsValid() + { + if($this->_validated) + { + if($this->_validators && $this->_validators->getCount()) + { + foreach($this->_validators as $validator) + if(!$validator->getIsValid()) + return false; + } + return true; + } + else + throw new TInvalidOperationException('page_isvalid_unknown'); + } + + /** + * @return TTheme the theme used for the page. Defaults to null. + */ + public function getTheme() + { + if(is_string($this->_theme)) + $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); + return $this->_theme; + } + + /** + * Sets the theme to be used for the page. + * @param string|TTheme the theme name or the theme object to be used for the page. + */ + public function setTheme($value) + { + $this->_theme=empty($value)?null:$value; + } + + + /** + * @return TTheme the stylesheet theme used for the page. Defaults to null. + */ + public function getStyleSheetTheme() + { + if(is_string($this->_styleSheet)) + $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); + return $this->_styleSheet; + } + + /** + * Sets the stylesheet theme to be used for the page. + * @param string|TTheme the stylesheet theme name or the stylesheet theme object to be used for the page. + */ + public function setStyleSheetTheme($value) + { + $this->_styleSheet=empty($value)?null:$value; + } + + /** + * Applies a skin in the current theme to a control. + * This method should only be used by framework developers. + * @param TControl a control to be applied skin with + */ + public function applyControlSkin($control) + { + if(($theme=$this->getTheme())!==null) + $theme->applySkin($control); + } + + /** + * Applies a stylesheet skin in the current theme to a control. + * This method should only be used by framework developers. + * @param TControl a control to be applied stylesheet skin with + */ + public function applyControlStyleSheet($control) + { + if(($theme=$this->getStyleSheetTheme())!==null) + $theme->applySkin($control); + } + + /** + * @return TClientScriptManager client script manager + */ + public function getClientScript() + { + if(!$this->_clientScript) { + $className = $classPath = $this->getService()->getClientScriptManagerClass(); + Prado::using($className); + if(($pos=strrpos($className,'.'))!==false) + $className=substr($className,$pos+1); + + if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) + throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); + + $this->_clientScript=new $className($this); + } + return $this->_clientScript; + } + + /** + * Raises OnPreInit event. + * This method is invoked right before {@link onInit OnInit} stage. + * You may override this method to provide additional initialization that + * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or + * {@link setStyleSheetTheme StyleSheetTheme}). + * Remember to call the parent implementation to ensure OnPreInit event is raised. + * @param mixed event parameter + */ + public function onPreInit($param) + { + $this->raiseEvent('OnPreInit',$this,$param); + } + + /** + * Raises OnInitComplete event. + * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage. + * You may override this method to provide additional initialization that + * should be done after {@link onInit OnInit}. + * Remember to call the parent implementation to ensure OnInitComplete event is raised. + * @param mixed event parameter + */ + public function onInitComplete($param) + { + $this->raiseEvent('OnInitComplete',$this,$param); + } + + /** + * Raises OnPreLoad event. + * This method is invoked right before {@link onLoad OnLoad} stage. + * You may override this method to provide additional page loading logic that + * should be done before {@link onLoad OnLoad}. + * Remember to call the parent implementation to ensure OnPreLoad event is raised. + * @param mixed event parameter + */ + public function onPreLoad($param) + { + $this->raiseEvent('OnPreLoad',$this,$param); + } + + /** + * Raises OnLoadComplete event. + * This method is invoked right after {@link onLoad OnLoad} stage. + * You may override this method to provide additional page loading logic that + * should be done after {@link onLoad OnLoad}. + * Remember to call the parent implementation to ensure OnLoadComplete event is raised. + * @param mixed event parameter + */ + public function onLoadComplete($param) + { + $this->raiseEvent('OnLoadComplete',$this,$param); + } + + /** + * Raises OnPreRenderComplete event. + * This method is invoked right after {@link onPreRender OnPreRender} stage. + * You may override this method to provide additional preparation for page rendering + * that should be done after {@link onPreRender OnPreRender}. + * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised. + * @param mixed event parameter + */ + public function onPreRenderComplete($param) + { + $this->raiseEvent('OnPreRenderComplete',$this,$param); + $cs=$this->getClientScript(); + $theme=$this->getTheme(); + if($theme instanceof ITheme) + { + foreach($theme->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($theme->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + $styleSheet=$this->getStyleSheetTheme(); + if($styleSheet instanceof ITheme) + { + foreach($styleSheet->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($styleSheet->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + + if($cs->getRequiresHead() && $this->getHead()===null) + throw new TConfigurationException('page_head_required'); + } + + /** + * Determines the media type of the CSS file. + * The media type is determined according to the following file name pattern: + * xxx.media-type.extension + * For example, 'mystyle.print.css' means its media type is 'print'. + * @param string CSS URL + * @return string media type of the CSS file + */ + private function getCssMediaType($url) + { + $segs=explode('.',basename($url)); + if(isset($segs[2])) + return $segs[count($segs)-2]; + else + return ''; + } + + /** + * Raises OnSaveStateComplete event. + * This method is invoked right after {@link onSaveState OnSaveState} stage. + * You may override this method to provide additional logic after page state is saved. + * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised. + * @param mixed event parameter + */ + public function onSaveStateComplete($param) + { + $this->raiseEvent('OnSaveStateComplete',$this,$param); + } + + /** + * Determines whether the current page request is a postback. + * Call {@link getIsPostBack} to get the result. + */ + private function determinePostBackMode() + { + $postData=$this->getRequest(); + if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) + $this->_postData=$postData; + } + + /** + * @return boolean whether the current page request is a postback + */ + public function getIsPostBack() + { + return $this->_postData!==null; + } + + /** + * @return boolean whether this is a callback request + */ + public function getIsCallback() + { + return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); + } + + /** + * This method is invoked when control state is to be saved. + * You can override this method to do last step state saving. + * Parent implementation must be invoked. + */ + public function saveState() + { + parent::saveState(); + $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); + } + + /** + * This method is invoked right after the control has loaded its state. + * You can override this method to initialize data from the control state. + * Parent implementation must be invoked. + */ + public function loadState() + { + parent::loadState(); + $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); + } + + /** + * Loads page state from persistent storage. + */ + protected function loadPageState() + { + Prado::trace("Loading state",'System.Web.UI.TPage'); + $state=$this->getStatePersister()->load(); + $this->loadStateRecursive($state,$this->getEnableViewState()); + } + + /** + * Saves page state from persistent storage. + */ + protected function savePageState() + { + Prado::trace("Saving state",'System.Web.UI.TPage'); + $state=&$this->saveStateRecursive($this->getEnableViewState()); + $this->getStatePersister()->save($state); + } + + /** + * @param string the field name + * @return boolean whether the specified field is a system field in postback data + */ + protected function isSystemPostField($field) + { + return isset(self::$_systemPostFields[$field]); + } + + /** + * Registers a control for loading post data in the next postback. + * This method needs to be invoked if the control to load post data + * may not have a post variable in some cases. For example, a checkbox, + * if not checked, will not have a post value. + * @param TControl control registered for loading post data + */ + public function registerRequiresPostData($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_controlsRegisteredForPostData[$id]=true; + $params=func_get_args(); + foreach($this->getCachingStack() as $item) + $item->registerAction('Page','registerRequiresPostData',array($id)); + } + + /** + * @return TControl the control responsible for the current postback event, null if nonexistent + */ + public function getPostBackEventTarget() + { + if($this->_postBackEventTarget===null && $this->_postData!==null) + { + $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); + if(!empty($eventTarget)) + $this->_postBackEventTarget=$this->findControl($eventTarget); + } + return $this->_postBackEventTarget; + } + + /** + * Registers a control to raise postback event in the current request. + * @param TControl control registered to raise postback event. + */ + public function setPostBackEventTarget(TControl $control) + { + $this->_postBackEventTarget=$control; + } + + /** + * @return string postback event parameter + */ + public function getPostBackEventParameter() + { + if($this->_postBackEventParameter===null && $this->_postData!==null) + { + if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) + $this->_postBackEventParameter=''; + } + return $this->_postBackEventParameter; + } + + /** + * @param string postback event parameter + */ + public function setPostBackEventParameter($value) + { + $this->_postBackEventParameter=$value; + } + + /** + * Processes post data. + * @param TMap post data to be processed + * @param boolean whether this method is invoked before {@link onLoad OnLoad}. + */ + protected function processPostData($postData,$beforeLoad) + { + $this->_isLoadingPostData=true; + if($beforeLoad) + $this->_restPostData=new TMap; + foreach($postData as $key=>$value) + { + if($this->isSystemPostField($key)) + continue; + else if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$postData)) + $this->_controlsPostDataChanged[]=$control; + } + else if($control instanceof IPostBackEventHandler && + empty($this->_postData[self::FIELD_POSTBACK_TARGET])) + { + $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); // not calling setPostBackEventTarget() because the control may be removed later + } + unset($this->_controlsRequiringPostData[$key]); + } + else if($beforeLoad) + $this->_restPostData->add($key,$value); + } + + foreach($this->_controlsRequiringPostData as $key=>$value) + { + if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$this->_postData)) + $this->_controlsPostDataChanged[]=$control; + } + else + throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); + unset($this->_controlsRequiringPostData[$key]); + } + } + $this->_isLoadingPostData=false; + } + + /** + * @return boolean true if loading post data. + */ + public function getIsLoadingPostData() + { + return $this->_isLoadingPostData; + } + + /** + * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback. + */ + protected function raiseChangedEvents() + { + foreach($this->_controlsPostDataChanged as $control) + $control->raisePostDataChangedEvent(); + } + + /** + * Raises PostBack event. + */ + protected function raisePostBackEvent() + { + if(($postBackHandler=$this->getPostBackEventTarget())===null) + $this->validate(); + else if($postBackHandler instanceof IPostBackEventHandler) + $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); + } + + /** + * @return boolean Whether form rendering is in progress + */ + public function getInFormRender() + { + return $this->_inFormRender; + } + + /** + * Ensures the control is rendered within a form. + * @param TControl the control to be rendered + * @throws TConfigurationException if the control is outside of the form + */ + public function ensureRenderInForm($control) + { + if(!$this->getIsCallback() && !$this->_inFormRender) + throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); + } + + /** + * @internal This method is invoked by TForm at the beginning of its rendering + */ + public function beginFormRender($writer) + { + if($this->_formRendered) + throw new TConfigurationException('page_form_duplicated'); + $this->_formRendered=true; + $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); + $this->_inFormRender=true; + } + + /** + * @internal This method is invoked by TForm at the end of its rendering + */ + public function endFormRender($writer) + { + if($this->_focus) + { + if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) + $focus=$this->_focus->getClientID(); + else + $focus=$this->_focus; + $this->getClientScript()->registerFocusControl($focus); + } + else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) + $this->getClientScript()->registerFocusControl($lastFocus); + $this->_inFormRender=false; + } + + /** + * Sets input focus on a control after the page is rendered to users. + * @param TControl|string control to receive focus, or the ID of the element on the page to receive focus + */ + public function setFocus($value) + { + $this->_focus=$value; + } + + /** + * @return boolean whether client supports javascript. Defaults to true. + */ + public function getClientSupportsJavaScript() + { + return $this->_enableJavaScript; + } + + /** + * @param boolean whether client supports javascript. If false, javascript will not be generated for controls. + */ + public function setClientSupportsJavaScript($value) + { + $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); + } + + /** + * @return THead page head, null if not available + */ + public function getHead() + { + return $this->_head; + } + + /** + * @param THead page head + * @throws TInvalidOperationException if a head already exists + */ + public function setHead(THead $value) + { + if($this->_head) + throw new TInvalidOperationException('page_head_duplicated'); + $this->_head=$value; + if($this->_title!==null) + { + $this->_head->setTitle($this->_title); + $this->_title=null; + } + } + + /** + * @return string page title. + */ + public function getTitle() + { + if($this->_head) + return $this->_head->getTitle(); + else + return $this->_title===null ? '' : $this->_title; + } + + /** + * Sets the page title. + * Note, a {@link THead} control needs to place on the page + * in order that this title be rendered. + * @param string page title. This will override the title set in {@link getHead Head}. + */ + public function setTitle($value) + { + if($this->_head) + $this->_head->setTitle($value); + else + $this->_title=$value; + } + + /** + * Returns the state to be stored on the client side. + * This method should only be used by framework and control developers. + * @return string the state to be stored on the client side + */ + public function getClientState() + { + return $this->_clientState; + } + + /** + * Sets the state to be stored on the client side. + * This method should only be used by framework and control developers. + * @param string the state to be stored on the client side + */ + public function setClientState($state) + { + $this->_clientState=$state; + } + + /** + * @return string the state postback from client side + */ + public function getRequestClientState() + { + return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); + } + + /** + * @return string class name of the page state persister. Defaults to TPageStatePersister. + */ + public function getStatePersisterClass() + { + return $this->_statePersisterClass; + } + + /** + * @param string class name of the page state persister. + */ + public function setStatePersisterClass($value) + { + $this->_statePersisterClass=$value; + } + + /** + * @return IPageStatePersister page state persister + */ + public function getStatePersister() + { + if($this->_statePersister===null) + { + $this->_statePersister=Prado::createComponent($this->_statePersisterClass); + if(!($this->_statePersister instanceof IPageStatePersister)) + throw new TInvalidDataTypeException('page_statepersister_invalid'); + $this->_statePersister->setPage($this); + } + return $this->_statePersister; + } + + /** + * @return boolean whether page state should be HMAC validated. Defaults to true. + */ + public function getEnableStateValidation() + { + return $this->_enableStateValidation; + } + + /** + * @param boolean whether page state should be HMAC validated. + */ + public function setEnableStateValidation($value) + { + $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether page state should be encrypted. Defaults to false. + */ + public function getEnableStateEncryption() + { + return $this->_enableStateEncryption; + } + + /** + * @param boolean whether page state should be encrypted. + */ + public function setEnableStateEncryption($value) + { + $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether page state should be compressed. Defaults to true. + * @since 3.1.6 + */ + public function getEnableStateCompression() + { + return $this->_enableStateCompression; + } + + /** + * @param boolean whether page state should be compressed. + * @since 3.1.6 + */ + public function setEnableStateCompression($value) + { + $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string the requested page path for this page + */ + public function getPagePath() + { + return $this->_pagePath; + } + + /** + * @param string the requested page path for this page + */ + public function setPagePath($value) + { + $this->_pagePath=$value; + } + + /** + * Registers an action associated with the content being cached. + * The registered action will be replayed if the content stored + * in the cache is served to end-users. + * @param string context of the action method. This is a property-path + * referring to the context object (e.g. Page, Page.ClientScript). + * @param string method name of the context object + * @param array list of parameters to be passed to the action method + */ + public function registerCachingAction($context,$funcName,$funcParams) + { + if($this->_cachingStack) + { + foreach($this->_cachingStack as $cache) + $cache->registerAction($context,$funcName,$funcParams); + } + } + + /** + * @return TStack stack of {@link TOutputCache} objects + */ + public function getCachingStack() + { + if(!$this->_cachingStack) + $this->_cachingStack=new TStack; + return $this->_cachingStack; + } + + /** + * Flushes output + */ + public function flushWriter() + { + if ($this->_writer) + $this->Response->write($this->_writer->flush()); + } + + /** + * Function to update view controls with those data in a given AR object. + * @param type $arObj + */ + public function tryUpdateView($arObj) + { + + } + + /** + * Function to try to update an AR object with data in view controls. + * @param type $arObj + */ + public function tryUpdateAR($arObj) + { + + } +} + +/** + * IPageStatePersister interface. + * + * IPageStatePersister interface is required for all page state persister + * classes. + * + * @author Qiang Xue + * @package System.Web.UI + * @since 3.1 + */ +interface IPageStatePersister +{ + /** + * @param TPage the page that this persister works for + */ + public function getPage(); + /** + * @param TPage the page that this persister works for + */ + public function setPage(TPage $page); + /** + * Saves state to persistent storage. + * @param mixed state to be stored + */ + public function save($state); + /** + * Loads page state from persistent storage + * @return mixed the restored state + */ + public function load(); +} + + +/** + * TPageStateFormatter class. + * + * TPageStateFormatter is a utility class to transform the page state + * into and from a string that can be properly saved in persistent storage. + * + * Depending on the {@link TPage::getEnableStateValidation() EnableStateValidation} + * and {@link TPage::getEnableStateEncryption() EnableStateEncryption}, + * TPageStateFormatter may do HMAC validation and encryption to prevent + * the state data from being tampered or viewed. + * The private keys and hashing/encryption methods are determined by + * {@link TApplication::getSecurityManager() SecurityManager}. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Web.UI + * @since 3.1 + */ +class TPageStateFormatter +{ + /** + * @param TPage + * @param mixed state data + * @return string serialized data + */ + public static function serialize($page,$data) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateValidation()) + $str=$sm->hashData(serialize($data)); + else + $str=serialize($data); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=gzcompress($str); + if($page->getEnableStateEncryption()) + $str=$sm->encrypt($str); + return base64_encode($str); + } + + /** + * @param TPage + * @param string serialized data + * @return mixed unserialized state data, null if data is corrupted + */ + public static function unserialize($page,$data) + { + $str=base64_decode($data); + if($str==='') + return null; + if($str!==false) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateEncryption()) + $str=$sm->decrypt($str); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=@gzuncompress($str); + if($page->getEnableStateValidation()) + { + if(($str=$sm->validateData($str))!==false) + return unserialize($str); + } + else + return unserialize($str); + } + return null; + } +} -- cgit v1.2.3