~ubuntu-branches/debian/experimental/php-nette/experimental

« back to all changes in this revision

Viewing changes to Nette-2.0.13/Nette/Forms/Rendering/DefaultFormRenderer.php

  • Committer: Package Import Robot
  • Author(s): David Prévot
  • Date: 2013-11-30 08:47:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20131130084754-4udf1xsu9085tnfc
Tags: 2.1.0~rc-1
* New upstream branch
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
 
3
 
/**
4
 
 * This file is part of the Nette Framework (http://nette.org)
5
 
 *
6
 
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7
 
 *
8
 
 * For the full copyright and license information, please view
9
 
 * the file license.txt that was distributed with this source code.
10
 
 */
11
 
 
12
 
namespace Nette\Forms\Rendering;
13
 
 
14
 
use Nette,
15
 
        Nette\Utils\Html;
16
 
 
17
 
 
18
 
/**
19
 
 * Converts a Form into the HTML output.
20
 
 *
21
 
 * @author     David Grudl
22
 
 */
23
 
class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
24
 
{
25
 
        /**
26
 
         *  /--- form.container
27
 
         *
28
 
         *    /--- if (form.errors) error.container
29
 
         *      .... error.item [.class]
30
 
         *    \---
31
 
         *
32
 
         *    /--- hidden.container
33
 
         *      .... HIDDEN CONTROLS
34
 
         *    \---
35
 
         *
36
 
         *    /--- group.container
37
 
         *      .... group.label
38
 
         *      .... group.description
39
 
         *
40
 
         *      /--- controls.container
41
 
         *
42
 
         *        /--- pair.container [.required .optional .odd]
43
 
         *
44
 
         *          /--- label.container
45
 
         *            .... LABEL
46
 
         *            .... label.suffix
47
 
         *            .... label.requiredsuffix
48
 
         *          \---
49
 
         *
50
 
         *          /--- control.container [.odd]
51
 
         *            .... CONTROL [.required .text .password .file .submit .button]
52
 
         *            .... control.requiredsuffix
53
 
         *            .... control.description
54
 
         *            .... if (control.errors) error.container
55
 
         *          \---
56
 
         *        \---
57
 
         *      \---
58
 
         *    \---
59
 
         *  \--
60
 
         *
61
 
         * @var array of HTML tags */
62
 
        public $wrappers = array(
63
 
                'form' => array(
64
 
                        'container' => NULL,
65
 
                        'errors' => TRUE,
66
 
                ),
67
 
 
68
 
                'error' => array(
69
 
                        'container' => 'ul class=error',
70
 
                        'item' => 'li',
71
 
                ),
72
 
 
73
 
                'group' => array(
74
 
                        'container' => 'fieldset',
75
 
                        'label' => 'legend',
76
 
                        'description' => 'p',
77
 
                ),
78
 
 
79
 
                'controls' => array(
80
 
                        'container' => 'table',
81
 
                ),
82
 
 
83
 
                'pair' => array(
84
 
                        'container' => 'tr',
85
 
                        '.required' => 'required',
86
 
                        '.optional' => NULL,
87
 
                        '.odd' => NULL,
88
 
                ),
89
 
 
90
 
                'control' => array(
91
 
                        'container' => 'td',
92
 
                        '.odd' => NULL,
93
 
 
94
 
                        'errors' => FALSE,
95
 
                        'description' => 'small',
96
 
                        'requiredsuffix' => '',
97
 
 
98
 
                        '.required' => 'required',
99
 
                        '.text' => 'text',
100
 
                        '.password' => 'text',
101
 
                        '.file' => 'text',
102
 
                        '.submit' => 'button',
103
 
                        '.image' => 'imagebutton',
104
 
                        '.button' => 'button',
105
 
                ),
106
 
 
107
 
                'label' => array(
108
 
                        'container' => 'th',
109
 
                        'suffix' => NULL,
110
 
                        'requiredsuffix' => '',
111
 
                ),
112
 
 
113
 
                'hidden' => array(
114
 
                        'container' => 'div',
115
 
                ),
116
 
        );
117
 
 
118
 
        /** @var Nette\Forms\Form */
119
 
        protected $form;
120
 
 
121
 
        /** @var int */
122
 
        protected $counter;
123
 
 
124
 
 
125
 
        /**
126
 
         * Provides complete form rendering.
127
 
         * @param  Nette\Forms\Form
128
 
         * @param  string 'begin', 'errors', 'body', 'end' or empty to render all
129
 
         * @return string
130
 
         */
131
 
        public function render(Nette\Forms\Form $form, $mode = NULL)
132
 
        {
133
 
                if ($this->form !== $form) {
134
 
                        $this->form = $form;
135
 
                        $this->init();
136
 
                }
137
 
 
138
 
                $s = '';
139
 
                if (!$mode || $mode === 'begin') {
140
 
                        $s .= $this->renderBegin();
141
 
                }
142
 
                if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
143
 
                        $s .= $this->renderErrors();
144
 
                }
145
 
                if (!$mode || $mode === 'body') {
146
 
                        $s .= $this->renderBody();
147
 
                }
148
 
                if (!$mode || $mode === 'end') {
149
 
                        $s .= $this->renderEnd();
150
 
                }
151
 
                return $s;
152
 
        }
