Completed
Pull Request — master (#80)
by ARCANEDEV
05:46
created

NoCaptchaV2::checkDataAttribute()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 4
dl 0
loc 12
ccs 8
cts 8
cp 1
crap 4
rs 9.8666
c 0
b 0
f 0
1
<?php namespace Arcanedev\NoCaptcha;
2
3
use Arcanedev\Html\Elements\Button;
4
use Arcanedev\Html\Elements\Div;
5
use Arcanedev\NoCaptcha\Exceptions\InvalidArgumentException;
6
use Arcanedev\NoCaptcha\Utilities\ResponseV2;
7
use Illuminate\Support\Arr;
8
9
/**
10
 * Class     NoCaptchaV2
11
 *
12
 * @package  Arcanedev\NoCaptcha
13
 * @author   ARCANEDEV <[email protected]>
14
 */
15
class NoCaptchaV2 extends AbstractNoCaptcha
16
{
17
    /* -----------------------------------------------------------------
18
     |  Properties
19
     | -----------------------------------------------------------------
20
     */
21
22
    /**
23
     * Decides if we've already loaded the script file or not.
24
     *
25
     * @param bool
26
     */
27
    protected $scriptLoaded = false;
28
29
    /**
30
     * noCaptcha Attributes
31
     *
32
     * @var array
33
     */
34
    protected $attributes;
35
36
    /* -----------------------------------------------------------------
37
     |  Constructor
38
     | -----------------------------------------------------------------
39
     */
40
41
    /**
42
     * NoCaptcha constructor.
43
     *
44
     * @param  string       $secret
45
     * @param  string       $siteKey
46
     * @param  string|null  $lang
47
     * @param  array        $attributes
48
     */
49 128
    public function __construct($secret, $siteKey, $lang = null, array $attributes = [])
50
    {
51 128
        parent::__construct($secret, $siteKey, $lang);
52
53 128
        $this->setAttributes($attributes);
54 128
    }
55
56
    /* -----------------------------------------------------------------
57
     |  Getters & Setters
58
     | -----------------------------------------------------------------
59
     */
60
61
    /**
62
     * Get script source link.
63
     *
64
     * @param  string|null  $callbackName
65
     *
66
     * @return string
67
     */
68 20
    private function getScriptSrc($callbackName = null)
69
    {
70 20
        $queries = [];
71
72 20
        if ($this->hasLang())
73 8
            Arr::set($queries, 'hl', $this->lang);
74
75 20
        if ($this->hasCallbackName($callbackName)) {
76 4
            Arr::set($queries, 'onload', $callbackName);
77 4
            Arr::set($queries, 'render', 'explicit');
78
        }
79
80 20
        return static::CLIENT_URL . (count($queries) ? '?' . http_build_query($queries) : '');
81
    }
82
83
    /**
84
     * Set noCaptcha Attributes.
85
     *
86
     * @param  array  $attributes
87
     *
88
     * @return self
89
     */
90 128
    public function setAttributes(array $attributes)
91
    {
92 128
        $this->attributes = $attributes;
93
94 128
        return $this;
95
    }
96
97
    /* -----------------------------------------------------------------
98
     |  Main Methods
99
     | -----------------------------------------------------------------
100
     */
101
102
    /**
103
     * Display Captcha.
104
     *
105
     * @param  string|null  $name
106
     * @param  array        $attributes
107
     *
108
     * @return \Arcanedev\Html\Elements\Div
109
     */
110 76
    public function display($name = null, array $attributes = [])
111
    {
112 76
        return Div::make()->attributes(array_merge(
113 76
            static::prepareNameAttribute($name),
114 64
            $this->prepareAttributes($attributes)
115
        ));
116
    }
117
118
    /**
119
     * Display image Captcha.
120
     *
121
     * @param  string|null  $name
122
     * @param  array        $attributes
123
     *
124
     * @return \Arcanedev\Html\Elements\Div
125
     */
126 12
    public function image($name = null, array $attributes = [])
127
    {
128 12
        return $this->display($name, array_merge(
129 9
            $attributes,
130 12
            ['data-type' => 'image']
131
        ));
132
    }
133
134
    /**
135
     * Display audio Captcha.
136
     *
137
     * @param  string|null  $name
138
     * @param  array        $attributes
139
     *
140
     * @return \Arcanedev\Html\Elements\Div
141
     */
142 12
    public function audio($name = null, array $attributes = [])
143
    {
144 12
        return $this->display($name, array_merge(
145 9
            $attributes,
146 12
            ['data-type' => 'audio']
147
        ));
148
    }
149
150
    /**
151
     * Display an invisible Captcha (bind the challenge to a button).
152
     *
153
     * @param  string  $value
154
     * @param  array   $attributes
155
     *
156
     * @return \Arcanedev\Html\Elements\Button
157
     */
158 4
    public function button($value, array $attributes = [])
159
    {
160 4
        return Button::make()->text($value)->attributes(array_merge(
161 4
            ['data-callback' => 'onSubmit'],
162 4
            $this->prepareAttributes($attributes)
163
        ));
164
    }
165
166
    /**
167
     * Get script tag.
168
     *
169
     * @param  string|null  $callbackName
170
     *
171
     * @return \Illuminate\Support\HtmlString
172
     */
173 20
    public function script($callbackName = null)
174
    {
175 20
        $script = '';
176
177 20
        if ( ! $this->scriptLoaded) {
178 20
            $script = '<script src="'.$this->getScriptSrc($callbackName).'" async defer></script>';
179 20
            $this->scriptLoaded = true;
180
        }
181
182 20
        return $this->toHtmlString($script);
183
    }
184
185
    /**
186
     * Get the NoCaptcha API Script.
187
     *
188
     * @return \Illuminate\Support\HtmlString
189
     */
190 4
    public function getApiScript()
191
    {
192 4
        return $this->toHtmlString(
193 3
            "<script>
194
                window.noCaptcha = {
195
                    captchas: [],
196
                    reset: function(name) {
197
                        var captcha = window.noCaptcha.get(name);
198
        
199
                        if (captcha)
200
                            window.noCaptcha.resetById(captcha.id);
201
                    },
202
                    resetById: function(id) {
203
                        grecaptcha.reset(id);
204
                    },
205
                    get: function(name) {
206
                        return window.noCaptcha.find(function (captcha) {
207
                            return captcha.name === name;
208
                        });
209
                    },
210
                    getById: function(id) {
211
                        return window.noCaptcha.find(function (captcha) {
212
                            return captcha.id === id;
213
                        });
214
                    },
215
                    find: function(callback) {
216
                        return window.noCaptcha.captchas.find(callback);
217
                    },
218
                    render: function(name, sitekey) {
219
                        var captcha = {
220
                            id: grecaptcha.render(name, {'sitekey' : sitekey}),
221
                            name: name
222
                        };
223
                        
224
                        window.noCaptcha.captchas.push(captcha);
225
                        
226
                        return captcha;
227
                    }
228
                }
229 1
            </script>"
230
        );
231
    }
232
233
    /**
234
     * Get script tag with a callback function.
235
     *
236
     * @param  array   $captchas
237
     * @param  string  $callbackName
238
     *
239
     * @return \Illuminate\Support\HtmlString
240
     */
241 4
    public function scriptWithCallback(array $captchas, $callbackName = 'captchaRenderCallback')
242
    {
243 4
        $script = $this->script($callbackName)->toHtml();
244
245 4
        if ( ! empty($script) && ! empty($captchas)) {
246 4
            $script = implode(PHP_EOL, [
247 4
                $this->getApiScript()->toHtml(),
248 4
                '<script>',
249 4
                    "var $callbackName = function() {",
250 4
                        $this->renderCaptchas($captchas),
251 4
                    '};',
252 4
                '</script>',
253 4
                $script,
254
            ]);
255
        }
256
257 4
        return $this->toHtmlString($script);
258
    }
259
260
    /**
261
     * Rendering captchas with callback function.
262
     *
263
     * @param  array  $captchas
264
     *
265
     * @return string
266
     */
267 4
    private function renderCaptchas(array $captchas)
268
    {
269
        return implode(PHP_EOL, array_map(function($captcha) {
270 4
            return "if (document.getElementById('{$captcha}')) { window.noCaptcha.render('{$captcha}', '{$this->siteKey}'); }";
271 4
        }, $captchas));
272
    }
273
274
    /* -----------------------------------------------------------------
275
     |  Check Methods
276
     | -----------------------------------------------------------------
277
     */
278
279
    /**
280
     * Check if callback is not empty.
281
     *
282
     * @param  string|null  $callbackName
283
     *
284
     * @return bool
285
     */
286 20
    private function hasCallbackName($callbackName)
287
    {
288 20
        return ! (is_null($callbackName) || trim($callbackName) === '');
289
    }
290
291
    /* -----------------------------------------------------------------
292
     |  Other Methods
293
     | -----------------------------------------------------------------
294
     */
295
296
    /**
297
     * Parse the response.
298
     *
299
     * @param  string  $json
300
     *
301
     * @return \Arcanedev\NoCaptcha\Utilities\AbstractResponse|mixed
302
     */
303 12
    protected function parseResponse($json)
304
    {
305 12
        return ResponseV2::fromJson($json);
306
    }
307
308
    /**
309
     * Prepare the attributes.
310
     *
311
     * @param  array  $attributes
312
     *
313
     * @return array
314
     */
315 68
    private function prepareAttributes(array $attributes)
316
    {
317 68
        $attributes = array_merge(
318 68
            array_filter($this->attributes),
319 68
            ['class' => 'g-recaptcha', 'data-sitekey' => $this->siteKey],
320 51
            array_filter($attributes)
321
        );
322
323 68
        self::checkDataAttribute($attributes, 'data-type', ['image', 'audio'], 'image');
324 68
        self::checkDataAttribute($attributes, 'data-theme', ['light', 'dark'], 'light');
325 68
        self::checkDataAttribute($attributes, 'data-size', ['normal', 'compact', 'invisible'], 'normal');
326 68
        self::checkDataAttribute($attributes, 'data-badge', ['bottomright', 'bottomleft', 'inline'], 'bottomright');
327
328 68
        return $attributes;
329
    }
330
331
    /**
332
     * Check the `data-*` attribute.
333
     *
334
     * @param  array   $attributes
335
     * @param  string  $name
336
     * @param  array   $supported
337
     * @param  string  $default
338
     */
339 68
    private static function checkDataAttribute(array &$attributes, $name, array $supported, $default)
340
    {
341 68
        $attribute = $attributes[$name] ?? null;
342
343 68
        if ( ! is_null($attribute)) {
344 60
            $attribute = (is_string($attribute) && in_array($attribute, $supported))
345 44
                ? strtolower(trim($attribute))
346 60
                : $default;
347
348 60
            $attributes[$name] = $attribute;
349
        }
350 68
    }
351
352
    /**
353
     * Prepare the name and id attributes.
354
     *
355
     * @param  string|null  $name
356
     *
357
     * @return array
358
     *
359
     * @throws \Arcanedev\NoCaptcha\Exceptions\InvalidArgumentException
360
     */
361 76
    protected static function prepareNameAttribute($name)
362
    {
363 76
        if (is_null($name))
364 4
            return [];
365
366 72
        if ($name === AbstractNoCaptcha::CAPTCHA_NAME) {
367 12
            throw new InvalidArgumentException(
368 12
                'The captcha name must be different from "' . AbstractNoCaptcha::CAPTCHA_NAME . '".'
369
            );
370
        }
371
372 60
        return array_combine(['id', 'name'], [$name, $name]);
373
    }
374
}
375