Completed
Push — namespace2 ( 8a6673...791eac )
by Fabio
08:25
created

TReCaptcha2::getActiveControl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * TReCaptcha2 class file
5
 *
6
 * @author Cristian Camilo Naranjo Valencia
7
 * @link http://icolectiva.co
8
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
9
 * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
10
 * @package System.Web.UI.WebControls
11
 */
12
13
namespace Prado\Web\UI\WebControls;
14
use Prado\TPropertyValue;
15
use Prado\Web\Javascripts\TJavaScript;
16
use Prado\Exceptions\TConfigurationException;
17
// FIXME: this controls should be moved to activecontrols
0 ignored issues
show
Coding Style introduced by
Comment refers to a FIXME task "this controls should be moved to activecontrols"
Loading history...
18
use Prado\Web\UI\ActiveControls\TActivePanel;
19
use Prado\Web\UI\ActiveControls\TActiveControlAdapter;
20
21
/**
22
 * TReCaptcha2 class.
23
 *
24
 * TReCaptcha2 displays a reCAPTCHA (a token displayed as an image) that can be used
25
 * to determine if the input is entered by a real user instead of some program. It can
26
 * also prevent multiple submits of the same form either by accident, or on purpose (ie. spamming).
27
 *
28
 * The reCAPTCHA to solve (a string consisting of two separate words) displayed is automatically
29
 * generated by the reCAPTCHA system at recaptcha.net. However, in order to use the services
30
 * of the site you will need to register and get a public and a private API key pair, and 
31
 * supply those to the reCAPTCHA control through setting the {@link setSecretKey SecretKey} 
32
 * and {@link setSiteKey SiteKey} properties. 
33
 *
34
 * Currently the reCAPTCHA API supports only one reCAPTCHA field per page, so you MUST make sure that all 
35
 * your input is protected and validated by a single reCAPTCHA control. Placing more than one reCAPTCHA
36
 * control on the page will lead to unpredictable results, and the user will most likely unable to solve 
37
 * any of them successfully.
38
 *
39
 * Upon postback, user input can be validated by calling {@link validate()}.
40
 * The {@link TReCaptcha2Validator} control can also be used to do validation, which provides
41
 * server-side validation. Calling (@link validate()) will invalidate the token supplied, so all consecutive
42
 * calls to the method - without solving a new captcha - will return false. Therefore if implementing a multi-stage
43
 * input process, you must make sure that you call validate() only once, either at the end of the input process, or 
44
 * you store the result till the end of the processing.
45
 *
46
 * The following template shows a typical use of TReCaptcha control:
47
 * <code>
48
 * <com:TReCaptcha2 ID="Captcha"
49
 *                 SiteKey="..."
50
 *                 SecretKey="..."
51
 * />
52
 * <com:TReCaptcha2Validator ControlToValidate="Captcha"
53
 *                          ErrorMessage="You are challenged!" />
54
 * </code>
55
 *
56
 * @author Cristian Camilo Naranjo Valencia
57
 * @package System.Web.UI.WebControls
58
 * @since 3.3.1
59
 */
