Completed
Push — master ( 5b5792...2b84bd )
by Schlaefer
03:33 queued 11s
created

Upload::_getUploadParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Plugin\BbcodeParser\src\Lib\jBBCode\Definitions;
4
5
use Cake\Cache\Cache;
6
use Cake\Core\Configure;
7
use Plugin\BbcodeParser\src\Lib\Helper\Message;
8
use Plugin\BbcodeParser\src\Lib\Helper\UrlParserTrait;
9
use Saito\DomainParser;
10
11
/**
12
 * Class Email handles [email][email protected][/email]
13
 *
14
 * @package Saito\Jbb\CodeDefinition
15
 */
16
class Email extends CodeDefinition
17
{
18
    use UrlParserTrait;
19
20
    protected $_sParseContent = false;
21
22
    protected $_sTagName = 'email';
23
24
    /**
25
     * {@inheritDoc}
26
     */
27
    protected function _parse($url, $attributes, \JBBCode\ElementNode $node)
28
    {
29
        return $this->_email($url);
30
    }
31
}
32
33
/**
34
 * Class EmailWithAttributes handles [[email protected]]foobar[/email]
35
 *
36
 * @package Saito\Jbb\CodeDefinition
37
 */
38
//@codingStandardsIgnoreStart
39
class EmailWithAttributes extends Email
40
//@codingStandardsIgnoreEnd
41
{
42
    protected $_sUseOptions = true;
43
44
    /**
45
     * {@inheritDoc}
46
     */
47
    protected function _parse($content, $attributes, \JBBCode\ElementNode $node)
48
    {
49
        return $this->_email($attributes['email'], $content);
50
    }
51
}
52
53
//@codingStandardsIgnoreStart
54
class Embed extends CodeDefinition
55
//@codingStandardsIgnoreEnd
56
{
57
    protected $_sTagName = 'embed';
58
59
    protected $_sParseContent = false;
60
61
    /**
62
     * {@inheritDoc}
63
     */
64
    protected function _parse($url, $attributes, \JBBCode\ElementNode $node)
65
    {
66
        if (!$this->_sOptions->get('content_embed_active')) {
0 ignored issues
show
Bug introduced by
The method get cannot be called on $this->_sOptions (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
67
            if (!$this->_sOptions->get('autolink')) {
0 ignored issues
show
Bug introduced by
The method get cannot be called on $this->_sOptions (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
68
                return $url;
69
            }
70
71
            return $this->Html->link($url, $url, ['target' => '_blank']);
72
        }
73
74
        $loader = function () use ($url) {
75
            $embed = ['url' => $url];
76
77
            try {
78
                $info = \Embed\Embed::create(
79
                    $url,
80
                    [
81
                    'min_image_width' => 100,
82
                    'min_image_height' => 100,
83
                    ]
84
                );
85
86
                $embed = [
87
                    'html' => $info->code,
88
                    'providerIcon' => $info->providerIcon,
89
                    'providerName' => $info->providerName,
90
                    'providerUrl' => $info->providerUrl,
91
                    'title' => $info->title,
92
                    'url' => $info->url ?? $url,
93
                ];
94
95
                if ($this->_sOptions->get('content_embed_text')) {
0 ignored issues
show
Bug introduced by
The method get cannot be called on $this->_sOptions (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
96
                    $embed['description'] = $info->description;
97
                }
98
99
                if ($this->_sOptions->get('content_embed_media')) {
0 ignored issues
show
Bug introduced by
The method get cannot be called on $this->_sOptions (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
100
                    $embed['image'] = $info->image;
101
                }
102
            } catch (\Throwable $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
103
            }
104
105
            return $embed;
106
        };
107
108
        $callable = \Closure::fromCallable($loader);
109
110
        $uid = 'embed-' . md5($url);
111
        $info = Cache::remember($uid, $callable, 'bbcodeParserEmbed');
112
113
        return $this->_sHelper->Html->div('js-embed', '', ['id' => $uid, 'data-embed' => json_encode($info)]);
114
    }
115
}
116
117
//@codingStandardsIgnoreStart
118
class Iframe extends CodeDefinition
119
//@codingStandardsIgnoreEnd
120
{
121
    protected $_sTagName = 'iframe';
122
123
    protected $_sParseContent = false;
124
125
    protected $_sUseOptions = true;
126
127
    /**
128
     * Array with domains from which embedding video is allowed
129
     *
130
     * array(
131
     *  'youtube' => 1,
132
     *  'vimeo' => 1,
133
     * );
134
     *
135
     * array('*' => 1) means every domain allowed
136
     *
137
     * @var array
138
     */
139
    protected $_allowedVideoDomains = null;
140
141
    /**
142
     * {@inheritDoc}
143
     */
144
    protected function _parse($url, $attributes, \JBBCode\ElementNode $node)
145
    {
146
        if (empty($attributes['src'])) {
147
            return false;
148
        }
149
150
        unset($attributes['iframe']);
151
152
        $allowed = $this->_checkHostAllowed($attributes['src']);
153
        if ($allowed !== true) {
154
            return $allowed;
155
        }
156
157
        if (strpos($attributes['src'], '?') === false) {
158
            $attributes['src'] .= '?';
159
        }
160
        $attributes['src'] .= '&amp;wmode=Opaque';
161
162
        $atrStr = '';
163
        foreach ($attributes as $attributeName => $attributeValue) {
164
            $atrStr .= "$attributeName=\"$attributeValue\" ";
165
        }
166
        $atrStr = rtrim($atrStr);
167
168
        $html = <<<eof
169
<div class="embed-responsive embed-responsive-16by9">
170
    <iframe class="embed-responsive-item" {$atrStr}></iframe>
171
</div>
172
eof;
173
174
        return $html;
175
    }
176
177
    /**
178
     * get allowed domains
179
     *
180
     * @return array
181
     */
182
    protected function _allowedDomains()
183
    {
184
        if ($this->_allowedVideoDomains !== null) {
185
            return $this->_allowedVideoDomains;
186
        }
187
188
        $ad = explode('|', $this->_sOptions->get('video_domains_allowed'));
0 ignored issues
show
Bug introduced by
The method get cannot be called on $this->_sOptions (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
189
        $trim = function ($v) {
190
            return trim($v);
191
        };
192
        $this->_allowedVideoDomains = array_fill_keys(array_map($trim, $ad), 1);
193
194
        return $this->_allowedVideoDomains;
195
    }
196
197
    /**
198
     * Check host allowed
199
     *
200
     * @param string $url url
201
     *
202
     * @return bool|string
203
     */
204
    protected function _checkHostAllowed($url)
205
    {
206
        $allowedDomains = $this->_allowedDomains();
207
        if (empty($allowedDomains)) {
208
            return false;
209
        }
210
211
        if ($allowedDomains === ['*' => 1]) {
212
            return true;
213
        }
214
215
        $host = DomainParser::domain($url);
216
        if ($host && isset($allowedDomains[$host])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $host of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
217
            return true;
218
        }
219
220
        $message = sprintf(
221
            __('Domain <strong>%s</strong> not allowed for embedding video.'),
222
            $host
223
        );
224
225
        return Message::format($message);
226
    }
227
}
228
229
//@codingStandardsIgnoreStart
230
class Flash extends Iframe
231
//@codingStandardsIgnoreEnd
232
{
233
    protected $_sTagName = 'flash_video';
234
235
    protected $_sParseContent = false;
236
237
    protected $_sUseOptions = false;
238
239
    protected static $_flashVideoDomainsWithHttps = [
240
        'vimeo' => 1,
241
        'youtube' => 1
242
    ];
243
244
    /**
245
     * {@inheritDoc}
246
     */
247
    protected function _parse($content, $attributes, \JBBCode\ElementNode $node)
248
    {
249
        $match = preg_match(
250
            "#(?P<url>.+?)\|(?P<width>.+?)\|(?<height>\d+)#is",
251
            $content,
252
            $matches
253
        );
254
        if (!$match) {
255
            return Message::format(__('No Flash detected.'));
256
        }
257
258
        $height = $matches['height'];
259
        $url = $matches['url'];
260
        $width = $matches['width'];
261
262
        $allowed = $this->_checkHostAllowed($url);
263
        if ($allowed !== true) {
264
            return $allowed;
265
        }
266
267
        if (env('HTTPS')) {
268
            $host = DomainParser::domain($url);
269
            if (isset(self::$_flashVideoDomainsWithHttps[$host])) {
270
                $url = str_ireplace('http://', 'https://', $url);
271
            }
272
        }
273
274
        $out = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="' . $width . '" height="' . $height . '">
275
									<param name="movie" value="' . $url . '"></param>
276
									<embed src="' . $url . '" width="' . $width . '" height="' . $height . '" type="application/x-shockwave-flash" wmode="opaque" style="width:' . $width . 'px; height:' . $height . 'px;" id="VideoPlayback" flashvars=""> </embed> </object>';
277
278
        return $out;
279
    }
280
}
281
282
//@codingStandardsIgnoreStart
283
class FileWithAttributes extends CodeDefinition
284
//@codingStandardsIgnoreEnd
285
{
286
    use UrlParserTrait;
287
288
    protected $_sTagName = 'file';
289
290
    protected $_sParseContent = false;
291
292
    protected $_sUseOptions = true;
293
294
    /**
295
     * {@inheritDoc}
296
     */
297
    protected function _parse($content, $attributes, \JBBCode\ElementNode $node)
298
    {
299
        if (empty($attributes['src']) || $attributes['src'] !== 'upload') {
300
            $message = sprintf(__('File not allowed.'));
301
302
            return Message::format($message);
303
        }
304
305
        $url = $this->_linkToUploadedFile($content);
306
307
        return $this->_sHelper->Html->link($content, $url, ['target' => '_blank']);
308
    }
309
}
310
311
//@codingStandardsIgnoreStart
312
class Image extends CodeDefinition
313
//@codingStandardsIgnoreEnd
314
{
315
    use UrlParserTrait;
316
317
    protected $_sTagName = 'img';
318
319
    protected $_sParseContent = false;
320
321
    /**
322
     * {@inheritDoc}
323
     */
324
    protected function _parse($url, $attributes, \JBBCode\ElementNode $node)
325
    {
326
        // image is internaly uploaded
327
        if (!empty($attributes['src']) && $attributes['src'] === 'upload') {
328
            $url = $this->_linkToUploadedFile($url);
329
        }
330
331
        // process [img=(parameters)]
332
        $options = [];
333
        if (!empty($attributes['img'])) {
334
            $default = trim($attributes['img']);
335
            switch ($default) {
336
                default:
0 ignored issues
show
Unused Code introduced by
default: preg_match(...ion[2]; } } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
337
                    preg_match(
338
                        '/(\d{0,3})(?:x(\d{0,3}))?/i',
339
                        $default,
340
                        $dimension
341
                    );
342
                    // $dimension for [img=50] or [img=50x100]
343
                    // [0] (50) or (50x100)
344
                    // [1] (50)
345
                    // [2] (100)
346
                    if (!empty($dimension[1])) {
347
                        $options['width'] = $dimension[1];
348
                        if (!empty($dimension[2])) {
349
                            $options['height'] = $dimension[2];
350
                        }
351
                    }
352
            }
353
        }
354
355
        $url = $this->_urlToHttps($url);
356
        $image = $this->Html->image($url, $options);
357
358
        if ($node->getParent()->getTagName() === 'Document') {
359
            $image = $this->_sHelper->Html->link(
360
                $image,
361
                $url,
362
                ['escape' => false, 'target' => '_blank']
363
            );
364
        }
365
366
        return $image;
367
    }
368
}
369
370
//@codingStandardsIgnoreStart
371
class ImageWithAttributes extends Image
372
//@codingStandardsIgnoreEnd
373
{
374
    protected $_sUseOptions = true;
375
}
376
377
/**
378
 * Class UlList handles [list][*]…[/list]
379
 *
380
 * @see https://gist.github.com/jbowens/5646994
381
 * @package Saito\Jbb\CodeDefinition
382
 */
383
//@codingStandardsIgnoreStart
384
class UlList extends CodeDefinition
385
//@codingStandardsIgnoreEnd
386
{
387
    protected $_sTagName = 'list';
388
389
    /**
390
     * {@inheritDoc}
391
     */
392
    protected function _parse($content, $attributes, \JBBCode\ElementNode $node)
393
    {
394
        $listPieces = explode('[*]', $content);
395
        unset($listPieces[0]);
396
        $listPieceProcessor = function ($li) {
397
            return '<li>' . $li . '</li>' . "\n";
398
        };
399
        $listPieces = array_map($listPieceProcessor, $listPieces);
400
401
        return '<ul>' . implode('', $listPieces) . '</ul>';
402
    }
403
}
404
405
//@codingStandardsIgnoreStart
406
class Spoiler extends CodeDefinition
407
//@codingStandardsIgnoreEnd
408
{
409
    protected $_sTagName = 'spoiler';
410
411
    /**
412
     * {@inheritDoc}
413
     */
414
    protected function _parse($content, $attributes, \JBBCode\ElementNode $node)
415
    {
416
        $length = mb_strlen(strip_tags($content));
417
        $minLenght = mb_strlen(__('Spoiler')) + 4;
418
        if ($length < $minLenght) {
419
            $length = $minLenght;
420
        }
421
422
        $title = $this->_mbStrpad(
423
            ' ' . __('Spoiler') . ' ',
424
            $length,
425
            '▇',
426
            STR_PAD_BOTH
427
        );
428
429
        $json = json_encode(['string' => $content]);
430
        $id = 'spoiler_' . rand(0, 9999999999999);
431
432
        $out = <<<EOF
433
<div class="richtext-spoiler" style="display: inline;">
434
	<script>
435
		window.$id = $json;
436
	</script>
437
	<a href="#" class="richtext-spoiler-link"
438
		onclick='this.parentNode.innerHTML = window.$id.string; delete window.$id; return false;'
439
		>
440
		$title
441
	</a>
442
</div>
443
EOF;
444
445
        return $out;
446
    }
447
448
    /**
449
     * Strpad
450
     *
451
     * @see http://www.php.net/manual/en/function.str-pad.php#111147
452
     *
453
     * @param string $str string
454
     * @param int $padLen length
455
     * @param string $padStr padding
456
     * @param int $dir direction
457
     *
458
     * @return null|string
459
     */
460
    protected function _mbStrpad(
461
        $str,
462
        $padLen,
463
        $padStr = ' ',
464
        $dir = STR_PAD_RIGHT
465
    ) {
466
        $strLen = mb_strlen($str);
467
        $padStrLen = mb_strlen($padStr);
468
        if (!$strLen && ($dir == STR_PAD_RIGHT || $dir == STR_PAD_LEFT)) {
469
            $strLen = 1; // @debug
470
        }
471
        if (!$padLen || !$padStrLen || $padLen <= $strLen) {
472
            return $str;
473
        }
474
475
        $result = null;
476
        $repeat = ceil($strLen - $padStrLen + $padLen);
477
        if ($dir == STR_PAD_RIGHT) {
478
            $result = $str . str_repeat($padStr, $repeat);
479
            $result = mb_substr($result, 0, $padLen);
480
        } else {
481
            if ($dir == STR_PAD_LEFT) {
482
                $result = str_repeat($padStr, $repeat) . $str;
483
                $result = mb_substr($result, -$padLen);
484
            } else {
485
                if ($dir == STR_PAD_BOTH) {
486
                    $length = ($padLen - $strLen) / 2;
487
                    $repeat = ceil($length / $padStrLen);
488
                    $result = mb_substr(str_repeat($padStr, $repeat), 0, floor($length)) .
489
                        $str .
490
                        mb_substr(str_repeat($padStr, $repeat), 0, ceil($length));
491
                }
492
            }
493
        }
494
495
        return $result;
496
    }
497
}
498
499
/**
500
 * Hanldes [upload]<image>[/upload]
501
 *
502
 * @deprecated since Saito 5.2; kept for backwards compatability
503
 */
504
//@codingStandardsIgnoreStart
505
class Upload extends Image
506
//@codingStandardsIgnoreEnd
507
{
508
    protected $_sTagName = 'upload';
509
510
    /**
511
     * {@inheritDoc}
512
     */
513
    protected function _parse($content, $attributes, \JBBCode\ElementNode $node)
514
    {
515
        $attributes['src'] = 'upload';
516
        if (!empty($attributes['width'])) {
517
            $attributes['img'] = $attributes['width'];
518
        }
519
        if (!empty($attributes['height'])) {
520
            $attributes['img'] .= 'x' . $attributes['height'];
521
        }
522
523
        return parent::_parse($content, $attributes, $node);
524
    }
525
}
526
527
/**
528
 * Hanldes [upload width=<width> height=<height>]<image>[/upload]
529
 *
530
 * @deprecated since Saito 5.2; kept for backwards compatability
531
 */
532
//@codingStandardsIgnoreStart
533
class UploadWithAttributes extends Upload
534
//@codingStandardsIgnoreEnd
535
{
536
    protected $_sUseOptions = true;
537
}
538
539
/**
540
 * Class Url handles [url]http://example.com[/url]
541
 *
542
 * @package Saito\Jbb\CodeDefinition
543
 */
544
//@codingStandardsIgnoreStart
545
class Url extends CodeDefinition
546
//@codingStandardsIgnoreEnd
547
{
548
    use UrlParserTrait;
549
550
    protected $_sParseContent = false;
551
552
    protected $_sTagName = 'url';
553
554
    /**
555
     * {@inheritDoc}
556
     */
557
    protected function _parse($url, $attributes, \JBBCode\ElementNode $node)
558
    {
559
        $defaults = ['label' => true];
560
        // parser may return $attributes = null
561
        if (empty($attributes)) {
562
            $attributes = [];
563
        }
564
        $attributes = $attributes + $defaults;
565
566
        return $this->_getUrl($url, $attributes);
567
    }
568
569
    /**
570
     * {@inheritDoc}
571
     */
572
    protected function _getUrl($content, $attributes)
573
    {
574
        $shortTag = true;
575
576
        return $this->_url($content, $content, $attributes['label'], $shortTag);
577
    }
578
}
579
580
/**
581
 * Class Link handles [link]http://example.com[/link]
582
 *
583
 * @package Saito\Jbb\CodeDefinition
584
 */
585
//@codingStandardsIgnoreStart
586
class Link extends Url
587
//@codingStandardsIgnoreEnd
588
{
589
    protected $_sTagName = 'link';
590
}
591
592
/**
593
 * Class UrlWithAttributes handles [url=http://example.com]foo[/url]
594
 *
595
 * @package Saito\Jbb\CodeDefinition
596
 */
597
//@codingStandardsIgnoreStart
598
class UrlWithAttributes extends Url
599
//@codingStandardsIgnoreEnd
600
{
601
    protected $_sParseContent = true;
602
603
    protected $_sUseOptions = true;
604
605
    /**
606
     * {@inheritDoc}
607
     */
608
    protected function _getUrl($content, $attributes)
609
    {
610
        $shortTag = false;
611
        $url = $attributes[$this->_sTagName];
612
613
        return $this->_url($url, $content, $attributes['label'], $shortTag);
614
    }
615
}
616
617
/**
618
 * Class LinkWithAttributes handles [link=http://example.com]foo[/link]
619
 *
620
 * @package Saito\Jbb\CodeDefinition
621
 */
622
//@codingStandardsIgnoreStart
623
class LinkWithAttributes extends UrlWithAttributes
624
//@codingStandardsIgnoreEnd
625
{
626
    protected $_sTagName = 'link';
627
}
628