From b5f4be7ea0b8bdf220297893a907fe59c017fbd5 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Sun, 6 Jan 2013 18:17:30 +0000 Subject: backported r3236 to trunk/ --- HISTORY | 1 + framework/Web/UI/WebControls/TReCaptcha.php | 66 +++++++++++++++++----- .../Web/UI/WebControls/TReCaptchaValidator.php | 59 ++++++++++++------- 3 files changed, 90 insertions(+), 36 deletions(-) diff --git a/HISTORY b/HISTORY index 669e742c..6bd48091 100644 --- a/HISTORY +++ b/HISTORY @@ -20,6 +20,7 @@ BUG: Issue #425 - Error generating WSDL (cezarpirajant) ENH: Issue #426 - Make TDataFieldAccessor capable to access data in nested arrays (ctrlaltca) BUG: Issue #427 - Var name mismatch in TActiveFileUpload.php (piotr.knapik) BUG: Issue #428 - Using a TActiveCustomValidator with a TActiveTextBox and AutoPostBack does not work as expected (ctrlaltca) +BUG: Issue #431 - TReCaptcha does not work well with active controls (Gabor) EHN: Permit to change the default cipher in TSecurityManager::setEncryption(); changed the default cipher from 3DES to AES256 (ctrlaltca) EHN: Use php's hash_hmac() when available in TSecurityManager, and permit the use of all algorithms supported by php (ctrlaltca) diff --git a/framework/Web/UI/WebControls/TReCaptcha.php b/framework/Web/UI/WebControls/TReCaptcha.php index b63edad8..c6cf4185 100644 --- a/framework/Web/UI/WebControls/TReCaptcha.php +++ b/framework/Web/UI/WebControls/TReCaptcha.php @@ -135,6 +135,16 @@ class TReCaptcha extends TWebControl implements IValidatable return $this->setViewState('Language', TPropertyValue::ensureString($value)); } + public function getCallbackScript() + { + return $this->getViewState('CallbackScript'); + } + + public function setCallbackScript($value) + { + return $this->setViewState('CallbackScript', TPropertyValue::ensureString($value)); + } + protected function getChallengeFieldName() { return /*$this->ClientID.'_'.*/self::ChallengeFieldName; @@ -212,21 +222,47 @@ class TReCaptcha extends TWebControl implements IValidatable public function renderContents($writer) { - $writer->write(TJavaScript::renderScriptBlock( - 'var RecaptchaOptions = '.TJavaScript::jsonEncode($this->getClientSideOptions()).';' - )); - - $html = recaptcha_get_html($this->getPublicKey()); - /* - reCAPTCHA currently does not support multiple validations per page - $html = str_replace( - array(self::ChallengeFieldName,self::ResponseFieldName), - array($this->getChallengeFieldName(),$this->getResponseFieldName()), - $html - ); - */ - $writer->write($html); + $readyscript = 'Event.fire(document, '.TJavaScript::quoteString('captchaready:'.$this->getClientID()).')'; + $cs = $this->Page->ClientScript; + $id = $this->getClientID(); + $divid = $id.'_1_recaptchadiv'; + $writer->write('
'); + + if (!$this->Page->IsCallback) + { + $writer->write(TJavaScript::renderScriptBlock( + 'var RecaptchaOptions = '.TJavaScript::jsonEncode($this->getClientSideOptions()).';' + )); + + $html = recaptcha_get_html($this->getPublicKey()); + /* + reCAPTCHA currently does not support multiple validations per page + $html = str_replace( + array(self::ChallengeFieldName,self::ResponseFieldName), + array($this->getChallengeFieldName(),$this->getResponseFieldName()), + $html + ); + */ + $writer->write($html); + + $cs->registerEndScript('ReCaptcha::EventScript', 'Event.observe(document, "dom:loaded", function() { '.$readyscript.'; } );'); + } + else + { + $options = $this->getClientSideOptions(); + $options['callback'] = new TJavaScriptLiteral('function() { '.$readyscript.'; '.$this->getCallbackScript().'; }'); + $cs->registerScriptFile('ReCaptcha::AjaxScript', 'http://www.google.com/recaptcha/api/js/recaptcha_ajax.js'); + $cs->registerEndScript('ReCaptcha::CreateScript::'.$id, implode(' ', array( + 'if (!$('.TJavaScript::quoteString($this->getResponseFieldName()).'))', + 'Recaptcha.create(', + TJavaScript::quoteString($this->getPublicKey()).', ', + TJavaScript::quoteString($divid).', ', + TJavaScript::encode($options), + ');', + ))); + } + + $writer->write('
'); } } - diff --git a/framework/Web/UI/WebControls/TReCaptchaValidator.php b/framework/Web/UI/WebControls/TReCaptchaValidator.php index cc1a4080..bba356b8 100644 --- a/framework/Web/UI/WebControls/TReCaptchaValidator.php +++ b/framework/Web/UI/WebControls/TReCaptchaValidator.php @@ -87,36 +87,53 @@ class TReCaptchaValidator extends TBaseValidator parent::onPreRender($param); $cs = $this->Page->getClientScript(); + $cs->registerPradoScript('validator'); // communicate validation status to the client side $value = $this->_isvalid===false ? '0' : '1'; $cs->registerHiddenField($this->getClientID().'_1',$value); - - // check if we need to request a new captcha too - if ($this->Page->IsCallback) + + // update validator display + if ($control = $this->getValidationTarget()) { - // force update of validator display - if ($control = $this->getValidationTarget()) - { - $cs->registerEndScript( - $this->getClientID().'::validate', - '$('.TJavaScript::quoteString($this->getClientID().'_1').').value = '.TJavaScript::quoteString($value).';'. - 'Prado.Validation.validateControl('.TJavaScript::quoteString($control->ClientID).');' - ); + $fn = 'captchaUpdateValidatorStatus_'.$this->getClientID(); - if ($control->getVisible(true)) - if ($this->_isvalid) + // check if we need to request a new captcha too + if ($this->Page->IsCallback) { - // if the challenge has been solved + we're in a callback and we still reach prerender phase, - // that means that some other validator failed and the user will be sent back to the page/form with - // the captcha control. in this case we need to force re-rendering of the control, because once - // solved, the old challenge won't validate anymore anyway - - $control->regenerateToken(); + if ($control->getVisible(true)) + if (!is_null($this->_isvalid)) + { + // if the response has been tested and we reach the pre-render phase + // then we need to regenerate the token, because it won't test positive + // anymore, even if solves correctly + + $control->regenerateToken(); + } } - } + + $cs->registerEndScript($this->getClientID().'::validate', implode(' ',array( + // this function will be used to update the validator + 'function '.$fn.'(valid)', + '{', + ' var v = $('.TJavaScript::quoteString($this->getClientID()).');', + ' $('.TJavaScript::quoteString($this->getClientID().'_1').').value = valid;', + ' Prado.Validation.validateControl('.TJavaScript::quoteString($control->ClientID).'); ', + '}', + '', + // update the validator to the result if we're in a callback + // (if we're in initial rendering or a postback then the result will be rendered directly to the page html anyway) + $this->Page->IsCallback ? $fn.'('.$value.');' : '', + '', + // wait for the captcha to be constructed + 'Event.observe(document,"captchaready:'.$control->getClientID().'",function() { ', + // install event handler that clears the validation error when user changes the captcha response field + 'Event.observe('.TJavaScript::quoteString($control->getResponseFieldName()).',"keyup",function() { ', + $fn.'("1");', + '});', + '});', + ))); } } } - -- cgit v1.2.3