HTMLPurifier_Printer_ConfigForm   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 197
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 21
eloc 95
dl 0
loc 197
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getCSS() 0 3 1
A getJavaScript() 0 3 1
B renderNamespace() 0 59 9
A setTextareaDimensions() 0 7 3
B render() 0 43 6
A __construct() 0 12 1
1
<?php
2
3
/**
4
 * @todo Rewrite to use Interchange objects
5
 */
6
class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
7
{
8
9
    /**
10
     * Printers for specific fields.
11
     * @type HTMLPurifier_Printer[]
12
     */
13
    protected $fields = array();
14
15
    /**
16
     * Documentation URL, can have fragment tagged on end.
17
     * @type string
18
     */
19
    protected $docURL;
20
21
    /**
22
     * Name of form element to stuff config in.
23
     * @type string
24
     */
25
    protected $name;
26
27
    /**
28
     * Whether or not to compress directive names, clipping them off
29
     * after a certain amount of letters. False to disable or integer letters
30
     * before clipping.
31
     * @type bool
32
     */
33
    protected $compress = false;
34
35
    /**
36
     * @param string $name Form element name for directives to be stuffed into
37
     * @param string $doc_url String documentation URL, will have fragment tagged on
38
     * @param bool $compress Integer max length before compressing a directive name, set to false to turn off
39
     */
40
    public function __construct(
41
        $name,
42
        $doc_url = null,
43
        $compress = false
44
    ) {
45
        parent::__construct();
46
        $this->docURL = $doc_url;
47
        $this->name = $name;
48
        $this->compress = $compress;
49
        // initialize sub-printers
50
        $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
51
        $this->fields[HTMLPurifier_VarParser::C_BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
52
    }
53
54
    /**
55
     * Sets default column and row size for textareas in sub-printers
56
     * @param $cols Integer columns of textarea, null to use default
57
     * @param $rows Integer rows of textarea, null to use default
58
     */
59
    public function setTextareaDimensions($cols = null, $rows = null)
60
    {
61
        if ($cols) {
62
            $this->fields['default']->cols = $cols;
63
        }
64
        if ($rows) {
65
            $this->fields['default']->rows = $rows;
66
        }
67
    }
68
69
    /**
70
     * Retrieves styling, in case it is not accessible by webserver
71
     */
72
    public static function getCSS()
73
    {
74
        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
75
    }
76
77
    /**
78
     * Retrieves JavaScript, in case it is not accessible by webserver
79
     */
80
    public static function getJavaScript()
81
    {
82
        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
83
    }
84
85
    /**
86
     * Returns HTML output for a configuration form
87
     * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array
88
     *        where [0] has an HTML namespace and [1] is being rendered.
89
     * @param array|bool $allowed Optional namespace(s) and directives to restrict form to.
90
     * @param bool $render_controls
91
     * @return string
92
     */
93
    public function render($config, $allowed = true, $render_controls = true)
94
    {
95
        if (is_array($config) && isset($config[0])) {
96
            $gen_config = $config[0];
97
            $config = $config[1];
98
        } else {
99
            $gen_config = $config;
100
        }
101
102
        $this->config = $config;
103
        $this->genConfig = $gen_config;
0 ignored issues
show
Bug Best Practice introduced by
The property genConfig does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
104
        $this->prepareGenerator($gen_config);
105
106
        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
0 ignored issues
show
Bug introduced by
It seems like $allowed can also be of type boolean; however, parameter $allowed of HTMLPurifier_Config::getAllowedDirectivesForForm() does only seem to accept array, 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

106
        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm(/** @scrutinizer ignore-type */ $allowed, $config->def);
Loading history...
107
        $all = array();
108
        foreach ($allowed as $key) {
109
            list($ns, $directive) = $key;
110
            $all[$ns][$directive] = $config->get($ns . '.' . $directive);
111
        }
112
113
        $ret = '';
114
        $ret .= $this->start('table', array('class' => 'hp-config'));
115
        $ret .= $this->start('thead');
116
        $ret .= $this->start('tr');
117
        $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
118
        $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
119
        $ret .= $this->end('tr');
120
        $ret .= $this->end('thead');
121
        foreach ($all as $ns => $directives) {
122
            $ret .= $this->renderNamespace($ns, $directives);
123
        }
124
        if ($render_controls) {
125
            $ret .= $this->start('tbody');
126
            $ret .= $this->start('tr');
127
            $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
128
            $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
129
            $ret .= '[<a href="?">Reset</a>]';
130
            $ret .= $this->end('td');
131
            $ret .= $this->end('tr');
132
            $ret .= $this->end('tbody');
133
        }
134
        $ret .= $this->end('table');
135
        return $ret;
136
    }
137
138
    /**
139
     * Renders a single namespace
140
     * @param $ns String namespace name
141
     * @param array $directives array of directives to values
142
     * @return string
143
     */
144
    protected function renderNamespace($ns, $directives)
145
    {
146
        $ret = '';
147
        $ret .= $this->start('tbody', array('class' => 'namespace'));
148
        $ret .= $this->start('tr');
149
        $ret .= $this->element('th', $ns, array('colspan' => 2));
150
        $ret .= $this->end('tr');
151
        $ret .= $this->end('tbody');
152
        $ret .= $this->start('tbody');
153
        foreach ($directives as $directive => $value) {
154
            $ret .= $this->start('tr');
155
            $ret .= $this->start('th');
156
            if ($this->docURL) {
157
                $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
158
                $ret .= $this->start('a', array('href' => $url));
159
            }
160
            $attr = array('for' => "{$this->name}:$ns.$directive");
161
162
            // crop directive name if it's too long
163
            if (!$this->compress || (strlen($directive) < $this->compress)) {
164
                $directive_disp = $directive;
165
            } else {
166
                $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
167
                $attr['title'] = $directive;
168
            }
169
170
            $ret .= $this->element(
171
                'label',
172
                $directive_disp,
173
                // component printers must create an element with this id
174
                $attr
175
            );
176
            if ($this->docURL) {
177
                $ret .= $this->end('a');
178
            }
179
            $ret .= $this->end('th');
180
181
            $ret .= $this->start('td');
182
            $def = $this->config->def->info["$ns.$directive"];
183
            if (is_int($def)) {
184
                $allow_null = $def < 0;
185
                $type = abs($def);
186
            } else {
187
                $type = $def->type;
188
                $allow_null = isset($def->allow_null);
189
            }
190
            if (!isset($this->fields[$type])) {
191
                $type = 0;
192
            } // default
193
            $type_obj = $this->fields[$type];
194
            if ($allow_null) {
195
                $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
196
            }
197
            $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
198
            $ret .= $this->end('td');
199
            $ret .= $this->end('tr');
200
        }
201
        $ret .= $this->end('tbody');
202
        return $ret;
203
    }
204
205
}
206
207
/**
208
 * Printer decorator for directives that accept null
209
 */
210
class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
211
{
212
    /**
213
     * Printer being decorated
214
     * @type HTMLPurifier_Printer
215
     */
216
    protected $obj;
217
218
    /**
219
     * @param HTMLPurifier_Printer $obj Printer to decorate
220
     */
221
    public function __construct($obj)
222
    {
223
        parent::__construct();
224
        $this->obj = $obj;
225
    }
226
227
    /**
228
     * @param string $ns
229
     * @param string $directive
230
     * @param string $value
231
     * @param string $name
232
     * @param HTMLPurifier_Config|array $config
233
     * @return string
234
     */
235
    public function render($ns, $directive, $value, $name, $config)
236
    {
237
        if (is_array($config) && isset($config[0])) {
238
            $gen_config = $config[0];
239
            $config = $config[1];
240
        } else {
241
            $gen_config = $config;
242
        }
243
        $this->prepareGenerator($gen_config);
244
245
        $ret = '';
246
        $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
247
        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
248
        $ret .= $this->text(' Null/Disabled');
249
        $ret .= $this->end('label');
250
        $attr = array(
251
            'type' => 'checkbox',
252
            'value' => '1',
253
            'class' => 'null-toggle',
254
            'name' => "$name" . "[Null_$ns.$directive]",
255
            'id' => "$name:Null_$ns.$directive",
256
            'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
257
        );
258
        if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
259
            // modify inline javascript slightly
260
            $attr['onclick'] =
261
                "toggleWriteability('$name:Yes_$ns.$directive',checked);" .
262
                "toggleWriteability('$name:No_$ns.$directive',checked)";
263
        }
264
        if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
265
            $attr['checked'] = 'checked';
266
        }
267
        $ret .= $this->elementEmpty('input', $attr);
268
        $ret .= $this->text(' or ');
269
        $ret .= $this->elementEmpty('br');
270
        $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
0 ignored issues
show
introduced by
The method render() does not exist on HTMLPurifier_Printer. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

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

270
        $ret .= $this->obj->/** @scrutinizer ignore-call */ render($ns, $directive, $value, $name, array($gen_config, $config));
Loading history...
271
        return $ret;
272
    }
