From d255f4d0e332740b3984e21ce3f7a4a4f1968ba3 Mon Sep 17 00:00:00 2001 From: wei <> Date: Tue, 2 May 2006 08:28:17 +0000 Subject: Adding more work on ActiveControls. --- framework/Web/Javascripts/TJavaScript.php | 15 +- framework/Web/Javascripts/js/ajax.js | 53 +++-- framework/Web/Javascripts/js/prado.js | 4 +- framework/Web/Javascripts/js/validator.js | 3 +- framework/Web/Javascripts/prado/ajax3.js | 213 +++++++++++++++++---- framework/Web/Javascripts/prado/validation3.js | 9 + framework/Web/UI/ActiveControls/TActiveControl.php | 28 ++- .../UI/ActiveControls/TActiveControlAdapter.php | 2 +- .../Web/UI/ActiveControls/TActivePageAdapter.php | 164 +++++++++++++--- .../UI/ActiveControls/TCallbackClientScript.php | 4 +- .../ActiveControls/TCallbackClientSideOptions.php | 209 ++++++++++++++++++++ .../Web/UI/ActiveControls/TCallbackResponse.php | 3 +- framework/Web/UI/TClientScriptManager.php | 15 ++ framework/Web/UI/TControl.php | 7 +- framework/Web/UI/TPage.php | 19 -- 15 files changed, 625 insertions(+), 123 deletions(-) create mode 100644 framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php (limited to 'framework/Web') diff --git a/framework/Web/Javascripts/TJavaScript.php b/framework/Web/Javascripts/TJavaScript.php index 75fc2438..0f6414ec 100644 --- a/framework/Web/Javascripts/TJavaScript.php +++ b/framework/Web/Javascripts/TJavaScript.php @@ -23,6 +23,11 @@ */ class TJavaScript { + /** + * @var TJSON JSON decoder and encoder instance + */ + private static $_json; + /** * Renders a list of javascript files * @param array URLs to the javascript files @@ -191,8 +196,9 @@ class TJavaScript */ public static function jsonEncode($value) { - Prado::using('System.Web.Javascripts.TJSON'); - return TJSON::encode($value); + if(is_null(self::$_json)) + self::$_json = Prado::createComponent('System.Web.Javascripts.TJSON'); + return self::$_json->encode($value); } /** @@ -203,8 +209,9 @@ class TJavaScript */ public static function jsonDecode($value) { - Prado::using('System.Web.Javascripts.TJSON'); - return TJSON::decode($value); + if(is_null(self::$_json)) + self::$_json = Prado::createComponent('System.Web.Javascripts.TJSON'); + return self::$_json->decode($value); } } diff --git a/framework/Web/Javascripts/js/ajax.js b/framework/Web/Javascripts/js/ajax.js index 2b509d61..6ae737bf 100644 --- a/framework/Web/Javascripts/js/ajax.js +++ b/framework/Web/Javascripts/js/ajax.js @@ -20,28 +20,53 @@ this.transport=Ajax.getTransport();this.setOptions(options);var onComplete=this. response=response.stripScripts();if(receiver){if(this.options.insertion){new this.options.insertion(receiver,response);}else{Element.update(receiver,response);}} if(this.responseIsSuccess()){if(this.onComplete) setTimeout(this.onComplete.bind(this),10);}}});Ajax.PeriodicalUpdater=Class.create();Ajax.PeriodicalUpdater.prototype=Object.extend(new Ajax.Base(),{initialize:function(container,url,options){this.setOptions(options);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=container;this.url=url;this.start();},start:function(){this.options.onComplete=this.updateComplete.bind(this);this.onTimerEvent();},stop:function(){this.updater.onComplete=undefined;clearTimeout(this.timer);(this.onComplete||Prototype.emptyFunction).apply(this,arguments);},updateComplete:function(request){if(this.options.decay){this.decay=(request.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=request.responseText;} -this.timer=setTimeout(this.onTimerEvent.bind(this),this.decay*this.frequency*1000);},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options);}});Prado.Callback=Class.create();Object.extend(Prado.Callback,{FIELD_CALLBACK_TARGET:'PRADO_CALLBACK_TARGET',FIELD_CALLBACK_PARAMETER:'PRADO_CALLBACK_PARAMETER',PostDataLoaders:['PRADO_PAGESTATE'],Exception:{"on505":function(request,transport,data) -{var msg='HTTP '+transport.status+" with response";Logger.error(msg,transport.responseText);this.logException(data);},onComplete:function(request,transport,data) -{if(transport.status!=505) -{var msg='HTTP '+transport.status+" with response : \n";msg+=transport.responseText+"\n";msg+="Data : \n"+inspect(data);Logger.warn(msg);}},formatException:function(e) +this.timer=setTimeout(this.onTimerEvent.bind(this),this.decay*this.frequency*1000);},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options);}});Object.extend(Ajax.Request.prototype,{respondToReadyState:function(readyState) +{var event=Ajax.Request.Events[readyState];var transport=this.transport,json=this.getHeaderData(Prado.CallbackRequest.DATA_HEADER);if(event=='Complete') +{Ajax.Responders.dispatch('on'+transport.status,this,transport,json);Prado.CallbackRequest.dispatchActions(this.getHeaderData(Prado.CallbackRequest.ACTION_HEADER));try{(this.options['on'+this.transport.status]||this.options['on'+(this.responseIsSuccess()?'Success':'Failure')]||Prototype.emptyFunction)(transport,json);}catch(e){this.dispatchException(e);} +if((this.header('Content-type')||'').match(/^text\/javascript/i)) +this.evalResponse();} +try{(this.options['on'+event]||Prototype.emptyFunction)(transport,json);Ajax.Responders.dispatch('on'+event,this,transport,json);}catch(e){this.dispatchException(e);} +if(event=='Complete') +this.transport.onreadystatechange=Prototype.emptyFunction;},getHeaderData:function(name) +{try +{var json=this.header(name);return eval('('+json+')');} +catch(e) +{if(typeof(json)=="string") +{Logger.info("using json") +return Prado.CallbackRequest.decode(json);}}}});Prado.CallbackRequest=Class.create();Object.extend(Prado.CallbackRequest,{FIELD_CALLBACK_TARGET:'PRADO_CALLBACK_TARGET',FIELD_CALLBACK_PARAMETER:'PRADO_CALLBACK_PARAMETER',PostDataLoaders:['PRADO_PAGESTATE'],DATA_HEADER:'X-PRADO-DATA',ACTION_HEADER:'X-PRADO-ACTIONS',ERROR_HEADER:'X-PRADO-ERROR',dispatchActions:function(actions) +{actions.each(this.__run);},__run:function(command) +{for(var method in command) +{if(command[method][0]) +{var id=command[method][0];if($(id)||id.indexOf("[]")>-1) +method.toFunction().apply(this,command[method]);else if(typeof(Logger)!="undefined") +{Logger.error("Error in executing callback response:","Unable to find HTML element with ID '"+id+"' before executing "+method+"().");}}}},Exception:{"on505":function(request,transport,data) +{var e=request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER);Logger.error("Callback Server Error "+e.code,this.formatException(e));},'on200':function(request,transport,data) +{if(transport.status<500) +{var msg='HTTP '+transport.status+" with response : \n";msg+=transport.responseText+"\n";msg+="Data : \n"+inspect(data)+"\n";msg+="Actions : \n";request.getHeaderData(Prado.CallbackRequest.ACTION_HEADER).each(function(action) +{msg+=inspect(action)+"\n";}) +Logger.warn(msg);}},onException:function(e) +{Logger.error('Uncaught Callback Client Exception:',e);},formatException:function(e) {var msg=e.type+" with message \""+e.message+"\"";msg+=" in "+e.file+"("+e.line+")\n";msg+="Stack trace:\n";var trace=e.trace;for(var i=0;i"+trace[i]["function"]+"()"+"\n";} -return msg;},logException:function(e) -{Logger.error("Callback Request Error "+e.code,this.formatException(e));}},encode:function(data) -{Prado.JSON.stringify(data);},decode:function(data) +msg+=e.version+" "+e.time+"\n";return msg;}},encode:function(data) +{return Prado.JSON.stringify(data);},decode:function(data) {return Prado.JSON.parse(data);}}) Event.OnLoad(function() {if(typeof Logger!="undefined") -Ajax.Responders.register(Prado.Callback.Exception);});Prado.Callback.prototype={url:window.location.href,options:{},id:null,parameters:null,initialize:function(id,parameters,onSuccess,options) -{this.options=options||{};this.id=id;this.parameters=parameters;var request={postBody:this._getPostData(),onSuccess:this._onSuccess.bind(this)} -Object.extend(this.options||{},request);new Ajax.Request(this.url,this.options);},_getPostData:function() -{var data={};Prado.Callback.PostDataLoaders.each(function(name) +Ajax.Responders.register(Prado.CallbackRequest.Exception);});Prado.CallbackRequest.prototype={url:window.location.href,options:{},id:null,request:null,initialize:function(id,options) +{this.options=options||{};this.id=id;var request={postBody:this._getPostData(),parameters:''} +Object.extend(this.options||{},request);if(this.options.CausesValidation!=false&&typeof(Prado.Validation)!="undefined") +{var form=this.options.Form||Prado.Validation.getForm();if(Prado.Validation.validate(form,this.options.ValidationGroup,this)==false) +return;} +this.request=new Ajax.Request(this.url,this.options);},_getPostData:function() +{var data={};Prado.CallbackRequest.PostDataLoaders.each(function(name) {$A(document.getElementsByName(name)).each(function(element) {var value=$F(element);if(typeof(value)!="undefined") data[name]=value;})}) -if(typeof(this.parameters)!="undefined") -data[Prado.Callback.FIELD_CALLBACK_PARAMETER]=Prado.Callback.encode(this.parameters);data[Prado.Callback.FIELD_CALLBACK_TARGET]=this.id;return $H(data).toQueryString();},_onSuccess:function(response,transport,json) -{}} +if(typeof(this.options.params)!="undefined") +data[Prado.CallbackRequest.FIELD_CALLBACK_PARAMETER]=Prado.CallbackRequest.encode(this.options.params);data[Prado.CallbackRequest.FIELD_CALLBACK_TARGET]=this.id;return $H(data).toQueryString();}} +Prado.Callback=function(UniqueID,parameter,onSuccess,options) +{var callback={'params':parameter||'','onSuccess':onSuccess||Prototype.emptyFunction,'CausesValidation':true};Object.extend(callback,options||{});new Prado.CallbackRequest(UniqueID,callback);return false;} Array.prototype.______array='______array';Prado.JSON={org:'http://www.JSON.org',copyright:'(c)2005 JSON.org',license:'http://www.crockford.com/JSON/license.html',stringify:function(arg){var c,i,l,s='',v;switch(typeof arg){case'object':if(arg){if(arg.______array=='______array'){for(i=0;i0) el.remove(0);for(var i=0;i -1) + method.toFunction().apply(this,command[method]); + else if(typeof(Logger) != "undefined") + { + Logger.error("Error in executing callback response:", + "Unable to find HTML element with ID '"+id+"' before executing "+method+"()."); + } + } + } + }, /** * Respond to Prado Callback request exceptions. @@ -32,26 +136,39 @@ Object.extend(Prado.Callback, * Server returns 505 exception. Just log it. */ "on505" : function(request, transport, data) - { - var msg = 'HTTP '+transport.status+" with response"; - Logger.error(msg, transport.responseText); - this.logException(data); + { + var e = request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER); + Logger.error("Callback Server Error "+e.code, this.formatException(e)); }, /** * Callback OnComplete event,logs reponse and data to console. */ - onComplete : function(request, transport, data) + 'on200' : function(request, transport, data) { - if(transport.status != 505) + if(transport.status < 500) { var msg = 'HTTP '+transport.status+" with response : \n"; msg += transport.responseText + "\n"; - msg += "Data : \n"+inspect(data); + msg += "Data : \n"+inspect(data)+"\n"; + msg += "Actions : \n"; + request.getHeaderData(Prado.CallbackRequest.ACTION_HEADER).each(function(action) + { + msg += inspect(action)+"\n"; + }) + Logger.warn(msg); } }, + /** + * Uncaught exceptions during callback response. + */ + onException : function(e) + { + Logger.error('Uncaught Callback Client Exception:', e); + }, + /** * Formats the exception message for display in console. */ @@ -67,15 +184,8 @@ Object.extend(Prado.Callback, msg += "("+trace[i].line+"): "; msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n"; } + msg += e.version+" "+e.time+"\n"; return msg; - }, - - /** - * Log Callback response exceptions to console. - */ - logException : function(e) - { - Logger.error("Callback Request Error "+e.code, this.formatException(e)); } }, @@ -84,7 +194,7 @@ Object.extend(Prado.Callback, */ encode : function(data) { - Prado.JSON.stringify(data); + return Prado.JSON.stringify(data); }, /** @@ -100,13 +210,13 @@ Object.extend(Prado.Callback, Event.OnLoad(function() { if(typeof Logger != "undefined") - Ajax.Responders.register(Prado.Callback.Exception); + Ajax.Responders.register(Prado.CallbackRequest.Exception); }); /** * Create and prepare a new callback request. */ -Prado.Callback.prototype = +Prado.CallbackRequest.prototype = { /** * Callback URL, same url as the current page. @@ -124,27 +234,31 @@ Prado.Callback.prototype = id : null, /** - * Callback parameters. + * Current callback request. */ - parameters : null, + request : null, /** * Prepare and inititate a callback request. */ - initialize : function(id, parameters, onSuccess, options) + initialize : function(id, options) { this.options = options || {}; this.id = id; - this.parameters = parameters; var request = { postBody : this._getPostData(), - onSuccess : this._onSuccess.bind(this) + parameters : '' } Object.extend(this.options || {},request); - - new Ajax.Request(this.url, this.options); + if(this.options.CausesValidation != false && typeof(Prado.Validation) != "undefined") + { + var form = this.options.Form || Prado.Validation.getForm(); + if(Prado.Validation.validate(form,this.options.ValidationGroup,this) == false) + return; + } + this.request = new Ajax.Request(this.url, this.options); }, /** @@ -156,7 +270,7 @@ Prado.Callback.prototype = { var data = {}; - Prado.Callback.PostDataLoaders.each(function(name) + Prado.CallbackRequest.PostDataLoaders.each(function(name) { $A(document.getElementsByName(name)).each(function(element) { @@ -165,17 +279,32 @@ Prado.Callback.prototype = data[name] = value; }) }) - if(typeof(this.parameters) != "undefined") - data[Prado.Callback.FIELD_CALLBACK_PARAMETER] = Prado.Callback.encode(this.parameters); - data[Prado.Callback.FIELD_CALLBACK_TARGET] = this.id; + if(typeof(this.options.params) != "undefined") + data[Prado.CallbackRequest.FIELD_CALLBACK_PARAMETER] = Prado.CallbackRequest.encode(this.options.params); + data[Prado.CallbackRequest.FIELD_CALLBACK_TARGET] = this.id; return $H(data).toQueryString(); - }, - - /** - * Dispatch a successfull response to the appropriate responders. - */ - _onSuccess : function(response, transport, json) - { - //Logger.info("asd"); } -} \ No newline at end of file +} + +/** + * Create a new callback request using default settings. + * @param string callback handler unique ID. + * @param mixed parameter to pass to callback handler on the server side. + * @param function client side onSuccess event handler. + * @param object additional request options. + * @return boolean always false. + */ +Prado.Callback = function(UniqueID, parameter, onSuccess, options) +{ + var callback = + { + 'params' : parameter || '', + 'onSuccess' : onSuccess || Prototype.emptyFunction, + 'CausesValidation' : true + }; + + Object.extend(callback, options || {}); + + new Prado.CallbackRequest(UniqueID, callback); + return false; +} diff --git a/framework/Web/Javascripts/prado/validation3.js b/framework/Web/Javascripts/prado/validation3.js index 1f154fec..8becac70 100644 --- a/framework/Web/Javascripts/prado/validation3.js +++ b/framework/Web/Javascripts/prado/validation3.js @@ -89,6 +89,15 @@ Object.extend(Prado.Validation, } }, + /** + * @return string first form ID. + */ + getForm : function() + { + var keys = $H(this.managers).keys(); + return keys[0]; + }, + /** * Check if the validators are valid for a particular form (and group). * The validators states will not be changed. diff --git a/framework/Web/UI/ActiveControls/TActiveControl.php b/framework/Web/UI/ActiveControls/TActiveControl.php index d289bab9..e61682d3 100644 --- a/framework/Web/UI/ActiveControls/TActiveControl.php +++ b/framework/Web/UI/ActiveControls/TActiveControl.php @@ -6,17 +6,43 @@ class TActiveControl extends TControl implements ICallbackEventHandler, IActiveControl { + private $_clientSide; + public function __construct() { parent::__construct(); $this->setAdapter(new TActiveControlAdapter($this)); } + + public function getClientSide() + { + if(is_null($this->_clientSide)) + $this->_clientSide = $this->createClientSideOptions(); + return $this->_clientSide; + } + + protected function createClientSideOptions() + { + $client = new TCallbackClientSideOptions; + return $client; + } public function raiseCallbackEvent($param) { - var_dump($param); + var_dump($param->getParameter()); + $param->Output->write($param->Parameter); $client = $this->getPage()->getCallbackClient(); $client->hide($this); + $client->toggle($this); + $client->update($this, 1); + $param->setData(array("asdasdad",1)); + } + + public function getCallbackReference() + { + // $formID = $this->getPage()->getForm()->getClientID(); + // $this->getClientSide()->setValidationForm($formID); + return $this->getPage()->getClientScript()->getCallbackReference($this); } } diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php index 187b2cac..b95bdd37 100644 --- a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php +++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -15,7 +15,7 @@ class TActiveControlAdapter extends TControlAdapter if(!self::$_renderedPosts) { $options = TJavascript::encode($this->getPage()->getPostDataLoaders(),false); - $script = "Prado.Callback.PostDataLoaders.concat({$options});"; + $script = "Prado.CallbackRequest.PostDataLoaders.concat({$options});"; $this->getPage()->getClientScript()->registerEndScript(get_class($this), $script); self::$_renderedPosts = true; } diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php index ab042d54..2607fec2 100644 --- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php +++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -22,6 +22,10 @@ */ class TActivePageAdapter extends TControlAdapter { + const CALLBACK_DATA_HEADER = 'X-PRADO-DATA'; + const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS'; + const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR'; + /** * @var ICallbackEventHandler callback event handler. */ @@ -35,11 +39,9 @@ class TActivePageAdapter extends TControlAdapter */ private $_callbackClient; /** - * @var TCallbackResponse callback response handler. + * @var TCallbackEventParameter callback result. */ - private $_callbackResponse; - - private $_callbackEventResult; + private $_result; /** * Constructor, trap errors and exception to let the callback response @@ -48,6 +50,7 @@ class TActivePageAdapter extends TControlAdapter public function __construct(TPage $control) { parent::__construct($control); + //$this->getApplication()->setResponse($this->getCallbackResponseHandler()); $this->trapCallbackErrorsExceptions(); } @@ -62,36 +65,36 @@ class TActivePageAdapter extends TControlAdapter protected function trapCallbackErrorsExceptions() { - //TODO: How to trap the errors and exceptions and return them - // as part of the response. + $this->getApplication()->setErrorHandler(new TCallbackErrorHandler); } + /** + * Render the callback response. + */ public function renderCallbackResponse($writer) { Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); $this->renderResponse($writer); } + /** + * Renders the callback response by adding additional callback data and + * javascript actions in the header. + */ protected function renderResponse($writer) { - //var_dump(getallheaders()); - //TODO: How to render the response, it will contain 3 pieces of data - // 1) The arbituary data returned to the client-side callback handler - // 2) client-side function call statements - // 3) Content body, which may need to be partitioned - - /* - $response = $this->getCallbackResponseHandler(); - $response->writeClientScriptResponse($this->getCallbackClientHandler()); - $response->writeResponseData($this->getCallbackEventResult()); - $response->flush(); - */ + $response = $this->getResponse(); + $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute()->toArray(); + $actions = TJavascript::jsonEncode($executeJavascript); + $response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); + $data = TJavascript::jsonEncode($this->_result->getData()); + $response->appendHeader(self::CALLBACK_DATA_HEADER.': '.$data); + $response->flush(); } /** * Trys to find the callback event handler and raise its callback event. - * @throws TInvalidCallbackRequestException if call back target is not - * found. + * @throws TInvalidCallbackRequestException if call back target is not found. * @throws TInvalidCallbackHandlerException if the requested target does not * implement ICallbackEventHandler. */ @@ -100,7 +103,11 @@ class TActivePageAdapter extends TControlAdapter if(($callbackHandler=$this->getCallbackEventTarget())!==null) { if($callbackHandler instanceof ICallbackEventHandler) - $callbackHandler->raiseCallbackEvent($this->getCallbackEventParameter()); + { + $writer = $this->getResponse()->createHtmlWriter(); + $this->_result = new TCallbackEventParameter($writer, $this->getCallbackEventParameter()); + $callbackHandler->raiseCallbackEvent($this->_result); + } else throw new TInvalidCallbackHandlerException($callbackHandler->getUniqueID()); } @@ -154,7 +161,6 @@ class TActivePageAdapter extends TControlAdapter $param = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); if(strlen($param) > 0) $this->_callbackEventParameter=TJavascript::jsonDecode((string)$param); - var_dump($param); } return $this->_callbackEventParameter; } @@ -187,23 +193,119 @@ class TActivePageAdapter extends TControlAdapter $this->_callbackClient = $handler; } +} + +/** + * TCallbackEventParameter class. + * + * The TCallbackEventParameter provides the parameter passed during the callback + * requestion in the {@link getParameter Parameter} property. The + * callback response response content (e.g. new HTML content) can be written to + * the {@link getOutput Output} property, which returns an instance of + * THtmlWriter. The response data (i.e., passing results back to the client-side + * callback handler function) can be set using {@link setData Data} property. + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ +class TCallbackEventParameter extends TEventParameter +{ + /** + * @var THtmlWriter output content. + */ + private $_output; + /** + * @var mixed callback request parameter. + */ + private $_parameter; + /** + * @var mixed callback response data. + */ + private $_data; + + /** + * Creates a new TCallbackEventParameter. + */ + public function __construct($writer, $parameter) + { + $this->_output = $writer; + $this->_parameter = $parameter; + } + + /** + * @return THtmlWriter holds the response content. + */ + public function getOutput() + { + return $this->_output; + } + /** - * Gets the callback response handler. - * @return TCallbackResponse callback response + * @return mixed callback request parameter. */ - public function getCallbackResponseHandler() + public function getParameter() { - if(is_null($this->_callbackResponse)) - $this->_callbackResponse = new TCallbackResponse; - return $this->_callbackResponse; + return $this->_parameter; } /** - * @param TCallbackResponse new callback response handler. + * @param mixed callback response data. */ - public function setCallbackResponseHandler($handler) + public function setData($value) { - $this->_callbackResponse = $handler; + $this->_data = $value; + } + + /** + * @return mixed callback response data. + */ + public function getData() + { + return $this->_data; + } +} + +class TCallbackErrorHandler extends TErrorHandler +{ + protected function displayException($exception) + { + if($this->getApplication()->getMode()===TApplication::STATE_DEBUG) + { + $response = $this->getApplication()->getResponse(); + $data = TJavascript::jsonEncode($this->getExceptionData($exception)); + $response->appendHeader('HTTP/1.0 505 Internal Error'); + $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$data); + } + else + { + error_log("Error happened while processing an existing error:\n".$exception->__toString()); + header('HTTP/1.0 500 Internal Error'); + } + } + + private function getExceptionData($exception) + { + $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 505; + $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; } } diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php index 550c88b5..10c0e638 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientScript.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -63,10 +63,8 @@ class TCallbackClientScript */ public function callClientFunction($function, $params=null) { - if(!is_array($params) && $params !== null) + if(!is_array($params)) $params = array($params); - else - $params = array(); if(count($params) > 0) { diff --git a/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php b/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php new file mode 100644 index 00000000..dea1b4e1 --- /dev/null +++ b/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php @@ -0,0 +1,209 @@ +_options = Prado::createComponent('System.Collections.TMap'); + } + + protected function setFunction($name, $code) + { + $this->_options->add($name, $this->ensureFunction($code)); + } + + protected function getOption($name) + { + return $this->_options->itemAt($name); + } + + public function getOptions() + { + return $this->_options; + } + + protected function ensureFunction($javascript) + { + return $javascript; + } +} + +/** + * The following client side events are executing in order if the callback + * request and response are send and received successfuly. + * + * - onUninitialized executed when AJAX request is uninitialized. + * - onLoading executed when AJAX request is initiated + * - onLoaded executed when AJAX request begins. + * - onInteractive executed when AJAX request is in progress. + * - onCompleteexecuted when AJAX response returns. + * + * The OnSuccess and OnFailure events are raised when the + * response is returned. A successful request/response will raise + * OnSuccess event otherwise OnFailure will be raised. + * + * - onSuccess executed when AJAX request returns and is successful. + * - onFailure executed when AJAX request returns and fails. + * + * - CausesValidation true to perform before callback request. + * - ValidationGroup validation grouping name. + */ +class TCallbackClientSideOptions extends TClientSideOptions +{ + protected function ensureFunction($javascript) + { + if(TJavascript::isFunction($javascript)) + return $javascript; + else + { + $code = "function(request, result){ {$javascript} }"; + return TJavascript::quoteFunction($code); + } + } + + /** + * @return string javascript code for client-side onUninitialized event + */ + public function getOnUninitialized() + { + return $this->getOption('onUninitialized'); + } + + /** + * @param string javascript code for client-side onUninitialized event. + */ + public function setOnUninitialized($javascript) + { + $this->setFunction('onUninitialized', $javascript); + } + + /** + * @return string javascript code for client-side onLoading event + */ + public function getOnLoading() + { + return $this->getOption('onLoading'); + } + + /** + * @param string javascript code for client-side onLoading event. + */ + public function setOnLoading($javascript) + { + $this->setFunction('onLoading', $javascript); + } + + /** + * @return string javascript code for client-side onLoaded event + */ + public function getOnLoaded() + { + return $this->getOption('onLoaded'); + } + + /** + * @param string javascript code for client-side onLoaded event. + */ + public function setOnLoaded($javascript) + { + $this->setFunction('onLoaded', $javascript); + } + /** + * @return string javascript code for client-side onInteractive event + */ + public function getOnInteractive() + { + return $this->getOption('onInteractive'); + } + + /** + * @param string javascript code for client-side onInteractive event. + */ + public function setonInteractive($javascript) + { + $this->setFunction('onInteractive', $javascript); + } + /** + * @return string javascript code for client-side onComplete event + */ + public function getOnComplete() + { + return $this->getOption('onComplete'); + } + + /** + * @param string javascript code for client-side onComplete event. + */ + public function setOnComplete($javascript) + { + $this->setFunction('onComplete', $javascript); + } + /** + * @return string javascript code for client-side onSuccess event + */ + public function getOnSuccess() + { + return $this->getOption('onSuccess'); + } + + /** + * @param string javascript code for client-side onSuccess event. + */ + public function setOnSuccess($javascript) + { + $this->setFunction('onSuccess', $javascript); + } + + /** + * @return string javascript code for client-side onFailure event + */ + public function getOnFailure() + { + return $this->getOption('onFailure'); + } + + /** + * @param string javascript code for client-side onFailure event. + */ + public function setOnFailure($javascript) + { + $this->setFunction('onFailure', $javascript); + } + + public function getCausesValidation() + { + return $this->getOption('CausesValidation'); + } + + public function setCausesValidation($value) + { + $this->getOptions()->add('CausesValidation', TPropertyValue::ensureBoolean($value)); + } + + public function getValidationGroup() + { + return $this->getOption('ValidationGroup'); + } + + public function setValidationGroup($value) + { + $this->getOptions()->add('ValidationGroup', $value); + } + + public function getValidationForm() + { + return $this->getOption('Form'); + } + + public function setValidationForm($value) + { + $this->getOptions()->add('Form', $value); + } +} + +?> diff --git a/framework/Web/UI/ActiveControls/TCallbackResponse.php b/framework/Web/UI/ActiveControls/TCallbackResponse.php index bda4e916..a05c20c7 100644 --- a/framework/Web/UI/ActiveControls/TCallbackResponse.php +++ b/framework/Web/UI/ActiveControls/TCallbackResponse.php @@ -11,8 +11,7 @@ class TCallbackResponse extends THttpResponse { - const CALLBACK_DATA_HEADER = 'X-PRADO-DATA'; - const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS'; + } diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php index 94ef19b6..46203b0b 100644 --- a/framework/Web/UI/TClientScriptManager.php +++ b/framework/Web/UI/TClientScriptManager.php @@ -164,6 +164,21 @@ class TClientScriptManager extends TApplicationComponent } } + public function getCallbackReference($callbackHandler, $options=null) + { + $options = !is_array($options) ? array() : $options; + $class = new TReflectionClass($callbackHandler); + if($class->hasMethod('getClientSide')) + { + $clientSide = $callbackHandler->getClientSide(); + $options = array_merge($options, $clientSide->getOptions()->toArray()); + } + $optionString = TJavascript::encode($options); + $this->registerPradoScriptInternal('ajax'); + $id = $callbackHandler->getUniqueID(); + return "new Prado.CallbackRequest('{$id}',{$optionString})"; + } + /** * Registers postback javascript for a control. * @param string javascript class responsible for the control being registered for postback diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index 1b5394ba..1db01df6 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -1215,9 +1215,6 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable protected function preRenderRecursive() { $this->autoDataBindProperties(); - - if($this->getEnabled() && $this instanceof IPostBackDataHandler) - $this->getPage()->registerPostDataLoader($this); if($this->getVisible(false)) { @@ -1238,6 +1235,10 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable } } $this->_stage=self::CS_PRERENDERED; + + + if($this->getEnabled() && $this instanceof IPostBackDataHandler) + $this->getPage()->registerPostDataLoader($this); } /** diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index 31c80320..c72cdd03 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -322,25 +322,6 @@ class TPage extends TTemplateControl $this->unloadRecursive(); } - /** - * Gets the callback response handler that permits changing the callback - * response headers and contents. - * @return TCallbackResponse callback response handler. - */ - public function getCallbackResponse() - { - return $this->getAdapter()->getCallbackResponseHandler(); - } - - /** - * Set a new callback respond handler. - * @param TCallbackResponse a different callback response handler. - */ - public function setCallbackResponse($responder) - { - $this->getAdapter()->setCallbackResponseHandler($responder); - } - /** * Gets the callback client script handler that allows javascript functions * to be executed during the callback response. -- cgit v1.2.3