Completed
Pull Request — master (#563)
by Richard
08:33
created

Element::getClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
ccs 5
cts 5
cp 1
crap 2
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
13
namespace Xoops\Form;
14
15
use Xoops\Html\Attributes;
16
17
/**
18
 * Element - Abstract base class for form elements
19
 *
20
 * @category  Xoops\Form\Element
21
 * @package   Xoops\Form
22
 * @author    trabis <[email protected]>
23
 * @copyright 2012-2016 XOOPS Project (http://xoops.org)
24
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
25
 * @link      http://xoops.org
26
 */
27
abstract class Element extends Attributes
28
{
29
    /**
30
     * @var string[] list of attributes to NOT render
31
     */
32
    protected $suppressList = ['caption', 'datalist', 'description', 'option'];
33
34
    /**
35
     * Javascript performing additional validation of this element data
36
     *
37
     * This property contains a list of Javascript snippets that will be sent to
38
     * \Xoops\Form\Form::renderValidationJS().
39
     * NB: All elements are added to the output one after the other, so don't forget
40
     * to add a ";" after each to ensure no Javascript syntax error is generated.
41
     *
42
     * @var array ()
43
     */
44
    public $customValidationCode = array();
45
46
    /**
47
     * extra attributes to go in the tag
48
     *
49
     * @var array
50
     */
51
    protected $extra = array();
52
53
    /**
54
     * __construct
55
     *
56
     * @param array $attributes array of attribute name => value pairs
57
     *                           Control attributes:
58
     *                               ElementFactory::FORM_KEY optional form or tray to hold this element
59
     */
60 135
    public function __construct($attributes = array())
61
    {
62 135
        parent::__construct($attributes);
63 135
        if ($this->has(ElementFactory::FORM_KEY)
64 135
            && $this->get(ElementFactory::FORM_KEY) instanceof ContainerInterface) {
65 4
            $this->get(ElementFactory::FORM_KEY)->addElement($this);
66
        }
67 135
    }
68
69
    /**
70
     * render - Generates output for the element.
71
     *
72
     * This method is abstract and must be overwritten by the child classes.
73
     *
74
     * @return    string
75
     */
76
    abstract public function render();
77
78
    /**
79
     * render attributes as a string to include in HTML output
80
     *
81
     * @return string
82
     */
83 58
    public function renderAttributeString()
84
    {
85 58
        $this->suppressRender($this->suppressList);
86
87
        // title attribute needs to be generated if not already set
88 58
        if (!$this->has('title')) {
89 56
            $this->set('title', $this->getTitle());
90
        }
91
        // generate id from name if not already set
92 58
        if (!$this->has('id')) {
93 50
            $id = $this->get('name');
94 50
            if (substr($id, -2) === '[]') {
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type false; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
            if (substr(/** @scrutinizer ignore-type */ $id, -2) === '[]') {
Loading history...
95 1
                $id = substr($id, 0, strlen($id)-2);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type false; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

95
                $id = substr($id, 0, strlen(/** @scrutinizer ignore-type */ $id)-2);
Loading history...
96
            }
97 50
            $this->set('id', $id);
98
        }
99 58
        return parent::renderAttributeString();
100
    }
101
102
    /**
103
     * getValue - Get an array of pre-selected values
104
     *
105
     * @param boolean $encode True to encode special characters
106
     *
107
     * @return mixed
108
     */
109 41
    public function getValue($encode = false)
110
    {
111 41
        $values = $this->get('value', '');
112 41
        if (is_array($values)) {
113 1
            $ret = array();
114 1
            foreach ($values as $value) {
115 1
                $ret[] = $encode ? htmlspecialchars($value, ENT_QUOTES) : $value;
116
            }
117 1
            return $ret;
118
        }
119 41
        return $encode ? htmlspecialchars($values, ENT_QUOTES) : $values;
120
    }
121
122
    /**
123
     * setValue - Set pre-selected values
124
     *
125
     * @param mixed $value value to assign to this element
126
     *
127
     * @return void
128
     */
129 18
    public function setValue($value)
130
    {
131 18
        $this->set('value', $value);
132 18
    }
133
134
    /**
135
     * setName - set the "name" attribute for the element
136
     *
137
     * @param string $name "name" attribute for the element
138
     *
139
     * @return void
140
     */
141 34
    public function setName($name)
142
    {
143 34
        $this->set('name', $name);
144 34
    }
145
146
    /**
147
     * getName - get the "name" attribute for the element
148
     *
149
     * @return string
150
     */
151 28
    public function getName()
152
    {
153 28
        return (string) $this->get('name');
154
    }
155
156
    /**
157
     * setAccessKey - set the access key attribute for the element
158
     *
159
     * @param string $key "accesskey" attribute for the element
160
     *
161
     * @return void
162
     */
163 3
    public function setAccessKey($key)
164
    {
165 3
        $this->set('accesskey', $key);
166 3
    }
167
168
    /**
169
     * getAccessKey - get the access key attribute for the element
170
     *
171
     * @return string "accesskey" attribute value
172
     */
173 2
    public function getAccessKey()
174
    {
175 2
        return (string) $this->get('accesskey');
176
    }
177
178
    /**
179
     * getAccessString - If the access key is found in the specified string, underline it
180
     *
181
     * @param string $str string to add access key highlight to
182
     *
183
     * @return string Enhanced string with the 1st occurrence of "accesskey underlined
184
     */
185 1
    public function getAccessString($str)
186
    {
187 1
        $access = $this->getAccessKey();
188 1
        if (!empty($access) && (false !== ($pos = strpos($str, $access)))) {
189 1
            return htmlspecialchars(substr($str, 0, $pos), ENT_QUOTES)
190 1
                . '<span style="text-decoration: underline;">'
191 1
                . htmlspecialchars(substr($str, $pos, 1), ENT_QUOTES) . '</span>'
192 1
                . htmlspecialchars(substr($str, $pos + 1), ENT_QUOTES);
193
        }
194 1
        return htmlspecialchars($str, ENT_QUOTES);
195
    }
196
197
    /**
198
     * setClass - set the "class" attribute for the element
199
     *
200
     * @param string $class class attribute for the element
201
     *
202
     * @return void
203
     */
204 5
    public function setClass($class)
205
    {
206 5
        $this->add('class', (string) $class);
207 5
    }
208
209
    /**
210
     * getClass - get the "class" attribute for the element
211
     *
212
     * @return string "class" attribute value
213
     */
214 3
    public function getClass()
215
    {
216 3
        $class = $this->get('class', false);
217 3
        if ($class === false) {
218 1
            return false;
219
        }
220 3
        return htmlspecialchars(implode(' ', $class), ENT_QUOTES);
221
    }
222
223
    /**
224
     * setPattern - set the "pattern" attribute for the element
225
     *
226
     * @param string $pattern            pattern attribute for the element
227
     * @param string $patternDescription pattern description
228
     *
229
     * @return void
230
     */
231 5
    public function setPattern($pattern, $patternDescription = '')
232
    {
233 5
        $this->set('pattern', $pattern);
234 5
        $this->set(':pattern_description', $patternDescription);
235 5
    }
236
237
    /**
238
     * getPattern - get the "pattern" attribute for the element
239
     *
240
     * @return string "pattern"
241
     */
242 1
    public function getPattern()
243
    {
244 1
        return (string) $this->get('pattern', '');
245
    }
246
247
    /**
248
     * getPatternDescription - get the "pattern_description"
249
     *
250
     * @return string "pattern_description"
251
     */
252 5
    public function getPatternDescription()
253
    {
254 5
        return (string) $this->get(':pattern_description', '');
255
    }
256
257
    /**
258
     * setDatalist - set the datalist attribute for the element
259
     *
260
     * @param string[]|string $datalist datalist attribute for the element
261
     *
262
     * @return void
263
     */
264 4
    public function setDatalist($datalist)
265
    {
266 4
        $this->add('datalist', $datalist);
267 4
    }
268
269
    /**
270
     * renderDatalist - get the datalist attribute for the element
271
     *
272
     * @return string "datalist" attribute value
273
     */
274 3
    public function renderDatalist()
275
    {
276 3
        if (!$this->isDatalist()) {
277 3
            return '';
278
        }
279 2
        $ret = "\n" . '<datalist id="list_' . $this->getName() . '">' . "\n";
280 2
        foreach ($this->get('datalist') as $datalist) {
281 2
            $ret .= '<option value="' . htmlspecialchars($datalist, ENT_QUOTES) . '">' . "\n";
282
        }
283 2
        $ret .= '</datalist>' . "\n";
284 2
        return $ret;
285
    }
286
287
    /**
288
     * isDatalist - is there a datalist for the element?
289
     *
290
     * @return boolean true if element has a non-empty datalist
291
     */
292 13
    public function isDatalist()
293
    {
294 13
        return $this->has('datalist');
295
    }
296
297
    /**
298
     * setCaption - set the caption for the element
299
     *
300
     * @param string $caption caption for element
301
     *
302
     * @return void
303
     */
304 34
    public function setCaption($caption)
305
    {
306 34
        $this->set('caption', $caption);
307 34
    }
308
309
    /**
310
     * getCaption - get the caption for the element
311
     *
312
     * @return string
313
     */
314 15
    public function getCaption()
315
    {
316 15
        return $this->get('caption', '');
317
        //return htmlspecialchars($this->caption, ENT_QUOTES);
318
    }
319
320
    /**
321
     * setTitle - set the title for the element
322
     *
323
     * @param string $title title for element
324
     *
325
     * @return void
326
     */
327 2
    public function setTitle($title)
328
    {
329 2
        $this->set('title', $title);
330 2
    }
331
332
    /**
333
     * getTitle - get the title for the element
334
     *
335
     * @return string
336
     */
337 57
    public function getTitle()
338
    {
339 57
        if ($this->has('title')) {
340 1
            return $this->get('title');
341
        } else {
342 57
            if ($this->has(':pattern_description')) {
343 4
                return htmlspecialchars(
344 4
                    strip_tags($this->get('caption') . ' - ' . $this->get(':pattern_description')),
0 ignored issues
show
Bug introduced by
Are you sure $this->get(':pattern_description') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

344
                    strip_tags($this->get('caption') . ' - ' . /** @scrutinizer ignore-type */ $this->get(':pattern_description')),
Loading history...
Bug introduced by
Are you sure $this->get('caption') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

344
                    strip_tags(/** @scrutinizer ignore-type */ $this->get('caption') . ' - ' . $this->get(':pattern_description')),
Loading history...
345 4
                    ENT_QUOTES
346
                );
347
            } else {
348 55
                return htmlspecialchars(strip_tags($this->get('caption')), ENT_QUOTES);
0 ignored issues
show
Bug introduced by
It seems like $this->get('caption') can also be of type false; however, parameter $str of strip_tags() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

348
                return htmlspecialchars(strip_tags(/** @scrutinizer ignore-type */ $this->get('caption')), ENT_QUOTES);
Loading history...
349
            }
350
        }
351
    }
352
353
    /**
354
     * setDescription - set the element's description
355
     *
356
     * @param string $description description
357
     *
358
     * @return void
359
     */
360 2
    public function setDescription($description)
361
    {
362 2
        $this->set('description', $description);
363 2
    }
364
365
    /**
366
     * getDescription - get the element's description
367
     *
368
     * @param boolean $encode True to encode special characters
369
     *
370
     * @return string
371
     */
372 6
    public function getDescription($encode = false)
373
    {
374 6
        $description = $this->get('description', '');
375 6
        return $encode ? htmlspecialchars($description, ENT_QUOTES) : $description;
376
    }
377
378
    /**
379
     * setHidden - flag the element as "hidden"
380
     *
381
     * @return void
382
     */
383 18
    public function setHidden()
384
    {
385 18
        $this->set('hidden');
386 18
    }
387
388
    /**
389
     * isHidden - is this a hidden element?
390
     *
391
     * @return boolean true if hidden
392
     */
393 13
    public function isHidden()
394
    {
395 13
        return $this->has('hidden');
396
    }
397
398
    /**
399
     * setRequired - set entry required
400
     *
401
     * @param boolean $bool true to set required entry for this element
402
     *
403
     * @return void
404
     */
405 11
    public function setRequired($bool = true)
406
    {
407 11
        if ($bool) {
408 11
            $this->set('required');
409
        }
410 11
    }
411
412
    /**
413
     * isRequired - is entry required for this element?
414
     *
415
     * @return boolean true if entry is required
416
     */
417 16
    public function isRequired()
418
    {
419 16
        return $this->has('required');
420
    }
421
422
    /**
423
     * setExtra - Add extra attributes to the element.
424
     *
425
     * This string will be inserted verbatim and unvalidated in the
426
     * element's tag. Know what you are doing!
427
     *
428
     * @param string  $extra   extra raw text to insert into form
429
     * @param boolean $replace If true, passed string will replace current
430
     *                         content, otherwise it will be appended to it
431
     *
432
     * @return string[] New content of the extra string
433
     *
434
     * @deprecated please use attributes for event scripting
435
     */
436 2
    public function setExtra($extra, $replace = false)
437
    {
438 2
        if ($replace) {
439 1
            $this->extra = array(trim($extra));
440
        } else {
441 2
            $this->extra[] = trim($extra);
442
        }
443 2
        return $this->extra;
444
    }
445
446
    /**
447
     * getExtra - Get the extra attributes for the element
448
     *
449
     * @param boolean $encode True to encode special characters
450
     *
451
     * @return string
452
     *
453
     * @see setExtra() this is going to disappear
454
     */
455 56
    public function getExtra($encode = false)
456
    {
457 56
        if (!$encode) {
458 56
            return implode(' ', $this->extra);
459
        }
460 1
        $value = array();
461 1
        foreach ($this->extra as $val) {
462 1
            $value[] = str_replace('>', '&gt;', str_replace('<', '&lt;', $val));
463
        }
464 1
        return empty($value) ? '' : implode(' ', $value);
465
    }
466
467
    /**
468
     * addCustomValidationCode - Add custom validation javascript
469
     *
470
     * This string will be inserted verbatim and unvalidated in the page.
471
     * Know what you are doing!
472
     *
473
     * @param string  $code    javascript code  to insert into form
474
     * @param boolean $replace If true, passed string will replace current code,
475
     *                          otherwise it will be appended to it
476
     *
477
     * @return void
478
     */
479 1
    public function addCustomValidationCode($code, $replace = false)
480
    {
481 1
        if ($replace) {
482 1
            $this->customValidationCode = [$code];
483
        } else {
484 1
            $this->customValidationCode[] = $code;
485
        }
486 1
    }
487
488
    /**
489
     * renderValidationJS - Render custom javascript validation code
490
     *
491
     * @return string|false
492
     */
493 4
    public function renderValidationJS()
494
    {
495
        // render custom validation code if any
496 4
        if (!empty($this->customValidationCode)) {
497 1
            return implode("\n", $this->customValidationCode);
498
            // generate validation code if required
499
        } else {
500 4
            if ($this->isRequired() && $eltname = $this->getName()) {
501
                // $eltname    = $this->getName();
502 1
                $eltcaption = $this->getCaption();
503 1
                $eltmsg = empty($eltcaption)
504 1
                    ? sprintf(\XoopsLocale::F_ENTER, $eltname)
505 1
                    : sprintf(\XoopsLocale::F_ENTER, $eltcaption);
506 1
                $eltmsg = str_replace(array(':', '?', '%'), '', $eltmsg);
507 1
                $eltmsg = str_replace('"', '\"', stripslashes($eltmsg));
508 1
                $eltmsg = strip_tags($eltmsg);
509
                return "\n"
510 1
                    . "if ( myform.{$eltname}.value == \"\" ) { window.alert(\"{$eltmsg}\");"
511 1
                    . " myform.{$eltname}.focus(); return false; }\n";
512
            }
513
        }
514 4
        return false;
515
    }
516
517
    /**
518
     * Test if a class that starts with the pattern string is set
519
     *
520
     * @param string $pattern 'starts with' to match
521
     *
522
     * @return integer|false false if no match, or index of matching class
523
     */
524 39
    public function hasClassLike($pattern)
525
    {
526 39
        $class = $this->get('class');
527 39
        if ($class) {
528 7
            $length = strlen($pattern);
529 7
            foreach ((array) $class as $i => $value) {
530 7
                if (0 === strncmp($value, $pattern, $length)) {
531 7
                    return $i;
532
                }
533
            }
534
        }
535 39
        return false;
536
    }
537
538
    /**
539
     * themeDecorateElement - add theme decoration to element
540
     *
541
     * @return void
542
     *
543
     * @todo this should ask the theme
544
     */
545 38
    public function themeDecorateElement()
546
    {
547 38
        $class = 'form-control';
548 38
        if ($this instanceof Button) {
549 6
            if (false !== $this->hasClassLike('btn')) {
0 ignored issues
show
introduced by
The condition false !== $this->hasClassLike('btn') can never be true.
Loading history...
550 2
                return;
551
            }
552 5
            $class = 'btn btn-default';
553 33
        } elseif (false !== $this->hasClassLike('form-') && false !== $this->hasClassLike('col-')) {
0 ignored issues
show
introduced by
The condition false !== $this->hasClas...s->hasClassLike('col-') can never be true.
Loading history...
554
            return;
555 33
        } elseif ($this instanceof TextArea) {
556 5
            $class = 'form-control';
557
        } /**
558
        } elseif ($this instanceof OptionElement) {
559
            $class = 'col-md-3';
560
            $options = $this->get('option', []);
561
            foreach ($options as $value) {
562
                if (is_array($value)) { // optgroup
563
                    foreach ($value as $subvalue) {
564
                        if (strlen($subvalue) > 20) {
565
                            $class = 'col-md-4';
566
                            break 2;
567
                        }
568
                    }
569
                } elseif (strlen($value) > 20) {
570
                    $class = 'col-md-4';
571
                    break;
572
                }
573
            }
574
        } else {
575
            $size = $this->get('size', 0);
576
            if ($size < 20) {
577
//                $class = 'col-md-2';
578
//            } elseif ($size < 30) {
579
                $class = 'col-md-3';
580
            } else {
581
                $class = 'form-control';
582
            }
583
        }
584
     */
585 38
        $this->add('class', $class);
586 38
    }
587
588
    /**
589
     * Convenience method to assist with setting attributes when using BC Element syntax
590
     *
591
     * Set attribute $name to $value, replacing $value with $default if $value is empty, or if the
592
     * value is not one of the values specified in the (optional) $enum array
593
     *
594
     * @param string $name    attribute name
595
     * @param mixed  $value   attribute value
596
     * @param mixed  $default default value
597
     * @param array  $enum    optional list of valid values
598
     *
599
     * @return void
600
     */
601 93
    public function setWithDefaults($name, $value, $default = null, $enum = null)
602
    {
603 93
        if (empty($value)) {
604 32
            $value = $default;
605 86
        } elseif (null !== $enum && !in_array($value, $enum)) {
606 1
            $value = $default;
607
        }
608 93
        $this->set($name, $value);
609 93
    }
610
611
    /**
612
     * Convenience method to assist with setting attributes when using BC Element syntax
613
     *
614
     * Set attribute $name to $value, replacing $value with $default if $value is empty, or if the
615
     * value is not one of the values specified in the (optional) $enum array
616
     *
617
     * @param string $name  attribute name
618
     * @param mixed  $value attribute value
619
     *
620
     * @return void
621
     */
622 27
    public function setIfNotEmpty($name, $value)
623
    {
624
        // don't overwrite
625 27
        if (!$this->has($name) && !empty($value)) {
626 17
            $this->set($name, $value);
627
        }
628 27
    }
629
630
    /**
631
     * Convenience method to assist with setting attributes
632
     *
633
     * Set attribute $name to $value, replacing $value with $default if $value is empty, or if the
634
     * value is not one of the values specified in the (optional) $enum array
635
     *
636
     * @param string $name  attribute name
637
     * @param mixed  $value attribute value
638
     *
639
     * @return void
640
     */
641 38
    public function setIfNotSet($name, $value)
642
    {
643
        // don't overwrite
644 38
        if (!$this->has($name)) {
645 38
            $this->set($name, $value);
646
        }
647 38
    }
648
}
649