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/

---
 framework/Web/UI/WebControls/TReCaptcha.php        | 66 +++++++++++++++++-----
 .../Web/UI/WebControls/TReCaptchaValidator.php     | 59 ++++++++++++-------
 2 files changed, 89 insertions(+), 36 deletions(-)

(limited to 'framework')

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('<div id="'.htmlspecialchars($divid).'">');
+	
+		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('</div>');
 	}
 
 }
-
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