153
 
 
154
 
 
155
 
        /** @deprecated */
156
 
        public function setClientScript()
157
 
        {
158
 
                trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
159
 
                return $this;
160
 
        }
161
 
 
162
 
 
163
 
        /**
164
 
         * Initializes form.
165
 
         * @return void
166
 
         */
167
 
        protected function init()
168
 
        {
169
 
                // TODO: only for back compatiblity - remove?
170
 
                $wrapper = & $this->wrappers['control'];
171
 
                foreach ($this->form->getControls() as $control) {
172
 
                        if ($control->isRequired() && isset($wrapper['.required'])) {
173
 
                                $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
174
 
                        }
175
 
 
176
 
                        $el = $control->getControlPrototype();
177
 
                        if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
178
 
                                $el->class($wrapper['.' . $el->type], TRUE);
179
 
                        }
180
 
                }
181
 
        }
182
 
 
183
 
 
184
 
        /**
185
 
         * Renders form begin.
186
 
         * @return string
187
 
         */
188
 
        public function renderBegin()
189
 
        {
190
 
                $this->counter = 0;
191
 
 
192
 
                foreach ($this->form->getControls() as $control) {
193
 
                        $control->setOption('rendered', FALSE);
194
 
                }
195
 
 
196
 
                if (strcasecmp($this->form->getMethod(), 'get') === 0) {
197
 
                        $el = clone $this->form->getElementPrototype();
198
 
                        $url = explode('?', (string) $el->action, 2);
199
 
                        $el->action = $url[0];
200
 
                        $s = '';
201
 
                        if (isset($url[1])) {
202
 
                                foreach (preg_split('#[;&]#', $url[1]) as $param) {
203
 
                                        $parts = explode('=', $param, 2);
204
 
                                        $name = urldecode($parts[0]);
205
 
                                        if (!isset($this->form[$name])) {
206
 
                                                $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
207
 
                                        }
208
 
                                }
209
 
                                $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
210
 
                        }
211
 
                        return $el->startTag() . $s;
212
 
 
213
 
 
214
 
                } else {
215
 
                        return $this->form->getElementPrototype()->startTag();
216
 
                }
217
 
        }
218
 
 
219
 
 
220
 
        /**
221
 
         * Renders form end.
222
 
         * @return string
223
 
         */
224
 
        public function renderEnd()
225
 
        {
226
 
                $s = '';
227
 
                foreach ($this->form->getControls() as $control) {
228
 
                        if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
229
 
                                $s .= (string) $control->getControl();
230
 
                        }
231
 
                }
232
 
                if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
233
 
                        $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
234
 
                }
235
 
                if ($s) {
236
 
                        $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
237
 
                }
238
 
 
239
 
                return $s . $this->form->getElementPrototype()->endTag() . "\n";
240
 
        }
241
 
 
242
 
 
243
 
        /**
244
 
         * Renders validation errors (per form or per control).
245
 
         * @return string
246
 
         */
247
 
        public function renderErrors(Nette\Forms\IControl $control = NULL)