60
61
class TReCaptcha2 extends TActivePanel implements \Prado\Web\UI\ActiveControls\ICallbackEventHandler, \Prado\Web\UI\IValidatable
62
{
63
    const ChallengeFieldName = 'g-recaptcha-response';
64
    private $_widgetId=0;
0 ignored issues
show
Unused Code introduced by
The property $_widgetId is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
65
    private $_isValid=true;
66
67
    public function __construct()
68
    {
69
        parent::__construct();
70
        $this->setAdapter(new TActiveControlAdapter($this));
71
    }
72
    public function getActiveControl()
73
    {
74
        return $this->getAdapter()->getBaseActiveControl();
75
    }
76
    public function getClientSide()
77
    {
78
        return $this->getAdapter()->getBaseActiveControl()->getClientSide();
79
    }
80
    public function getClientClassName()
81
    {
82
        return 'Prado.WebUI.TReCaptcha2';
83
    }
84
    public function getTagName()
85
    {
86
        return 'div';
87
    }
88
    /**
89
     * Returns true if this control validated successfully. 
90
     * Defaults to true.
91
     * @return bool wether this control validated successfully.
92
     */
93
    public function getIsValid()
94
    {
95
        return $this->_isValid;
96
    }
97
    /**
98
     * @param bool wether this control is valid.
99
     */
100
    public function setIsValid($value)
101
    {
102
        $this->_isValid=TPropertyValue::ensureBoolean($value);
103
    }
104
    public function getValidationPropertyValue()
105
    {
106
        return $this->Request[$this->getResponseFieldName()];
107
    }
108
    public function getResponseFieldName()
109
    {
110
        $captchas = $this->Page->findControlsByType('TReCaptcha2');
111
        $cont = 0;
112
        $responseFieldName = self::ChallengeFieldName;
113
        foreach ($captchas as $captcha)
114
        {
115
            if ($this->getClientID() == $captcha->ClientID)
116
            {
117
                $responseFieldName .= ($cont > 0) ? '-'.$cont : '';
118
            }
119
            $cont++;
120
        }
121
        return $responseFieldName;
122
    }
123
    /**
124
     * Returns your site key. 
125
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
126
     */
127
    public function getSiteKey()
128
    {
129
        return $this->getViewState('SiteKey');
130
    }
131
    /**
132
     * @param string your site key.
133
     */
134
    public function setSiteKey($value)
135
    {
136
        $this->setViewState('SiteKey', TPropertyValue::ensureString($value));
137
    }
138
    /**
139
     * Returns your secret key. 
140
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
141
     */
142
    public function getSecretKey()
143
    {
144
        return $this->getViewState('SecretKey');
145
    }
146
    /**
147
     * @param string your secret key.
148
     */
149
    public function setSecretKey($value)
150
    {
151
        $this->setViewState('SecretKey', TPropertyValue::ensureString($value));
152
    }
153
    /**
154
     * Returns your language.
155
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
156
     */
157
    public function getLanguage()
158
    {
159
        return $this->getViewState('Language', 'en');
160
    }
161
    /**
162
     * @param string your language.
163
     */
164
    public function setLanguage($value)
165
    {
166
        $this->setViewState('Language', TPropertyValue::ensureString($value), 'en');
167
    }
168
    /**
169
     * Returns the color theme of the widget. 
170
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
171
     */
172
    public function getTheme()
173
    {
174
        return $this->getViewState('Theme', 'light');
175
    }
176
    /**
177
     * The color theme of the widget.
178
     * Default: light
179
     * @param string the color theme of the widget.
180
     */
181
    public function setTheme($value)
182
    {
183
        $this->setViewState('Theme', TPropertyValue::ensureString($value), 'light');
184
    }
185
    /**
186
     * Returns the type of CAPTCHA to serve.
187
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
188
     */
189
    public function getType()
190
    {
191
        return $this->getViewState('Type', 'image');
192
    }
193
    /**
194
     * The type of CAPTCHA to serve.
195
     * Default: image
196
     * @param string the type of CAPTCHA to serve.
197
     */
198
    public function setType($value)
199
    {
200
        $this->setViewState('Type', TPropertyValue::ensureString($value), 'image');
201
    }
202
    /**
203
     * Returns the size of the widget.
204
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
205
     */
206
    public function getSize()
207
    {
208
        return $this->getViewState('Size', 'normal');
209
    }
210
    /**
211
     * The size of the widget.
212
     * Default: normal
213
     * @param string the size of the widget.
214
     */
215
    public function setSize($value)
216
    {
217
        $this->setViewState('Size', TPropertyValue::ensureString($value), 'normal');
218
    }
219
    /**
220
     * Returns the tabindex of the widget and challenge.
221
     * If other elements in your page use tabindex, it should be set to make user navigation easier.
222
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
223
     */
224
    public function getTabIndex()
225
    {
226
        return $this->getViewState('TabIndex', 0);
227
    }
228
    /**
229
     * The tabindex of the widget and challenge.
230
     * If other elements in your page use tabindex, it should be set to make user navigation easier.
231
     * Default: 0
232
     * @param string the tabindex of the widget and challenge.
233
     */
234
    public function setTabIndex($value)
235
    {
236
        $this->setViewState('TabIndex', TPropertyValue::ensureInteger($value), 0);
237
    }
238
    /**
239
     * Resets the reCAPTCHA widget.
240
     * Optional widget ID, defaults to the first widget created if unspecified.
241
     */
242
    public function reset()
243
    {
244
        $this->Page->CallbackClient->callClientFunction('grecaptcha.reset',array(array($this->WidgetId)));
0 ignored issues
show
Bug introduced by
The property WidgetId does not seem to exist. Did you mean _widgetId?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
245
    }
246
    /**
247
     * Gets the response for the reCAPTCHA widget.
248
     */
249
    public function getResponse()
250
    {
251
        return $this->getViewState('Response', '');
252
    }
253
    public function setResponse($value)
254
    {
255
        $this->setViewState('Response', TPropertyValue::ensureString($value), '');
256
    }
257
    public function getWidgetId()
258
    {
259
        return $this->getViewState('WidgetId', 0);
260
    }
261
    public function setWidgetId($value)
262
    {
263
        $this->setViewState('WidgetId', TPropertyValue::ensureInteger($value), 0);
264
    }
265
    protected function getClientOptions()
266
    {
267
        $options['ID'] = $this->getClientID();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
268
        $options['EventTarget'] = $this->getUniqueID();
269
        $options['FormID'] = $this->Page->getForm()->getClientID();
270
        $options['onCallback'] = $this->hasEventHandler('OnCallback');
271
        $options['onCallbackExpired'] = $this->hasEventHandler('OnCallbackExpired');
272
        $options['options']['sitekey'] = $this->getSiteKey();
273
        if ($theme = $this->getTheme()) $options['options']['theme'] = $theme;
274
        if ($type = $this->getType()) $options['options']['type'] = $type;
275
        if ($size = $this->getSize()) $options['options']['size'] = $size;
276
        if ($tabIndex = $this->getTabIndex()) $options['options']['tabindex'] = $tabIndex;
277
278
        return $options;
279
    }
280
    protected function registerClientScript()
281
    {
282
        $id         = $this->getClientID();
283
        $options    = TJavaScript::encode($this->getClientOptions());
284
        $className  = $this->getClientClassName();
285
        $cs         = $this->Page->ClientScript;
286
        $code       = "new $className($options);";
287
288
        $cs->registerPradoScript('ajax');
289
        $cs->registerEndScript("grecaptcha:$id", $code);
290
    }
291
    public function validate()
292
    {
293
        $value = $this->getValidationPropertyValue();
294
        if($value === null || empty($value))
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !($value === null || empty($value));.
Loading history...
295
            return false;
296
297
        return true;
298
    }
299
    /**
300
     * Checks for API keys
301
     * @param mixed event parameter
302
     */
303
    public function onPreRender($param)
304
    {
305
        parent::onPreRender($param);
306
307
        if("" == $this->getSiteKey())
308
            throw new TConfigurationException('recaptcha_publickey_unknown');
309
        if("" == $this->getSecretKey())
310
            throw new TConfigurationException('recaptcha_privatekey_unknown');
311
312
        // need to register captcha fields so they will be sent postback
313
        $this->Page->registerRequiresPostData($this->getResponseFieldName());
314
        $this->Page->ClientScript->registerHeadScriptFile('grecaptcha2', 'https://www.google.com/recaptcha/api.js?onload=TReCaptcha2_onloadCallback&render=explicit&hl=' . $this->getLanguage());
315
    }
316
    protected function addAttributesToRender($writer)
317
    {
318
        $writer->addAttribute('id',$this->getClientID());
319
        parent::addAttributesToRender($writer);
320
    }
321
    public function raiseCallbackEvent($param)
322
    {
323
        $params = $param->getCallbackParameter();
324
        if ($params instanceof stdClass)
0 ignored issues
show
Bug introduced by
The class Prado\Web\UI\WebControls\stdClass does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
325
        {
326
            $callback = property_exists($params, 'onCallback');
327
            $callbackExpired = property_exists($params, 'onCallbackExpired');
328
329
            if ($callback)
330
            {
331
                $this->WidgetId = $params->widgetId;
0 ignored issues
show
Bug introduced by
The property WidgetId does not seem to exist. Did you mean _widgetId?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
332
                $this->Response = $params->response;
333
                $this->Page->CallbackClient->jQuery($params->responseField, 'text',array($params->response));
334
335
                if ($params->onCallback)
336
                {
337
                    $this->onCallback($param);
338
                }
339
            }
340
341
            if ($callbackExpired)
342
            {
343
                $this->Response = '';
344
                $this->reset();
345
346
                if ($params->onCallbackExpired)
347
                {
348
                    $this->onCallbackExpired($param);
349
                }
350
            }
351
        }
352
    }
353
354
    public function onCallback($param)
355
    {
356
        $this->raiseEvent('OnCallback', $this, $param);
357
    }
358
359
    public function onCallbackExpired($param)
360
    {
361
        $this->raiseEvent('OnCallbackExpired', $this, $param);
362
    }
363
364
    public function render($writer)
365
    {
366
        $this->registerClientScript();
367
        parent::render($writer);
368
    }
369
}
370