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
|
|
|
*/ |
12
|
|
|
protected $fields = array(); |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Documentation URL, can have fragment tagged on end |
16
|
|
|
*/ |
17
|
|
|
protected $docURL; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Name of form element to stuff config in |
21
|
|
|
*/ |
22
|
|
|
protected $name; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Whether or not to compress directive names, clipping them off |
26
|
|
|
* after a certain amount of letters. False to disable or integer letters |
27
|
|
|
* before clipping. |
28
|
|
|
*/ |
29
|
|
|
protected $compress = false; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @param $name Form element name for directives to be stuffed into |
33
|
|
|
* @param $doc_url String documentation URL, will have fragment tagged on |
34
|
|
|
* @param $compress Integer max length before compressing a directive name, set to false to turn off |
35
|
|
|
*/ |
36
|
|
|
public function __construct( |
37
|
|
|
$name, $doc_url = null, $compress = false |
38
|
|
|
) { |
39
|
|
|
parent::__construct(); |
40
|
|
|
$this->docURL = $doc_url; |
41
|
|
|
$this->name = $name; |
42
|
|
|
$this->compress = $compress; |
43
|
|
|
// initialize sub-printers |
44
|
|
|
$this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); |
45
|
|
|
$this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Sets default column and row size for textareas in sub-printers |
50
|
|
|
* @param $cols Integer columns of textarea, null to use default |
51
|
|
|
* @param $rows Integer rows of textarea, null to use default |
52
|
|
|
*/ |
53
|
|
|
public function setTextareaDimensions($cols = null, $rows = null) { |
54
|
|
|
if ($cols) $this->fields['default']->cols = $cols; |
55
|
|
|
if ($rows) $this->fields['default']->rows = $rows; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Retrieves styling, in case it is not accessible by webserver |
60
|
|
|
*/ |
61
|
|
|
public static function getCSS() { |
62
|
|
|
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Retrieves JavaScript, in case it is not accessible by webserver |
67
|
|
|
*/ |
68
|
|
|
public static function getJavaScript() { |
69
|
|
|
return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Returns HTML output for a configuration form |
74
|
|
|
* @param $config Configuration object of current form state, or an array |
75
|
|
|
* where [0] has an HTML namespace and [1] is being rendered. |
76
|
|
|
* @param $allowed Optional namespace(s) and directives to restrict form to. |
77
|
|
|
*/ |
78
|
|
|
public function render($config, $allowed = true, $render_controls = true) { |
79
|
|
View Code Duplication |
if (is_array($config) && isset($config[0])) { |
80
|
|
|
$gen_config = $config[0]; |
81
|
|
|
$config = $config[1]; |
82
|
|
|
} else { |
83
|
|
|
$gen_config = $config; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$this->config = $config; |
87
|
|
|
$this->genConfig = $gen_config; |
|
|
|
|
88
|
|
|
$this->prepareGenerator($gen_config); |
89
|
|
|
|
90
|
|
|
$allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def); |
91
|
|
|
$all = array(); |
92
|
|
|
foreach ($allowed as $key) { |
93
|
|
|
list($ns, $directive) = $key; |
94
|
|
|
$all[$ns][$directive] = $config->get($ns .'.'. $directive); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
$ret = ''; |
98
|
|
|
$ret .= $this->start('table', array('class' => 'hp-config')); |
99
|
|
|
$ret .= $this->start('thead'); |
100
|
|
|
$ret .= $this->start('tr'); |
101
|
|
|
$ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); |
102
|
|
|
$ret .= $this->element('th', 'Value', array('class' => 'hp-value')); |
103
|
|
|
$ret .= $this->end('tr'); |
104
|
|
|
$ret .= $this->end('thead'); |
105
|
|
|
foreach ($all as $ns => $directives) { |
106
|
|
|
$ret .= $this->renderNamespace($ns, $directives); |
107
|
|
|
} |
108
|
|
|
if ($render_controls) { |
109
|
|
|
$ret .= $this->start('tbody'); |
110
|
|
|
$ret .= $this->start('tr'); |
111
|
|
|
$ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); |
112
|
|
|
$ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); |
113
|
|
|
$ret .= '[<a href="?">Reset</a>]'; |
114
|
|
|
$ret .= $this->end('td'); |
115
|
|
|
$ret .= $this->end('tr'); |
116
|
|
|
$ret .= $this->end('tbody'); |
117
|
|
|
} |
118
|
|
|
$ret .= $this->end('table'); |
119
|
|
|
return $ret; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Renders a single namespace |
124
|
|
|
* @param $ns String namespace name |
125
|
|
|
* @param $directive Associative array of directives to values |
126
|
|
|
*/ |
127
|
|
|
protected function renderNamespace($ns, $directives) { |
128
|
|
|
$ret = ''; |
129
|
|
|
$ret .= $this->start('tbody', array('class' => 'namespace')); |
130
|
|
|
$ret .= $this->start('tr'); |
131
|
|
|
$ret .= $this->element('th', $ns, array('colspan' => 2)); |
132
|
|
|
$ret .= $this->end('tr'); |
133
|
|
|
$ret .= $this->end('tbody'); |
134
|
|
|
$ret .= $this->start('tbody'); |
135
|
|
|
foreach ($directives as $directive => $value) { |
136
|
|
|
$ret .= $this->start('tr'); |
137
|
|
|
$ret .= $this->start('th'); |
138
|
|
|
if ($this->docURL) { |
139
|
|
|
$url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); |
140
|
|
|
$ret .= $this->start('a', array('href' => $url)); |
141
|
|
|
} |
142
|
|
|
$attr = array('for' => "{$this->name}:$ns.$directive"); |
143
|
|
|
|
144
|
|
|
// crop directive name if it's too long |
145
|
|
|
if (!$this->compress || (strlen($directive) < $this->compress)) { |
146
|
|
|
$directive_disp = $directive; |
147
|
|
|
} else { |
148
|
|
|
$directive_disp = substr($directive, 0, $this->compress - 2) . '...'; |
149
|
|
|
$attr['title'] = $directive; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
$ret .= $this->element( |
153
|
|
|
'label', |
154
|
|
|
$directive_disp, |
155
|
|
|
// component printers must create an element with this id |
156
|
|
|
$attr |
157
|
|
|
); |
158
|
|
|
if ($this->docURL) $ret .= $this->end('a'); |
159
|
|
|
$ret .= $this->end('th'); |
160
|
|
|
|
161
|
|
|
$ret .= $this->start('td'); |
162
|
|
|
$def = $this->config->def->info["$ns.$directive"]; |
163
|
|
View Code Duplication |
if (is_int($def)) { |
164
|
|
|
$allow_null = $def < 0; |
165
|
|
|
$type = abs($def); |
166
|
|
|
} else { |
167
|
|
|
$type = $def->type; |
168
|
|
|
$allow_null = isset($def->allow_null); |
169
|
|
|
} |
170
|
|
|
if (!isset($this->fields[$type])) $type = 0; // default |
171
|
|
|
$type_obj = $this->fields[$type]; |
172
|
|
|
if ($allow_null) { |
173
|
|
|
$type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); |
174
|
|
|
} |
175
|
|
|
$ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); |
|
|
|
|
176
|
|
|
$ret .= $this->end('td'); |
177
|
|
|
$ret .= $this->end('tr'); |
178
|
|
|
} |
179
|
|
|
$ret .= $this->end('tbody'); |
180
|
|
|
return $ret; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Printer decorator for directives that accept null |
187
|
|
|
*/ |
188
|
|
|
class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer { |
189
|
|
|
/** |
190
|
|
|
* Printer being decorated |
191
|
|
|
*/ |
192
|
|
|
protected $obj; |
193
|
|
|
/** |
194
|
|
|
* @param $obj Printer to decorate |
195
|
|
|
*/ |
196
|
|
|
public function __construct($obj) { |
197
|
|
|
parent::__construct(); |
198
|
|
|
$this->obj = $obj; |
199
|
|
|
} |
200
|
|
|
public function render($ns, $directive, $value, $name, $config) { |
201
|
|
View Code Duplication |
if (is_array($config) && isset($config[0])) { |
202
|
|
|
$gen_config = $config[0]; |
203
|
|
|
$config = $config[1]; |
204
|
|
|
} else { |
205
|
|
|
$gen_config = $config; |
206
|
|
|
} |
207
|
|
|
$this->prepareGenerator($gen_config); |
208
|
|
|
|
209
|
|
|
$ret = ''; |
210
|
|
|
$ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive")); |
211
|
|
|
$ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); |
212
|
|
|
$ret .= $this->text(' Null/Disabled'); |
213
|
|
|
$ret .= $this->end('label'); |
214
|
|
|
$attr = array( |
215
|
|
|
'type' => 'checkbox', |
216
|
|
|
'value' => '1', |
217
|
|
|
'class' => 'null-toggle', |
218
|
|
|
'name' => "$name"."[Null_$ns.$directive]", |
219
|
|
|
'id' => "$name:Null_$ns.$directive", |
220
|
|
|
'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! |
221
|
|
|
); |
222
|
|
|
if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { |
223
|
|
|
// modify inline javascript slightly |
224
|
|
|
$attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)"; |
225
|
|
|
} |
226
|
|
|
if ($value === null) $attr['checked'] = 'checked'; |
227
|
|
|
$ret .= $this->elementEmpty('input', $attr); |
228
|
|
|
$ret .= $this->text(' or '); |
229
|
|
|
$ret .= $this->elementEmpty('br'); |
230
|
|
|
$ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config)); |
231
|
|
|
return $ret; |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Swiss-army knife configuration form field printer |
237
|
|
|
*/ |
238
|
|
|
class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { |
239
|
|
|
public $cols = 18; |
240
|
|
|
public $rows = 5; |
241
|
|
|
public function render($ns, $directive, $value, $name, $config) { |
242
|
|
View Code Duplication |
if (is_array($config) && isset($config[0])) { |
243
|
|
|
$gen_config = $config[0]; |
244
|
|
|
$config = $config[1]; |
245
|
|
|
} else { |
246
|
|
|
$gen_config = $config; |
247
|
|
|
} |
248
|
|
|
$this->prepareGenerator($gen_config); |
249
|
|
|
// this should probably be split up a little |
250
|
|
|
$ret = ''; |
251
|
|
|
$def = $config->def->info["$ns.$directive"]; |
252
|
|
|
if (is_int($def)) { |
253
|
|
|
$type = abs($def); |
254
|
|
|
} else { |
255
|
|
|
$type = $def->type; |
256
|
|
|
} |
257
|
|
|
if (is_array($value)) { |
258
|
|
|
switch ($type) { |
259
|
|
|
case HTMLPurifier_VarParser::LOOKUP: |
260
|
|
|
$array = $value; |
261
|
|
|
$value = array(); |
262
|
|
|
foreach ($array as $val => $b) { |
263
|
|
|
$value[] = $val; |
264
|
|
|
} |
265
|
|
|
case HTMLPurifier_VarParser::ALIST: |
266
|
|
|
$value = implode(PHP_EOL, $value); |
267
|
|
|
break; |
268
|
|
|
case HTMLPurifier_VarParser::HASH: |
269
|
|
|
$nvalue = ''; |
270
|
|
|
foreach ($value as $i => $v) { |
271
|
|
|
$nvalue .= "$i:$v" . PHP_EOL; |
272
|
|
|
} |
273
|
|
|
$value = $nvalue; |
274
|
|
|
break; |
275
|
|
|
default: |
276
|
|
|
$value = ''; |
277
|
|
|
} |
278
|
|
|
} |
279
|
|
|
if ($type === HTMLPurifier_VarParser::MIXED) { |
280
|
|
|
return 'Not supported'; |
281
|
|
|
$value = serialize($value); |
|
|
|
|
282
|
|
|
} |
283
|
|
|
$attr = array( |
284
|
|
|
'name' => "$name"."[$ns.$directive]", |
285
|
|
|
'id' => "$name:$ns.$directive" |
286
|
|
|
); |
287
|
|
|
if ($value === null) $attr['disabled'] = 'disabled'; |
288
|
|
|
if (isset($def->allowed)) { |
289
|
|
|
$ret .= $this->start('select', $attr); |
290
|
|
|
foreach ($def->allowed as $val => $b) { |
291
|
|
|
$attr = array(); |
292
|
|
|
if ($value == $val) $attr['selected'] = 'selected'; |
293
|
|
|
$ret .= $this->element('option', $val, $attr); |
294
|
|
|
} |
295
|
|
|
$ret .= $this->end('select'); |
296
|
|
|
} elseif ( |
297
|
|
|
$type === HTMLPurifier_VarParser::TEXT || |
298
|
|
|
$type === HTMLPurifier_VarParser::ITEXT || |
299
|
|
|
$type === HTMLPurifier_VarParser::ALIST || |
300
|
|
|
$type === HTMLPurifier_VarParser::HASH || |
301
|
|
|
$type === HTMLPurifier_VarParser::LOOKUP |
302
|
|
|
) { |
303
|
|
|
$attr['cols'] = $this->cols; |
304
|
|
|
$attr['rows'] = $this->rows; |
305
|
|
|
$ret .= $this->start('textarea', $attr); |
306
|
|
|
$ret .= $this->text($value); |
307
|
|
|
$ret .= $this->end('textarea'); |
308
|
|
|
} else { |
309
|
|
|
$attr['value'] = $value; |
310
|
|
|
$attr['type'] = 'text'; |
311
|
|
|
$ret .= $this->elementEmpty('input', $attr); |
312
|
|
|
} |
313
|
|
|
return $ret; |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Bool form field printer |
319
|
|
|
*/ |
320
|
|
|
class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { |
321
|
|
|
public function render($ns, $directive, $value, $name, $config) { |
322
|
|
View Code Duplication |
if (is_array($config) && isset($config[0])) { |
323
|
|
|
$gen_config = $config[0]; |
324
|
|
|
$config = $config[1]; |
|
|
|
|
325
|
|
|
} else { |
326
|
|
|
$gen_config = $config; |
327
|
|
|
} |
328
|
|
|
$this->prepareGenerator($gen_config); |
329
|
|
|
$ret = ''; |
330
|
|
|
$ret .= $this->start('div', array('id' => "$name:$ns.$directive")); |
331
|
|
|
|
332
|
|
|
$ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive")); |
333
|
|
|
$ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); |
334
|
|
|
$ret .= $this->text(' Yes'); |
335
|
|
|
$ret .= $this->end('label'); |
336
|
|
|
|
337
|
|
|
$attr = array( |
338
|
|
|
'type' => 'radio', |
339
|
|
|
'name' => "$name"."[$ns.$directive]", |
340
|
|
|
'id' => "$name:Yes_$ns.$directive", |
341
|
|
|
'value' => '1' |
342
|
|
|
); |
343
|
|
|
if ($value === true) $attr['checked'] = 'checked'; |
344
|
|
|
if ($value === null) $attr['disabled'] = 'disabled'; |
345
|
|
|
$ret .= $this->elementEmpty('input', $attr); |
346
|
|
|
|
347
|
|
|
$ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); |
348
|
|
|
$ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); |
349
|
|
|
$ret .= $this->text(' No'); |
350
|
|
|
$ret .= $this->end('label'); |
351
|
|
|
|
352
|
|
|
$attr = array( |
353
|
|
|
'type' => 'radio', |
354
|
|
|
'name' => "$name"."[$ns.$directive]", |
355
|
|
|
'id' => "$name:No_$ns.$directive", |
356
|
|
|
'value' => '0' |
357
|
|
|
); |
358
|
|
|
if ($value === false) $attr['checked'] = 'checked'; |
359
|
|
|
if ($value === null) $attr['disabled'] = 'disabled'; |
360
|
|
|
$ret .= $this->elementEmpty('input', $attr); |
361
|
|
|
|
362
|
|
|
$ret .= $this->end('div'); |
363
|
|
|
|
364
|
|
|
return $ret; |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
// vim: et sw=4 sts=4 |
369
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.