273
}
274
275
/**
276
 * Swiss-army knife configuration form field printer
277
 */
278
class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
279
{
280
    /**
281
     * @type int
282
     */
283
    public $cols = 18;
284
285
    /**
286
     * @type int
287
     */
288
    public $rows = 5;
289
290
    /**
291
     * @param string $ns
292
     * @param string $directive
293
     * @param string $value
294
     * @param string $name
295
     * @param HTMLPurifier_Config|array $config
296
     * @return string
297
     */
298
    public function render($ns, $directive, $value, $name, $config)
299
    {
300
        if (is_array($config) && isset($config[0])) {
301
            $gen_config = $config[0];
302
            $config = $config[1];
303
        } else {
304
            $gen_config = $config;
305
        }
306
        $this->prepareGenerator($gen_config);
307
        // this should probably be split up a little
308
        $ret = '';
309
        $def = $config->def->info["$ns.$directive"];
310
        if (is_int($def)) {
311
            $type = abs($def);
312
        } else {
313
            $type = $def->type;
314
        }
315
        if (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
316
            switch ($type) {
317
                case HTMLPurifier_VarParser::LOOKUP:
318
                    $array = $value;
319
                    $value = array();
320
                    foreach ($array as $val => $b) {
321
                        $value[] = $val;
322
                    }
323
                    //TODO does this need a break?
324
                case HTMLPurifier_VarParser::ALIST:
325
                    $value = implode(PHP_EOL, $value);
326
                    break;
327
                case HTMLPurifier_VarParser::HASH:
328
                    $nvalue = '';
329
                    foreach ($value as $i => $v) {
330
                        if (is_array($v)) {
331
                            // HACK
332
                            $v = implode(";", $v);
333
                        }
334
                        $nvalue .= "$i:$v" . PHP_EOL;
335
                    }
336
                    $value = $nvalue;
337
                    break;
338
                default:
339
                    $value = '';
340
            }
341
        }
342
        if ($type === HTMLPurifier_VarParser::C_MIXED) {
343
            return 'Not supported';
344
            $value = serialize($value);
0 ignored issues
show
Unused Code introduced by
$value = serialize($value) is not 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...
345
        }
346
        $attr = array(
347
            'name' => "$name" . "[$ns.$directive]",
348
            'id' => "$name:$ns.$directive"
349
        );
350
        if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
351
            $attr['disabled'] = 'disabled';
352
        }
353
        if (isset($def->allowed)) {
354
            $ret .= $this->start('select', $attr);
355
            foreach ($def->allowed as $val => $b) {
356
                $attr = array();
357
                if ($value == $val) {
358
                    $attr['selected'] = 'selected';
359
                }
360
                $ret .= $this->element('option', $val, $attr);
361
            }
362
            $ret .= $this->end('select');
363
        } elseif ($type === HTMLPurifier_VarParser::TEXT ||
364
                $type === HTMLPurifier_VarParser::ITEXT ||
365
                $type === HTMLPurifier_VarParser::ALIST ||
366
                $type === HTMLPurifier_VarParser::HASH ||
367
                $type === HTMLPurifier_VarParser::LOOKUP) {
368
            $attr['cols'] = $this->cols;
369
            $attr['rows'] = $this->rows;
370
            $ret .= $this->start('textarea', $attr);
371
            $ret .= $this->text($value);
372
            $ret .= $this->end('textarea');
373
        } else {
374
            $attr['value'] = $value;
375
            $attr['type'] = 'text';
376
            $ret .= $this->elementEmpty('input', $attr);
377
        }
378
        return $ret;
379
    }
