summaryrefslogtreecommitdiff
path: root/framework/Web/UI/WebControls/TReCaptchaValidator.php
blob: bba356b86131d39fc6185fc3004e2c33253719b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<?php

/**
 * TReCaptchaValidator class file
 *
 * @author Bérczi Gábor <gabor.berczi@devworx.hu>
 * @link http://www.devworx.hu/
 * @copyright Copyright &copy; 2011 DevWorx
 * @license http://www.pradosoft.com/license/
 * @package System.Web.UI.WebControls
 */

Prado::using('System.Web.UI.WebControls.TBaseValidator');
Prado::using('System.Web.UI.WebControls.TReCaptcha');

/**
 * TReCaptchaValidator class
 *
 * TReCaptchaValidator validates user input against a reCAPTCHA represented by
 * a {@link TReCaptcha} control. The input control fails validation if its value
 * is not the same as the token displayed in reCAPTCHA. Note, if the user does
 * not enter any thing, it is still considered as failing the validation.
 *
 * To use TReCaptchaValidator, specify the {@link setControlToValidate ControlToValidate}
 * to be the ID path of the {@link TReCaptcha} control.
 *
 * @author Bérczi Gábor <gabor.berczi@devworx.hu>
 * @package System.Web.UI.WebControls
 * @since 3.2
 */
class TReCaptchaValidator extends TBaseValidator
{
	protected $_isvalid = null;

	/**
	 * Gets the name of the javascript class responsible for performing validation for this control.
	 * This method overrides the parent implementation.
	 * @return string the javascript class name
	 */
	protected function getClientClassName()
	{
		return 'Prado.WebUI.TReCaptchaValidator';
	}

	public function getEnableClientScript()
	{
		return true;
	}

	protected function getCaptchaControl()
	{
		$control = $this->getValidationTarget();
		if (!$control)
			throw new Exception('No target control specified for TReCaptchaValidator');
		if (!($control instanceof TReCaptcha))
			throw new Exception('TReCaptchaValidator only works with TReCaptcha controls');
		return $control;
	}

	public function getClientScriptOptions()
	{
		$options = parent::getClientScriptOptions();
		$options['ResponseFieldName'] = $this->getCaptchaControl()->getResponseFieldName();
		return $options;
	}

	/**
	 * This method overrides the parent's implementation.
	 * The validation succeeds if the input control has the same value
	 * as the one displayed in the corresponding RECAPTCHA control.
	 *
	 * @return boolean whether the validation succeeds
	 */
	protected function evaluateIsValid()
	{
		// check validity only once (if trying to evaulate multiple times, all redundant checks would fail)
		if (is_null($this->_isvalid))
		{
			$control = $this->getCaptchaControl();
			$this->_isvalid = $control->validate();
		}
		return ($this->_isvalid==true);
	}

	public function onPreRender($param)
	{
		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);
		
		// update validator display
		if ($control = $this->getValidationTarget())
		{
			$fn = 'captchaUpdateValidatorStatus_'.$this->getClientID();

			// check if we need to request a new captcha too
			if ($this->Page->IsCallback)
			{
				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");',
					'});',
				'});',
			)));
		}
	}

}