summaryrefslogtreecommitdiff
path: root/lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php
diff options
context:
space:
mode:
authoremkael <emkael@tlen.pl>2016-02-24 23:18:07 +0100
committeremkael <emkael@tlen.pl>2016-02-24 23:18:07 +0100
commit6f7fdef0f500cd4bb540affd3bc1482243f337c1 (patch)
tree4853eecd0769a903e6130c1896e1d070848150dd /lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php
parent61f2ea48a4e11cb5fb941b3783e19c9e9ef38a45 (diff)
* Prado 3.3.0
Diffstat (limited to 'lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php')
-rw-r--r--lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php415
1 files changed, 415 insertions, 0 deletions
diff --git a/lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php
new file mode 100644
index 0000000..1803f44
--- /dev/null
+++ b/lib/prado/framework/Web/UI/ActiveControls/TActivePageAdapter.php
@@ -0,0 +1,415 @@
+<?php
+/**
+ * TActivePageAdapter, TCallbackErrorHandler and TInvalidCallbackException class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @author Gabor Berczi <gabor.berczi@devworx.hu> (lazyload additions & progressive rendering)
+ * @link https://github.com/pradosoft/prado
+ * @copyright Copyright &copy; 2005-2015 The PRADO Group
+ * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
+ * @package System.Web.UI.ActiveControls
+ */
+
+/**
+ * Load callback response adapter class.
+ */
+Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter');
+Prado::using('System.Web.UI.ActiveControls.TCallbackClientScript');
+Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter');
+
+/**
+ * TActivePageAdapter class.
+ *
+ * Callback request handler.
+ *
+ * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @author Gabor Berczi <gabor.berczi@devworx.hu> (lazyload additions & progressive rendering)
+ * @package System.Web.UI.ActiveControls
+ * @since 3.1
+ */
+class TActivePageAdapter extends TControlAdapter
+{
+ /**
+ * Callback response data header name.
+ */
+ const CALLBACK_DATA_HEADER = 'X-PRADO-DATA';
+ /**
+ * Callback response client-side action header name.
+ */
+ const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS';
+ /**
+ * Callback error header name.
+ */
+ const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR';
+ /**
+ * Callback page state header name.
+ */
+ const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE';
+ /**
+ * Script list header name.
+ */
+ const CALLBACK_SCRIPTLIST_HEADER = 'X-PRADO-SCRIPTLIST';
+ /**
+ * Stylesheet list header name.
+ */
+ const CALLBACK_STYLESHEETLIST_HEADER = 'X-PRADO-STYLESHEETLIST';
+ /**
+ * Stylesheet header name.
+ */
+ const CALLBACK_STYLESHEET_HEADER = 'X-PRADO-STYLESHEET';
+ /**
+ * Hidden field list header name.
+ */
+ const CALLBACK_HIDDENFIELDLIST_HEADER = 'X-PRADO-HIDDENFIELDLIST';
+
+ /**
+ * Callback redirect url header name.
+ */
+ const CALLBACK_REDIRECT = 'X-PRADO-REDIRECT';
+
+ /**
+ * @var ICallbackEventHandler callback event handler.
+ */
+ private $_callbackEventTarget;
+ /**
+ * @var mixed callback event parameter.
+ */
+ private $_callbackEventParameter;
+ /**
+ * @var TCallbackClientScript callback client script handler
+ */
+ private $_callbackClient;
+
+ private $_controlsToRender=array();
+
+ /**
+ * Constructor, trap errors and exception to let the callback response
+ * handle them.
+ */
+ public function __construct(TPage $control)
+ {
+ parent::__construct($control);
+
+ //TODO: can this be done later?
+ $response = $this->getApplication()->getResponse();
+ $response->setAdapter(new TCallbackResponseAdapter($response));
+
+ $this->trapCallbackErrorsExceptions();
+ }
+
+ /**
+ * Process the callback request.
+ * @param THtmlWriter html content writer.
+ */
+ public function processCallbackEvent($writer)
+ {
+ Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->raiseCallbackEvent();
+ }
+
+ /**
+ * Register a control for defered render() call.
+ * @param TControl control for defered rendering
+ * @param THtmlWriter the renderer
+ */
+ public function registerControlToRender($control,$writer)
+ {
+ $id = $control->getUniqueID();
+ if(!isset($this->_controlsToRender[$id]))
+ $this->_controlsToRender[$id] = array($control,$writer);
+ }
+
+ /**
+ * Trap errors and exceptions to be handled by TCallbackErrorHandler.
+ */
+ protected function trapCallbackErrorsExceptions()
+ {
+ $this->getApplication()->setErrorHandler(new TCallbackErrorHandler);
+ }
+
+ /**
+ * Render the callback response.
+ * @param THtmlWriter html content writer.
+ */
+ public function renderCallbackResponse($writer)
+ {
+ Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ if(($url = $this->getResponse()->getAdapter()->getRedirectedUrl())===null)
+ $this->renderResponse($writer);
+ else
+ $this->redirect($url);
+ }
+
+ /**
+ * Redirect url on the client-side using javascript.
+ * @param string new url to load.
+ */
+ protected function redirect($url)
+ {
+ Prado::trace("ActivePage redirect()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ $this->appendContentPart($this->getResponse(), self::CALLBACK_REDIRECT, $url);
+ }
+
+ /**
+ * Renders the callback response by adding additional callback data and
+ * javascript actions in the header and page state if required.
+ * @param THtmlWriter html content writer.
+ */
+ protected function renderResponse($writer)
+ {
+ Prado::trace("ActivePage renderResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter');
+ //renders all the defered render() calls.
+ foreach($this->_controlsToRender as $rid => $forRender)
+ $forRender[0]->render($forRender[1]);
+
+ $response = $this->getResponse();
+
+ //send response data in header
+ if($response->getHasAdapter())
+ {
+ $responseData = $response->getAdapter()->getResponseData();
+ if($responseData!==null)
+ {
+ $data = TJavaScript::jsonEncode($responseData);
+
+ $this->appendContentPart($response, self::CALLBACK_DATA_HEADER, $data);
+ }
+ }
+
+ //sends page state in header
+ if(($handler = $this->getCallbackEventTarget()) !== null)
+ {
+ if($handler->getActiveControl()->getClientSide()->getEnablePageStateUpdate())
+ {
+ $pagestate = $this->getPage()->getClientState();
+ $this->appendContentPart($response, self::CALLBACK_PAGESTATE_HEADER, $pagestate);
+ }
+ }
+
+ //safari must receive at least 1 byte of data.
+ $writer->write(" ");
+
+ //output the end javascript
+ if($this->getPage()->getClientScript()->hasEndScripts())
+ {
+ $writer = $response->createHtmlWriter();
+ $this->getPage()->getClientScript()->renderEndScriptsCallback($writer);
+ $this->getPage()->getCallbackClient()->evaluateScript($writer);
+ }
+
+ //output the actions
+ $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute();
+ $actions = TJavaScript::jsonEncode($executeJavascript);
+ $this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions);
+
+
+ $cs = $this->Page->getClientScript();
+
+ // collect all stylesheet file references
+ $stylesheets = $cs->getStyleSheetUrls();
+ if (count($stylesheets)>0)
+ $this->appendContentPart($response, self::CALLBACK_STYLESHEETLIST_HEADER, TJavaScript::jsonEncode($stylesheets));
+
+ // collect all stylesheet snippets references
+ $stylesheets = $cs->getStyleSheetCodes();
+ if (count($stylesheets)>0)
+ $this->appendContentPart($response, self::CALLBACK_STYLESHEET_HEADER, TJavaScript::jsonEncode($stylesheets));
+
+ // collect all script file references
+ $scripts = $cs->getScriptUrls();
+ if (count($scripts)>0)
+ $this->appendContentPart($response, self::CALLBACK_SCRIPTLIST_HEADER, TJavaScript::jsonEncode($scripts));
+
+ // collect all hidden field references
+ $fields = $cs->getHiddenFields();
+ if (count($fields)>0)
+ $this->appendContentPart($response, self::CALLBACK_HIDDENFIELDLIST_HEADER, TJavaScript::jsonEncode($fields));
+ }
+
+ /**
+ * Appends data or javascript code to the body content surrounded with delimiters
+ */
+ private function appendContentPart($response, $delimiter, $data)
+ {
+ $content = $response->createHtmlWriter();
+ $content->getWriter()->setBoundary($delimiter);
+ $content->write($data);
+ }
+
+ /**
+ * Trys to find the callback event handler and raise its callback event.
+ * @throws TInvalidCallbackException if call back target is not found.
+ * @throws TInvalidCallbackException if the requested target does not
+ * implement ICallbackEventHandler.
+ */
+ private function raiseCallbackEvent()
+ {
+ if(($callbackHandler=$this->getCallbackEventTarget())!==null)
+ {
+ if($callbackHandler instanceof ICallbackEventHandler)
+ {
+ $param = $this->getCallbackEventParameter();
+ $result = new TCallbackEventParameter($this->getResponse(), $param);
+ $callbackHandler->raiseCallbackEvent($result);
+ }
+ else
+ {
+ throw new TInvalidCallbackException(
+ 'callback_invalid_handler', $callbackHandler->getUniqueID());
+ }
+ }
+ else
+ {
+ $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET);
+ throw new TInvalidCallbackException('callback_invalid_target', $target);
+ }
+ }
+
+ /**
+ * @return TControl the control responsible for the current callback event,
+ * null if nonexistent
+ */
+ public function getCallbackEventTarget()
+ {
+ if($this->_callbackEventTarget===null)
+ {
+ $eventTarget=$this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET);
+ if(!empty($eventTarget))
+ $this->_callbackEventTarget=$this->getPage()->findControl($eventTarget);
+ }
+ return $this->_callbackEventTarget;
+ }
+
+ /**
+ * 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->_callbackEventTarget=$control;
+ }
+
+ /**
+ * Gets callback parameter.
+ * @return string postback event parameter
+ */
+ public function getCallbackEventParameter()
+ {
+ if($this->_callbackEventParameter===null)
+ {
+ $param = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER);
+ $this->_callbackEventParameter=$param;
+ }
+ return $this->_callbackEventParameter;
+ }
+
+ /**
+ * @param mixed postback event parameter
+ */
+ public function setCallbackEventParameter($value)
+ {
+ $this->_callbackEventParameter=$value;
+ }
+
+ /**
+ * Gets the callback client script handler. It handlers the javascript functions
+ * to be executed during the callback response.
+ * @return TCallbackClientScript callback client handler.
+ */
+ public function getCallbackClientHandler()
+ {
+ if($this->_callbackClient===null)
+ $this->_callbackClient = new TCallbackClientScript;
+ return $this->_callbackClient;
+ }
+}
+
+/**
+ * TCallbackErrorHandler class.
+ *
+ * Captures errors and exceptions and send them back during callback response.
+ * When the application is in debug mode, the error and exception stack trace
+ * are shown. A TJavascriptLogger must be present on the client-side to view
+ * the error stack trace.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.ActiveControls
+ * @since 3.1
+ */
+class TCallbackErrorHandler extends TErrorHandler
+{
+ /**
+ * Displays the exceptions to the client-side TJavascriptLogger.
+ * A HTTP 500 status code is sent and the stack trace is sent as JSON encoded.
+ * @param Exception exception details.
+ */
+ protected function displayException($exception)
+ {
+ if($this->getApplication()->getMode()===TApplication::STATE_DEBUG)
+ {
+ $response = $this->getApplication()->getResponse();
+ $trace = $this->getExceptionStackTrace($exception);
+ // avoid error on non-utf8 strings
+ try {
+ $trace = TJavaScript::jsonEncode($trace);
+ } catch (Exception $e) {
+ // strip everythin not 7bit ascii
+ $trace = preg_replace('/[^(\x20-\x7F)]*/','', serialize($trace));
+ }
+
+ // avoid exception loop if headers have already been sent
+ try {
+ $response->setStatusCode(500, 'Internal Server Error');
+ } catch (Exception $e) { }
+
+ $content = $response->createHtmlWriter();
+ $content->getWriter()->setBoundary(TActivePageAdapter::CALLBACK_ERROR_HEADER);
+ $content->write($trace);
+ }
+ else
+ {
+ error_log("Error happened while processing an existing error:\n".$exception->__toString());
+ header('HTTP/1.0 500 Internal Server Error', true, 500);
+ }
+ $this->getApplication()->getResponse()->flush();
+ }
+
+ /**
+ * @param Exception exception details.
+ * @return array exception stack trace details.
+ */
+ private function getExceptionStackTrace($exception)
+ {
+ $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500;
+ $data['file']=$exception->getFile();
+ $data['line']=$exception->getLine();
+ $data['trace']=$exception->getTrace();
+ if($exception instanceof TPhpErrorException)
+ {
+ // if PHP exception, we want to show the 2nd stack level context
+ // because the 1st stack level is of little use (it's in error handler)
+ if(isset($trace[0]) && isset($trace[0]['file']) && isset($trace[0]['line']))
+ {
+ $data['file']=$trace[0]['file'];
+ $data['line']=$trace[0]['line'];
+ }
+ }
+ $data['type']=get_class($exception);
+ $data['message']=$exception->getMessage();
+ $data['version']=$_SERVER['SERVER_SOFTWARE'].' '.Prado::getVersion();
+ $data['time']=@strftime('%Y-%m-%d %H:%M',time());
+ return $data;
+ }
+}
+
+/**
+ * TInvalidCallbackException class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @package System.Web.UI.ActiveControls
+ * @since 3.1
+ */
+class TInvalidCallbackException extends TException
+{
+}
+