380
}
381
382
/**
383
 * Bool form field printer
384
 */
385
class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer
386
{
387
    /**
388
     * @param string $ns
389
     * @param string $directive
390
     * @param string $value
391
     * @param string $name
392
     * @param HTMLPurifier_Config|array $config
393
     * @return string
394
     */
395
    public function render($ns, $directive, $value, $name, $config)
396
    {
397
        if (is_array($config) && isset($config[0])) {
398
            $gen_config = $config[0];
399
            $config = $config[1];
0 ignored issues
show
Unused Code introduced by
The assignment to $config is dead and can be removed.
Loading history...
400
        } else {
401
            $gen_config = $config;
402
        }
403
        $this->prepareGenerator($gen_config);
404
        $ret = '';
405
        $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
406
407
        $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
408
        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
409
        $ret .= $this->text(' Yes');
410
        $ret .= $this->end('label');
411
412
        $attr = array(
413
            'type' => 'radio',
414
            'name' => "$name" . "[$ns.$directive]",
415
            'id' => "$name:Yes_$ns.$directive",
416
            'value' => '1'
417
        );
418
        if ($value === true) {
0 ignored issues
show
introduced by
The condition $value === true is always false.
Loading history...
419
            $attr['checked'] = 'checked';
420
        }
421
        if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
422
            $attr['disabled'] = 'disabled';
423
        }
424
        $ret .= $this->elementEmpty('input', $attr);
425
426
        $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
427
        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
428
        $ret .= $this->text(' No');
429
        $ret .= $this->end('label');
430
431
        $attr = array(
432
            'type' => 'radio',
433
            'name' => "$name" . "[$ns.$directive]",
434
            'id' => "$name:No_$ns.$directive",
435
            'value' => '0'
436
        );
437
        if ($value === false) {
0 ignored issues
show
introduced by
The condition $value === false is always false.
Loading history...
438
            $attr['checked'] = 'checked';
439
        }
440
        if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
441
            $attr['disabled'] = 'disabled';
442
        }
443
        $ret .= $this->elementEmpty('input', $attr);
444
445
        $ret .= $this->end('div');
446
447
        return $ret;
448
    }
449
}
450
451
// vim: et sw=4 sts=4
452