Completed
Push — fixformtests ( 05d1ed )
by Richard
08:35
created

Element::themeDecorateElement()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 41
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.027

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 5
nop 0
dl 0
loc 41
rs 9.2222
c 0
b 0
f 0
ccs 10
cts 11
cp 0.9091
crap 6.027

1 Method

Rating   Name   Duplication   Size   Complexity  
A Element::setWithDefaults() 0 8 4
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
    public function __construct($attributes = array())
61
    {
62
        parent::__construct($attributes);
63
        if ($this->has(ElementFactory::FORM_KEY)
64
            && $this->get(ElementFactory::FORM_KEY) instanceof ContainerInterface) {
65
            $this->get(ElementFactory::FORM_KEY)->addElement($this);
66
        }
67
    }
68
	
69
	 /**
70
     * defaultRender - Generates default 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 defaultRender();
77
78
    /**
79
     * render - Generates output for the element.
80
     *
81
     * @return    string
82
     */
83
    public function render(){
84
		$xoops = \Xoops::getInstance();
85
		return $xoops->theme()->renderer($this);
86
	}
87
88
    /**
89
     * render attributes as a string to include in HTML output
90
     *
91
     * @return string
92
     */
93
    public function renderAttributeString()
94
    {
95
        $this->suppressRender($this->suppressList);
96
97
        // title attribute needs to be generated if not already set
98
        if (!$this->has('title')) {
99
            $this->set('title', $this->getTitle());
100
        }
101
        // generate id from name if not already set
102
        if (!$this->has('id')) {
103
            $id = $this->get('name');
104
            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

104
            if (substr(/** @scrutinizer ignore-type */ $id, -2) === '[]') {
Loading history...
105
                $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

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

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
351
        } else {
352
            if ($this->has(':pattern_description')) {
353
                return htmlspecialchars(
354
                    strip_tags($this->get('caption') . ' - ' . $this->get(':pattern_description')),
0 ignored issues
show
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

354
                    strip_tags(/** @scrutinizer ignore-type */ $this->get('caption') . ' - ' . $this->get(':pattern_description')),
Loading history...
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

354
                    strip_tags($this->get('caption') . ' - ' . /** @scrutinizer ignore-type */ $this->get(':pattern_description')),
Loading history...
355
                    ENT_QUOTES
356
                );
357
            } else {
358
                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

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