%PDF- %PDF-
Direktori : /www/varak.net/losik.varak.net/vendor/nette/forms/src/Forms/Rendering/ |
Current File : /www/varak.net/losik.varak.net/vendor/nette/forms/src/Forms/Rendering/DefaultFormRenderer.php |
<?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Forms\Rendering; use Nette; use Nette\HtmlStringable; use Nette\Utils\Html; /** * Converts a Form into the HTML output. */ class DefaultFormRenderer implements Nette\Forms\FormRenderer { use Nette\SmartObject; /** * /--- form.container * * /--- error.container * .... error.item [.class] * \--- * * /--- hidden.container * .... HIDDEN CONTROLS * \--- * * /--- group.container * .... group.label * .... group.description * * /--- controls.container * * /--- pair.container [.required .optional .odd] * * /--- label.container * .... LABEL * .... label.suffix * .... label.requiredsuffix * \--- * * /--- control.container [.odd .multi] * .... CONTROL [.required .error .text .password .file .submit .button] * .... control.requiredsuffix * .... control.description * .... control.errorcontainer + control.erroritem * \--- * \--- * \--- * \--- * \-- * @var array of HTML tags */ 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, ], ]; /** @var Nette\Forms\Form */ protected $form; /** @var int */ protected $counter; /** * Provides complete form rendering. * @param string $mode 'begin', 'errors', 'ownerrors', 'body', 'end' or empty to render all */ public function render(Nette\Forms\Form $form, ?string $mode = null): string { if ($this->form !== $form) { $this->form = $form; } $s = ''; if (!$mode || $mode === 'begin') { $s .= $this->renderBegin(); } if (!$mode || strtolower($mode) === 'ownerrors') { $s .= $this->renderErrors(); } elseif ($mode === 'errors') { $s .= $this->renderErrors(null, false); } if (!$mode || $mode === 'body') { $s .= $this->renderBody(); } if (!$mode || $mode === 'end') { $s .= $this->renderEnd(); } return $s; } /** * Renders form begin. */ public function renderBegin(): string { $this->counter = 0; foreach ($this->form->getControls() as $control) { $control->setOption('rendered', false); } if ($this->form->isMethod('get')) { $el = clone $this->form->getElementPrototype(); $el->action = (string) $el->action; $query = parse_url($el->action, PHP_URL_QUERY) ?: ''; $el->action = str_replace("?$query", '', $el->action); $s = ''; foreach (preg_split('#[;&]#', $query, -1, PREG_SPLIT_NO_EMPTY) as $param) { $parts = explode('=', $param, 2); $name = urldecode($parts[0]); $prefix = explode('[', $name, 2)[0]; if (!isset($this->form[$prefix])) { $s .= Html::el('input', ['type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])]); } } return $el->startTag() . ($s ? "\n\t" . $this->getWrapper('hidden container')->setHtml($s) : ''); } else { return $this->form->getElementPrototype()->startTag(); } } /** * Renders form end. */ public function renderEnd(): string { $s = ''; foreach ($this->form->getControls() as $control) { if ($control->getOption('type') === 'hidden' && !$control->getOption('rendered')) { $s .= $control->getControl(); } } if (iterator_count($this->form->getComponents(true, Nette\Forms\Controls\TextInput::class)) < 2) { $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->'; } if ($s) { $s = $this->getWrapper('hidden container')->setHtml($s) . "\n"; } return $s . $this->form->getElementPrototype()->endTag() . "\n"; } /** * Renders validation errors (per form or per control). */ public function renderErrors(?Nette\Forms\Control $control = null, bool $own = true): string { $errors = $control ? $control->getErrors() : ($own ? $this->form->getOwnErrors() : $this->form->getErrors()); return $this->doRenderErrors($errors, (bool) $control); } private function doRenderErrors(array $errors, bool $control): string { if (!$errors) { return ''; } $container = $this->getWrapper($control ? 'control errorcontainer' : 'error container'); $item = $this->getWrapper($control ? 'control erroritem' : 'error item'); foreach ($errors as $error) { $item = clone $item; if ($error instanceof HtmlStringable) { $item->addHtml($error); } else { $item->setText($error); } $container->addHtml($item); } return $control ? "\n\t" . $container->render() : "\n" . $container->render(0); } /** * Renders form body. */ public function renderBody(): string { $s = $remains = ''; $defaultContainer = $this->getWrapper('group container'); $translator = $this->form->getTranslator(); foreach ($this->form->getGroups() as $group) { if (!$group->getControls() || !$group->getOption('visual')) { continue; } $container = $group->getOption('container') ?? $defaultContainer; $container = $container instanceof Html ? clone $container : Html::el($container); $id = $group->getOption('id'); if ($id) { $container->id = $id; } $s .= "\n" . $container->startTag(); $text = $group->getOption('label'); if ($text instanceof HtmlStringable) { $s .= $this->getWrapper('group label')->addHtml($text); } elseif ($text != null) { // intentionally == if ($translator !== null) { $text = $translator->translate($text); } $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n"; } $text = $group->getOption('description'); if ($text instanceof HtmlStringable) { $s .= $text; } elseif ($text != null) { // intentionally == if ($translator !== null) { $text = $translator->translate($text); } $s .= $this->getWrapper('group description')->setText($text) . "\n"; } $s .= $this->renderControls($group); $remains = $container->endTag() . "\n" . $remains; if (!$group->getOption('embedNext')) { $s .= $remains; $remains = ''; } } $s .= $remains . $this->renderControls($this->form); $container = $this->getWrapper('form container'); $container->setHtml($s); return $container->render(0); } /** * Renders group of controls. * @param Nette\Forms\Container|Nette\Forms\ControlGroup $parent */ public function renderControls($parent): string { if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) { throw new Nette\InvalidArgumentException('Argument must be Nette\Forms\Container or Nette\Forms\ControlGroup instance.'); } $container = $this->getWrapper('controls container'); $buttons = null; foreach ($parent->getControls() as $control) { if ( $control->getOption('rendered') || $control->getOption('type') === 'hidden' || $control->getForm(false) !== $this->form ) { // skip } elseif ($control->getOption('type') === 'button') { $buttons[] = $control; } else { if ($buttons) { $container->addHtml($this->renderPairMulti($buttons)); $buttons = null; } $container->addHtml($this->renderPair($control)); } } if ($buttons) { $container->addHtml($this->renderPairMulti($buttons)); } $s = ''; if (count($container)) { $s .= "\n" . $container . "\n"; } return $s; } /** * Renders single visual row. */ public function renderPair(Nette\Forms\Control $control): string { $pair = $this->getWrapper('pair container'); $pair->addHtml($this->renderLabel($control)); $pair->addHtml($this->renderControl($control)); $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), true); $pair->class($control->hasErrors() ? $this->getValue('pair .error') : null, true); $pair->class($control->getOption('class'), true); if (++$this->counter % 2) { $pair->class($this->getValue('pair .odd'), true); } $pair->id = $control->getOption('id'); return $pair->render(0); } /** * Renders single visual row of multiple controls. * @param Nette\Forms\Control[] $controls */ public function renderPairMulti(array $controls): string { $s = []; foreach ($controls as $control) { if (!$control instanceof Nette\Forms\Control) { throw new Nette\InvalidArgumentException('Argument must be array of Nette\Forms\Control instances.'); } $description = $control->getOption('description'); if ($description instanceof HtmlStringable) { $description = ' ' . $description; } elseif ($description != null) { // intentionally == if ($control instanceof Nette\Forms\Controls\BaseControl) { $description = $control->translate($description); } $description = ' ' . $this->getWrapper('control description')->setText($description); } else { $description = ''; } $control->setOption('rendered', true); $el = $this->renderControlElement($control); if ($el instanceof Html) { if ($el->getName() === 'input') { $el->class($this->getValue("control .$el->type"), true); } $el->class($this->getValue('control .error'), $control->hasErrors()); } $s[] = $el . $description; } $pair = $this->getWrapper('pair container'); $pair->addHtml($this->renderLabel($control)); $pair->addHtml($this->getWrapper('control container')->setHtml(implode(' ', $s))); return $pair->render(0); } /** * Renders 'label' part of visual row of controls. */ public function renderLabel(Nette\Forms\Control $control): Html { $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : ''); $label = $this->renderLabelElement($control); if ($label instanceof Html) { $label->addHtml($suffix); if ($control->isRequired()) { $label->class($this->getValue('control .required'), true); } } elseif ($label != null) { // @intentionally == $label .= $suffix; } return $this->getWrapper('label container')->setHtml((string) $label); } /** * Renders 'control' part of visual row of controls. */ public function renderControl(Nette\Forms\Control $control): Html { $body = $this->getWrapper('control container'); if ($this->counter % 2) { $body->class($this->getValue('control .odd'), true); } if (!$this->getWrapper('pair container')->getName()) { $body->class($control->getOption('class'), true); $body->id = $control->getOption('id'); } $description = $control->getOption('description'); if ($description instanceof HtmlStringable) { $description = ' ' . $description; } elseif ($description != null) { // intentionally == if ($control instanceof Nette\Forms\Controls\BaseControl) { $description = $control->translate($description); } $description = ' ' . $this->getWrapper('control description')->setText($description); } else { $description = ''; } if ($control->isRequired()) { $description = $this->getValue('control requiredsuffix') . $description; } $els = $errors = []; renderControl: $control->setOption('rendered', true); $el = $this->renderControlElement($control); if ($el instanceof Html) { if ($el->getName() === 'input') { $el->class($this->getValue("control .$el->type"), true); } $el->class($this->getValue('control .error'), $control->hasErrors()); } $els[] = $el; $errors = array_merge($errors, $control->getErrors()); if ($nextTo = $control->getOption('nextTo')) { $control = $control->getForm()->getComponent($nextTo); $body->class($this->getValue('control .multi'), true); goto renderControl; } return $body->setHtml(implode('', $els) . $description . $this->doRenderErrors($errors, true)); } /** @return string|Html|null */ protected function renderLabelElement(Nette\Forms\Control $control) { return $control->getLabel(); } /** @return string|Html */ protected function renderControlElement(Nette\Forms\Control $control) { return $control->getControl(); } public function getWrapper(string $name): Html { $data = $this->getValue($name); return $data instanceof Html ? clone $data : Html::el($data); } /** @return mixed */ protected function getValue(string $name) { $name = explode(' ', $name); $data = &$this->wrappers[$name[0]][$name[1]]; return $data; } }