248
 
        {
249
 
                $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
250
 
                if (count($errors)) {
251
 
                        $ul = $this->getWrapper('error container');
252
 
                        $li = $this->getWrapper('error item');
253
 
 
254
 
                        foreach ($errors as $error) {
255
 
                                $item = clone $li;
256
 
                                if ($error instanceof Html) {
257
 
                                        $item->add($error);
258
 
                                } else {
259
 
                                        $item->setText($error);
260
 
                                }
261
 
                                $ul->add($item);
262
 
                        }
263
 
                        return "\n" . $ul->render(0);
264
 
                }
265
 
        }
266
 
 
267
 
 
268
 
        /**
269
 
         * Renders form body.
270
 
         * @return string
271
 
         */
272
 
        public function renderBody()
273
 
        {
274
 
                $s = $remains = '';
275
 
 
276
 
                $defaultContainer = $this->getWrapper('group container');
277
 
                $translator = $this->form->getTranslator();
278
 
 
279
 
                foreach ($this->form->getGroups() as $group) {
280
 
                        if (!$group->getControls() || !$group->getOption('visual')) {
281
 
                                continue;
282
 
                        }
283
 
 
284
 
                        $container = $group->getOption('container', $defaultContainer);
285
 
                        $container = $container instanceof Html ? clone $container : Html::el($container);
286
 
 
287
 
                        $s .= "\n" . $container->startTag();
288
 
 
289
 
                        $text = $group->getOption('label');
290
 
                        if ($text instanceof Html) {
291
 
                                $s .= $text;
292
 
 
293
 
                        } elseif (is_string($text)) {
294
 
                                if ($translator !== NULL) {
295
 
                                        $text = $translator->translate($text);
296
 
                                }
297
 
                                $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
298
 
                        }
299
 
 
300
 
                        $text = $group->getOption('description');
301
 
                        if ($text instanceof Html) {
302
 
                                $s .= $text;
303
 
 
304
 
                        } elseif (is_string($text)) {
305
 
                                if ($translator !== NULL) {
306
 
                                        $text = $translator->translate($text);
307
 
                                }
308
 
                                $s .= $this->getWrapper('group description')->setText($text) . "\n";
309
 
                        }
310
 
 
311
 
                        $s .= $this->renderControls($group);
312
 
 
313
 
                        $remains = $container->endTag() . "\n" . $remains;
314
 
                        if (!$group->getOption('embedNext')) {
315
 
                                $s .= $remains;
316
 
                                $remains = '';
317
 
                        }
318
 
                }
319
 
 
320
 
                $s .= $remains . $this->renderControls($this->form);
321
 
 
322
 
                $container = $this->getWrapper('form container');
323
 
                $container->setHtml($s);
324
 
                return $container->render(0);
325
 
        }
326
 
 
327
 
 
328
 
        /**
329
 
         * Renders group of controls.
330
 
         * @param  Nette\Forms\Container|FormGroup
331
 
         * @return string
332
 
         */
333
 
        public function renderControls($parent)
334
 
        {
335
 
                if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
336
 
                        throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
337
 
                }
338
 
 
339
 
                $container = $this->getWrapper('controls container');
340
 
 
341
 
                $buttons = NULL;
342
 
                foreach ($parent->getControls() as $control) {
343
 
                        if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
344
 
                                // skip
345
 
 
346
 
                        } elseif ($control instanceof Nette\Forms\Controls\Button) {
347
 
                                $buttons[] = $control;
348
 
 
349
 
                        } else {
350
 
                                if ($buttons) {
351
 
                                        $container->add($this->renderPairMulti($buttons));
352
 
                                        $buttons = NULL;
353
 
                                }
354
 
                                $container->add($this->renderPair($control));
355
 
                        }
356
 
                }
357
 
 
358
 
                if ($buttons) {
359
 
                        $container->add($this->renderPairMulti($buttons));
360
 
                }
361
 
 
362
 
                $s = '';
363
 
                if (count($container)) {
364
 
                        $s .= "\n" . $container . "\n";
365
 
                }
366
 
 
367
 
                return $s;
368
 
        }
