PluginProbe
Packeta / 1.6.2
Packeta v1.6.2
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / forms / src / Forms / Rendering / DefaultFormRenderer.php

DefaultFormRenderer.php in Packeta 1.6.2, at deps/nette/forms/src/Forms/Rendering/DefaultFormRenderer.php

376 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\Forms\Rendering;
9
10 use Packetery\Nette;
11 use Packetery\Nette\HtmlStringable;
12 use Packetery\Nette\Utils\Html;
13 /**
14 * Converts a Form into the HTML output.
15 */
16 class DefaultFormRenderer implements \Packetery\Nette\Forms\FormRenderer
17 {
18 use \Packetery\Nette\SmartObject;
19 /**
20 * /--- form.container
21 *
22 * /--- error.container
23 * .... error.item [.class]
24 * \---
25 *
26 * /--- hidden.container
27 * .... HIDDEN CONTROLS
28 * \---
29 *
30 * /--- group.container
31 * .... group.label
32 * .... group.description
33 *
34 * /--- controls.container
35 *
36 * /--- pair.container [.required .optional .odd]
37 *
38 * /--- label.container
39 * .... LABEL
40 * .... label.suffix
41 * .... label.requiredsuffix
42 * \---
43 *
44 * /--- control.container [.odd .multi]
45 * .... CONTROL [.required .error .text .password .file .submit .button]
46 * .... control.requiredsuffix
47 * .... control.description
48 * .... control.errorcontainer + control.erroritem
49 * \---
50 * \---
51 * \---
52 * \---
53 * \--
54 * @var array of HTML tags */
55 public $wrappers = ['form' => ['container' => null], 'error' => ['container' => 'ul class=error', 'item' => 'li'], 'group' => ['container' => 'fieldset', 'label' => 'legend', 'description' => 'p'], 'controls' => ['container' => 'table'], 'pair' => ['container' => 'tr', '.required' => 'required', '.optional' => null, '.odd' => null, '.error' => null], 'control' => ['container' => 'td', '.odd' => null, '.multi' => null, 'description' => 'small', 'requiredsuffix' => '', 'errorcontainer' => 'span class=error', 'erroritem' => '', '.required' => 'required', '.error' => null, '.text' => 'text', '.password' => 'text', '.file' => 'text', '.email' => 'text', '.number' => 'text', '.submit' => 'button', '.image' => 'imagebutton', '.button' => 'button'], 'label' => ['container' => 'th', 'suffix' => null, 'requiredsuffix' => ''], 'hidden' => ['container' => null]];
56 /** @var \Packetery\Nette\Forms\Form */
57 protected $form;
58 /** @var int */
59 protected $counter;
60 /**
61 * Provides complete form rendering.
62 * @param string $mode 'begin', 'errors', 'ownerrors', 'body', 'end' or empty to render all
63 */
64 public function render(\Packetery\Nette\Forms\Form $form, string $mode = null) : string
65 {
66 if ($this->form !== $form) {
67 $this->form = $form;
68 }
69 $s = '';
70 if (!$mode || $mode === 'begin') {
71 $s .= $this->renderBegin();
72 }
73 if (!$mode || \strtolower($mode) === 'ownerrors') {
74 $s .= $this->renderErrors();
75 } elseif ($mode === 'errors') {
76 $s .= $this->renderErrors(null, \false);
77 }
78 if (!$mode || $mode === 'body') {
79 $s .= $this->renderBody();
80 }
81 if (!$mode || $mode === 'end') {
82 $s .= $this->renderEnd();
83 }
84 return $s;
85 }
86 /**
87 * Renders form begin.
88 */
89 public function renderBegin() : string
90 {
91 $this->counter = 0;
92 foreach ($this->form->getControls() as $control) {
93 $control->setOption('rendered', \false);
94 }
95 if ($this->form->isMethod('get')) {
96 $el = clone $this->form->getElementPrototype();
97 $el->action = (string) $el->action;
98 $query = \parse_url($el->action, \PHP_URL_QUERY) ?: '';
99 $el->action = \str_replace("?{$query}", '', $el->action);
100 $s = '';
101 foreach (\preg_split('#[;&]#', $query, -1, \PREG_SPLIT_NO_EMPTY) as $param) {
102 $parts = \explode('=', $param, 2);
103 $name = \urldecode($parts[0]);
104 $prefix = \explode('[', $name, 2)[0];
105 if (!isset($this->form[$prefix])) {
106 $s .= Html::el('input', ['type' => 'hidden', 'name' => $name, 'value' => \urldecode($parts[1])]);
107 }
108 }
109 return $el->startTag() . ($s ? "\n\t" . $this->getWrapper('hidden container')->setHtml($s) : '');
110 } else {
111 return $this->form->getElementPrototype()->startTag();
112 }
113 }
114 /**
115 * Renders form end.
116 */
117 public function renderEnd() : string
118 {
119 $s = '';
120 foreach ($this->form->getControls() as $control) {
121 if ($control->getOption('type') === 'hidden' && !$control->getOption('rendered')) {
122 $s .= $control->getControl();
123 }
124 }
125 if (\iterator_count($this->form->getComponents(\true, \Packetery\Nette\Forms\Controls\TextInput::class)) < 2) {
126 $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
127 }
128 if ($s) {
129 $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
130 }
131 return $s . $this->form->getElementPrototype()->endTag() . "\n";
132 }
133 /**
134 * Renders validation errors (per form or per control).
135 */
136 public function renderErrors(\Packetery\Nette\Forms\Control $control = null, bool $own = \true) : string
137 {
138 $errors = $control ? $control->getErrors() : ($own ? $this->form->getOwnErrors() : $this->form->getErrors());
139 return $this->doRenderErrors($errors, (bool) $control);
140 }
141 private function doRenderErrors(array $errors, bool $control) : string
142 {
143 if (!$errors) {
144 return '';
145 }
146 $container = $this->getWrapper($control ? 'control errorcontainer' : 'error container');
147 $item = $this->getWrapper($control ? 'control erroritem' : 'error item');
148 foreach ($errors as $error) {
149 $item = clone $item;
150 if ($error instanceof HtmlStringable) {
151 $item->addHtml($error);
152 } else {
153 $item->setText($error);
154 }
155 $container->addHtml($item);
156 }
157 return $control ? "\n\t" . $container->render() : "\n" . $container->render(0);
158 }
159 /**
160 * Renders form body.
161 */
162 public function renderBody() : string
163 {
164 $s = $remains = '';
165 $defaultContainer = $this->getWrapper('group container');
166 $translator = $this->form->getTranslator();
167 foreach ($this->form->getGroups() as $group) {
168 if (!$group->getControls() || !$group->getOption('visual')) {
169 continue;
170 }
171 $container = $group->getOption('container', $defaultContainer);
172 $container = $container instanceof Html ? clone $container : Html::el($container);
173 $id = $group->getOption('id');
174 if ($id) {
175 $container->id = $id;
176 }
177 $s .= "\n" . $container->startTag();
178 $text = $group->getOption('label');
179 if ($text instanceof HtmlStringable) {
180 $s .= $this->getWrapper('group label')->addHtml($text);
181 } elseif ($text != null) {
182 // intentionally ==
183 if ($translator !== null) {
184 $text = $translator->translate($text);
185 }
186 $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
187 }
188 $text = $group->getOption('description');
189 if ($text instanceof HtmlStringable) {
190 $s .= $text;
191 } elseif ($text != null) {
192 // intentionally ==
193 if ($translator !== null) {
194 $text = $translator->translate($text);
195 }
196 $s .= $this->getWrapper('group description')->setText($text) . "\n";
197 }
198 $s .= $this->renderControls($group);
199 $remains = $container->endTag() . "\n" . $remains;
200 if (!$group->getOption('embedNext')) {
201 $s .= $remains;
202 $remains = '';
203 }
204 }
205 $s .= $remains . $this->renderControls($this->form);
206 $container = $this->getWrapper('form container');
207 $container->setHtml($s);
208 return $container->render(0);
209 }
210 /**
211 * Renders group of controls.
212 * @param \Packetery\Nette\Forms\Container|\Packetery\Nette\Forms\ControlGroup $parent
213 */
214 public function renderControls($parent) : string
215 {
216 if (!($parent instanceof \Packetery\Nette\Forms\Container || $parent instanceof \Packetery\Nette\Forms\ControlGroup)) {
217 throw new \Packetery\Nette\InvalidArgumentException('Argument must be \\Packetery\\Nette\\Forms\\Container or \\Packetery\\Nette\\Forms\\ControlGroup instance.');
218 }
219 $container = $this->getWrapper('controls container');
220 $buttons = null;
221 foreach ($parent->getControls() as $control) {
222 if ($control->getOption('rendered') || $control->getOption('type') === 'hidden' || $control->getForm(\false) !== $this->form) {
223 // skip
224 } elseif ($control->getOption('type') === 'button') {
225 $buttons[] = $control;
226 } else {
227 if ($buttons) {
228 $container->addHtml($this->renderPairMulti($buttons));
229 $buttons = null;
230 }
231 $container->addHtml($this->renderPair($control));
232 }
233 }
234 if ($buttons) {
235 $container->addHtml($this->renderPairMulti($buttons));
236 }
237 $s = '';
238 if (\count($container)) {
239 $s .= "\n" . $container . "\n";
240 }
241 return $s;
242 }
243 /**
244 * Renders single visual row.
245 */
246 public function renderPair(\Packetery\Nette\Forms\Control $control) : string
247 {
248 $pair = $this->getWrapper('pair container');
249 $pair->addHtml($this->renderLabel($control));
250 $pair->addHtml($this->renderControl($control));
251 $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), \true);
252 $pair->class($control->hasErrors() ? $this->getValue('pair .error') : null, \true);
253 $pair->class($control->getOption('class'), \true);
254 if (++$this->counter % 2) {
255 $pair->class($this->getValue('pair .odd'), \true);
256 }
257 $pair->id = $control->getOption('id');
258 return $pair->render(0);
259 }
260 /**
261 * Renders single visual row of multiple controls.
262 * @param \Packetery\Nette\Forms\Control[] $controls
263 */
264 public function renderPairMulti(array $controls) : string
265 {
266 $s = [];
267 foreach ($controls as $control) {
268 if (!$control instanceof \Packetery\Nette\Forms\Control) {
269 throw new \Packetery\Nette\InvalidArgumentException('Argument must be array of \\Packetery\\Nette\\Forms\\IControl instances.');
270 }
271 $description = $control->getOption('description');
272 if ($description instanceof HtmlStringable) {
273 $description = ' ' . $description;
274 } elseif ($description != null) {
275 // intentionally ==
276 if ($control instanceof \Packetery\Nette\Forms\Controls\BaseControl) {
277 $description = $control->translate($description);
278 }
279 $description = ' ' . $this->getWrapper('control description')->setText($description);
280 } else {
281 $description = '';
282 }
283 $control->setOption('rendered', \true);
284 $el = $control->getControl();
285 if ($el instanceof Html) {
286 if ($el->getName() === 'input') {
287 $el->class($this->getValue("control .{$el->type}"), \true);
288 }
289 $el->class($this->getValue('control .error'), $control->hasErrors());
290 }
291 $s[] = $el . $description;
292 }
293 $pair = $this->getWrapper('pair container');
294 $pair->addHtml($this->renderLabel($control));
295 $pair->addHtml($this->getWrapper('control container')->setHtml(\implode(' ', $s)));
296 return $pair->render(0);
297 }
298 /**
299 * Renders 'label' part of visual row of controls.
300 */
301 public function renderLabel(\Packetery\Nette\Forms\Control $control) : Html
302 {
303 $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
304 $label = $control->getLabel();
305 if ($label instanceof Html) {
306 $label->addHtml($suffix);
307 if ($control->isRequired()) {
308 $label->class($this->getValue('control .required'), \true);
309 }
310 } elseif ($label != null) {
311 // @intentionally ==
312 $label .= $suffix;
313 }
314 return $this->getWrapper('label container')->setHtml((string) $label);
315 }
316 /**
317 * Renders 'control' part of visual row of controls.
318 */
319 public function renderControl(\Packetery\Nette\Forms\Control $control) : Html
320 {
321 $body = $this->getWrapper('control container');
322 if ($this->counter % 2) {
323 $body->class($this->getValue('control .odd'), \true);
324 }
325 if (!$this->getWrapper('pair container')->getName()) {
326 $body->class($control->getOption('class'), \true);
327 $body->id = $control->getOption('id');
328 }
329 $description = $control->getOption('description');
330 if ($description instanceof HtmlStringable) {
331 $description = ' ' . $description;
332 } elseif ($description != null) {
333 // intentionally ==
334 if ($control instanceof \Packetery\Nette\Forms\Controls\BaseControl) {
335 $description = $control->translate($description);
336 }
337 $description = ' ' . $this->getWrapper('control description')->setText($description);
338 } else {
339 $description = '';
340 }
341 if ($control->isRequired()) {
342 $description = $this->getValue('control requiredsuffix') . $description;
343 }
344 $els = $errors = [];
345 renderControl:
346 $control->setOption('rendered', \true);
347 $el = $control->getControl();
348 if ($el instanceof Html) {
349 if ($el->getName() === 'input') {
350 $el->class($this->getValue("control .{$el->type}"), \true);
351 }
352 $el->class($this->getValue('control .error'), $control->hasErrors());
353 }
354 $els[] = $el;
355 $errors = \array_merge($errors, $control->getErrors());
356 if ($nextTo = $control->getOption('nextTo')) {
357 $control = $control->getForm()->getComponent($nextTo);
358 $body->class($this->getValue('control .multi'), \true);
359 goto renderControl;
360 }
361 return $body->setHtml(\implode('', $els) . $description . $this->doRenderErrors($errors, \true));
362 }
363 public function getWrapper(string $name) : Html
364 {
365 $data = $this->getValue($name);
366 return $data instanceof Html ? clone $data : Html::el($data);
367 }
368 /** @return mixed */
369 protected function getValue(string $name)
370 {
371 $name = \explode(' ', $name);
372 $data =& $this->wrappers[$name[0]][$name[1]];
373 return $data;
374 }
375 }
376