369
 
 
370
 
 
371
 
        /**
372
 
         * Renders single visual row.
373
 
         * @return string
374
 
         */
375
 
        public function renderPair(Nette\Forms\IControl $control)
376
 
        {
377
 
                $pair = $this->getWrapper('pair container');
378
 
                $pair->add($this->renderLabel($control));
379
 
                $pair->add($this->renderControl($control));
380
 
                $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
381
 
                $pair->class($control->getOption('class'), TRUE);
382
 
                if (++$this->counter % 2) {
383
 
                        $pair->class($this->getValue('pair .odd'), TRUE);
384
 
                }
385
 
                $pair->id = $control->getOption('id');
386
 
                return $pair->render(0);
387
 
        }
388
 
 
389
 
 
390
 
        /**
391
 
         * Renders single visual row of multiple controls.
392
 
         * @param  IFormControl[]
393
 
         * @return string
394
 
         */
395
 
        public function renderPairMulti(array $controls)
396
 
        {
397
 
                $s = array();
398
 
                foreach ($controls as $control) {
399
 
                        if (!$control instanceof Nette\Forms\IControl) {
400
 
                                throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances.");
401
 
                        }
402
 
                        $s[] = (string) $control->getControl();
403
 
                }
404
 
                $pair = $this->getWrapper('pair container');
405
 
                $pair->add($this->renderLabel($control));
406
 
                $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
407
 
                return $pair->render(0);
408
 
        }
409
 
 
410
 
 
411
 
        /**
412
 
         * Renders 'label' part of visual row of controls.
413
 
         * @return string
414
 
         */
415
 
        public function renderLabel(Nette\Forms\IControl $control)
416
 
        {
417
 
                $head = $this->getWrapper('label container');
418
 
 
419
 
                if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
420
 
                        return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? '&nbsp;' : '');
421
 
 
422
 
                } else {
423
 
                        $label = $control->getLabel();
424
 
                        $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
425
 
                        if ($label instanceof Html) {
426
 
                                $label->setHtml($label->getHtml() . $suffix);
427
 
                                $suffix = '';
428
 
                        }
429
 
                        return $head->setHtml((string) $label . $suffix);
430
 
                }
431
 
        }
432
 
 
433
 
 
434
 
        /**
435
 
         * Renders 'control' part of visual row of controls.
436
 
         * @return string
437
 
         */
438
 
        public function renderControl(Nette\Forms\IControl $control)
439
 
        {
440
 
                $body = $this->getWrapper('control container');
441
 
                if ($this->counter % 2) {
442
 
                        $body->class($this->getValue('control .odd'), TRUE);
443
 
                }
444
 
 
445
 
                $description = $control->getOption('description');
446
 
                if ($description instanceof Html) {
447
 
                        $description = ' ' . $control->getOption('description');
448
 
 
449
 
                } elseif (is_string($description)) {
450
 
                        $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
451
 
 
452
 
                } else {
453
 
                        $description = '';
454
 
                }
455
 
 
456
 
                if ($control->isRequired()) {
457
 
                        $description = $this->getValue('control requiredsuffix') . $description;
458
 
                }
459
 
 
460
 
                if ($this->getValue('control errors')) {
461
 
                        $description .= $this->renderErrors($control);
462
 
                }
463
 
 
464
 
                if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
465
 
                        return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
466
 
 
467
 
                } else {
468
 
                        return $body->setHtml((string) $control->getControl() . $description);
469
 
                }
470
 
        }
471
 
 
472
 
 
473
 
        /**
474
 
         * @param  string
475
 
         * @return Nette\Utils\Html
476
 
         */
477
 
        protected function getWrapper($name)
478
 
        {
479
 
                $data = $this->getValue($name);
480
 
                return $data instanceof Html ? clone $data : Html::el($data);
481
 
        }
482
 
 
483
 
 
484
 
        /**
485
 
         * @param  string
486
 
         * @return string
487
 
         */
488
 
        protected function getValue($name)
489
 
        {
490
 
                $name = explode(' ', $name);
491
 
                $data = & $this->wrappers[$name[0]][$name[1]];
492
 
                return $data;
493
 
        }
494
 
 
495
 
}