From 35c2c034fd7c6e636223d54c58052d4c5e73f344 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 7 Jun 2018 23:32:39 +0200 Subject: [PATCH] ipl: add ipl\Html\Form prototype --- library/vendor/ipl/Html/Form.php | 294 ++++++++++++++++++ .../ipl/Html/FormDecorator/DdDtDecorator.php | 134 ++++++++ .../ipl/Html/FormElement/BaseFormElement.php | 279 +++++++++++++++++ .../Html/FormElement/FormElementContainer.php | 157 ++++++++++ .../ipl/Html/FormElement/HiddenElement.php | 8 + .../ipl/Html/FormElement/InputElement.php | 35 +++ .../ipl/Html/FormElement/SelectElement.php | 46 +++ .../ipl/Html/FormElement/SelectOption.php | 43 +++ .../ipl/Html/FormElement/SubmitElement.php | 47 +++ .../ipl/Html/FormElement/TextElement.php | 8 + .../ipl/Html/FormElement/TextareaElement.php | 10 + .../ipl/Validator/GreaterThanValidator.php | 38 +++ .../vendor/ipl/Validator/MessageContainer.php | 38 +++ .../vendor/ipl/Validator/SimpleValidator.php | 24 ++ .../ipl/Validator/ValidatorInterface.php | 18 ++ 15 files changed, 1179 insertions(+) create mode 100644 library/vendor/ipl/Html/Form.php create mode 100644 library/vendor/ipl/Html/FormDecorator/DdDtDecorator.php create mode 100644 library/vendor/ipl/Html/FormElement/BaseFormElement.php create mode 100644 library/vendor/ipl/Html/FormElement/FormElementContainer.php create mode 100644 library/vendor/ipl/Html/FormElement/HiddenElement.php create mode 100644 library/vendor/ipl/Html/FormElement/InputElement.php create mode 100644 library/vendor/ipl/Html/FormElement/SelectElement.php create mode 100644 library/vendor/ipl/Html/FormElement/SelectOption.php create mode 100644 library/vendor/ipl/Html/FormElement/SubmitElement.php create mode 100644 library/vendor/ipl/Html/FormElement/TextElement.php create mode 100644 library/vendor/ipl/Html/FormElement/TextareaElement.php create mode 100644 library/vendor/ipl/Validator/GreaterThanValidator.php create mode 100644 library/vendor/ipl/Validator/MessageContainer.php create mode 100644 library/vendor/ipl/Validator/SimpleValidator.php create mode 100644 library/vendor/ipl/Validator/ValidatorInterface.php diff --git a/library/vendor/ipl/Html/Form.php b/library/vendor/ipl/Html/Form.php new file mode 100644 index 00000000..1374a755 --- /dev/null +++ b/library/vendor/ipl/Html/Form.php @@ -0,0 +1,294 @@ +request = $request; + + if ($this->getAction() === null) { + $this->setAction($request->getUrl()); + } + + return $this; + } + + /** + * @return Request|null + */ + public function getRequest() + { + return $this->request; + } + + /** + * @param Request $request + * @return $this + */ + public function handleRequest(Request $request) + { + $this->setRequest($request); + if ($this->hasBeenSent()) { + $this->populate($request->getParams()); + } + + $this->ensureAssembled(); + if ($this->hasBeenSubmitted()) { + if ($this->isValid()) { + $this->onSuccess(); + } else { + $this->onError(); + } + } elseif ($this->hasBeenSent()) { + $this->validatePartial(); + } + + return $this; + } + + public function onSuccess() + { + // $this->redirectOnSuccess(); + } + + public function onError() + { + /** + $error = Html::tag('p', ['class' => 'error'], 'ERROR: '); + foreach ($this->getElements() as $element) { + foreach ($element->getMessages() as $message) { + $error->add(sprintf('%s: %s', $element->getName(), $message)); + } + } + + $this->add($error); + */ + } + + // TODO: onElementRegistered + public function onRegisteredElement($name, BaseFormElement $element) + { + if ($element instanceof SubmitElement && ! $this->hasSubmitButton()) { + $this->setSubmitButton($element); + } + + if (array_key_exists($name, $this->populatedValues)) { + $element->setValue($this->populatedValues[$name]); + } + } + + public function isValid() + { + if ($this->isValid === null) { + $this->validate(); + } + + return $this->isValid; + } + + public function validate() + { + $valid = true; + foreach ($this->elements as $element) { + if ($element->isRequired() && ! $element->hasValue()) { + $element->addMessage('This field is required'); + $valid = false; + continue; + } + if (! $element->isValid()) { + $valid = false; + } + } + + $this->isValid = $valid; + } + + public function validatePartial() + { + foreach ($this->getElements() as $element) { + if ($element->hasValue()) { + $element->validate(); + } + } + } + + public function getValue($name, $default = null) + { + if ($this->hasElement($name)) { + return $this->getElement($name)->getValue(); + } else { + return $default; + } + } + + public function getValues() + { + $values = []; + foreach ($this->getElements() as $element) { + if (! $element->isIgnored()) { + $values[] = $element->getValue(); + } + } + + return $values; + } + + /** + * @return bool + */ + public function hasBeenSent() + { + if ($this->request === null) { + return false; + } + + if ($this->request->getMethod() !== $this->getMethod()) { + return false; + } + + // TODO: Check form name element + + return true; + } + + public function getSuccessUrl() + { + return $this->getAction(); + } + + public function redirectOnSuccess() + { + $this->request->getResponse()->redirectAndExit($this->getSuccessUrl()); + } + + /** + * @return bool + */ + public function hasBeenSubmitted() + { + if ($this->hasSubmitButton()) { + return $this->getSubmitButton()->hasBeenPressed(); + } else { + return $this->hasBeenSent(); + } + } + + public function getSubmitButton() + { + return $this->submitButton; + } + + public function hasSubmitButton() + { + return $this->submitButton !== null; + } + + public function setSubmitButton(SubmitElement $element) + { + $this->submitButton = $element; + + return $this; + } + + public function populate($values) + { + foreach ($values as $name => $value) { + $this->populatedValues[$name] = $value; + if ($this->hasElement($name)) { + try { + $element = $this->getElement($name); + } catch (InvalidArgumentException $exception) { + // This will not happen, as we checked for hasElement + } + + $element->setValue($value); + } + } + } + + /** + * @return mixed + */ + public function getMethod() + { + $method = $this->getAttributes()->get('method')->getValue(); + if ($method === null) { + // WRONG. Problem: + // right now we get the method in assemble, that's too late. + // TODO: fix this via getMethodAttribute callback + return 'POST'; + } + + return $method; + } + + /** + * @param $method + * @return $this + */ + public function setMethod($method) + { + $this->getAttributes()->set('method', strtoupper($method)); + + return $this; + } + + /** + * @return string + */ + public function getAction() + { + return $this->getAttributes()->get('action')->getValue(); + } + + /** + * @param $action + * @return $this + */ + public function setAction($action) + { + $this->getAttributes()->set('action', $action); + + return $this; + } +} diff --git a/library/vendor/ipl/Html/FormDecorator/DdDtDecorator.php b/library/vendor/ipl/Html/FormDecorator/DdDtDecorator.php new file mode 100644 index 00000000..dd2ca54f --- /dev/null +++ b/library/vendor/ipl/Html/FormDecorator/DdDtDecorator.php @@ -0,0 +1,134 @@ +wrapped = $document; + $document->addWrapper($newWrapper); + + return $newWrapper; + } + + protected function renderLabel() + { + if ($this->wrapped instanceof BaseFormElement) { + $label = $this->wrapped->getLabel(); + if (strlen($label)) { + return Html::tag('label', null, $label); + } + } + + return null; + } + + public function XXrenderAttributes() + { + // TODO: only when sent?! + if ($this->wrapped instanceof BaseFormElement) { + if (! $this->wrapped->isValid()) { + $this->getAttributes()->add('class', 'errors'); + } + } + + return parent::renderAttributes(); + } + + protected function renderDescription() + { + if ($this->wrapped instanceof BaseFormElement) { + $description = $this->wrapped->getDescription(); + if (strlen($description)) { + return Html::tag('p', ['class' => 'description'], $description); + } + } + + return null; + } + + protected function renderErrors() + { + if ($this->wrapped instanceof BaseFormElement) { + $errors = []; + foreach ($this->wrapped->getMessages() as $message) { + $errors[] = Html::tag('p', ['class' => 'error'], $message); + } + + if (! empty($errors)) { + return $errors; + } + } + + return null; + } + + public function add($content) + { + if ($content !== $this->wrapped) { + parent::add($content); + } + + return $this; + } + + /** + * @throws \Icinga\Exception\IcingaException + */ + protected function assemble() + { + $this->add([$this->dt(), $this->dd()]); + $this->ready = true; + } + + public function dt() + { + if ($this->dt === null) { + $this->dt = Html::tag('dt', null, $this->renderLabel()); + } + + return $this->dt; + } + + /** + * @return \dipl\Html\HtmlElement + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ + public function dd() + { + if ($this->dd === null) { + $this->dd = Html::tag('dd', null, [ + $this->wrapped, + $this->renderErrors(), + $this->renderDescription() + ]); + } + + return $this->dd; + } +} diff --git a/library/vendor/ipl/Html/FormElement/BaseFormElement.php b/library/vendor/ipl/Html/FormElement/BaseFormElement.php new file mode 100644 index 00000000..8e7ba602 --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/BaseFormElement.php @@ -0,0 +1,279 @@ +getAttributes() + ->registerAttributeCallback('label', [$this, 'getNoAttribute'], [$this, 'setLabel']) + ->registerAttributeCallback('name', [$this, 'getNameAttribute'], [$this, 'setName']) + ->registerAttributeCallback('value', [$this, 'getValueAttribute'], [$this, 'setValue']) + ->registerAttributeCallback('description', [$this, 'getNoAttribute'], [$this, 'setDescription']) + ->registerAttributeCallback('validators', null, [$this, 'setValidators']) + ->registerAttributeCallback('required', [$this, 'getRequiredAttribute'], [$this, 'setRequired']); + if ($attributes !== null) { + $this->addAttributes($attributes); + } + $this->setName($name); + } + + /** + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * @param string $description + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * @param string $label + * @return $this + */ + public function setLabel($label) + { + $this->label = $label; + + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $name + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * @param $value + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + $this->isValid = null; + + return $this; + } + + public function isRequired() + { + return $this->required; + } + + public function setRequired($required = true) + { + $this->required = (bool) $required; + + return $this; + } + + public function isIgnored() + { + return $this->ignored; + } + + public function setIgnored($ignored = true) + { + $this->required = (bool) $ignored; + + return $this; + } + + /** + * @return Attribute|string + */ + public function getNameAttribute() + { + return $this->getName(); + } + + /** + * @return mixed + */ + public function getValueAttribute() + { + return $this->getValue(); + } + + /** + * @return null + */ + public function getNoAttribute() + { + return null; + } + + /** + * @return null|Attribute + */ + public function getRequiredAttribute() + { + if ($this->isRequired()) { + return new Attribute('required', true); + } + + return null; + } + + /** + * @return ValidatorInterface[] + */ + public function getValidators() + { + return $this->validators; + } + + /** + * @param array $validators + */ + public function setValidators(array $validators) + { + $this->validators = []; + foreach ($validators as $validator => $options) { + if (! $validator instanceof ValidatorInterface) { + $validator = $this->createValidator($validator, $options); + } + + $this->validators[] = $validator; + } + } + + /** + * @param $name + * @param $options + * @return ValidatorInterface + */ + public function createValidator($name, $options) + { + $class = 'ipl\\Validator\\' . ucfirst($name) . 'Validator'; + if (class_exists($class)) { + return new $class($options); + } else { + throw new InvalidArgumentException( + 'Unable to create Validator: %s', + $name + ); + } + } + + public function hasValue() + { + $value = $this->getValue(); + + return $value !== null && $value !== '' && $value !== []; + } + + /** + * @return bool + */ + public function isValid() + { + if ($this->isValid === null) { + $this->validate(); + } + + return $this->isValid; + } + + /** + * @return $this + */ + public function validate() + { + $isValid = true; + + foreach ($this->getValidators() as $validator) { + if (! $validator->isValid($this->getValue())) { + $isValid = false; + foreach ($validator->getMessages() as $message) { + $this->addMessage($message); + } + } + } + + $this->isValid = $isValid; + + return $this; + } +} diff --git a/library/vendor/ipl/Html/FormElement/FormElementContainer.php b/library/vendor/ipl/Html/FormElement/FormElementContainer.php new file mode 100644 index 00000000..5dc5ae6e --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/FormElementContainer.php @@ -0,0 +1,157 @@ +elements; + } + + /** + * @param $name + * @return BaseFormElement + */ + public function getElement($name) + { + if (! array_key_exists($name, $this->elements)) { + throw new InvalidArgumentException(sprintf( + 'Trying to get inexistant element "%s"', + $name + )); + } + return $this->elements[$name]; + } + + /** + * @param string|BaseFormElement $element + * @return bool + */ + public function hasElement($element) + { + if (is_string($element)) { + return array_key_exists($element, $this->elements); + } elseif ($element instanceof BaseFormElement) { + return in_array($element, $this->elements, true); + } else { + return false; + } + } + + /** + * @param string $name + * @param string|BaseFormElement $type + * @param array|null $options + * @return $this + */ + public function addElement($name, $type = null, $options = null) + { + $this->registerElement($name, $type, $options); + + if ($this instanceof BaseHtmlElement) { + $element = $this->decorate($this->getElement($name)); + } + //... + + $this->add($element); + + return $this; + } + + protected function decorate(BaseFormElement $element) + { + if ($this->hasDefaultElementDecorator()) { + $this->getDefaultElementDecorator()->wrap($element); + } + + return $element; + } + + /** + * @param string $name + * @param string|BaseFormElement $type + * @param array|null $options + * @return $this + */ + public function registerElement($name, $type = null, $options = null) + { + if (is_string($type)) { + $type = $this->createElement($name, $type, $options); + } + + $this->elements[$name] = $type; + + if (method_exists($this, 'onRegisteredElement')) { + $this->onRegisteredElement($name, $type); + } + + return $this; + } + + /** + * TODO: Add PluginLoader + * + * @param $name + * @param $type + * @param $options + * @return BaseFormElement + */ + public function createElement($name, $type, $attributes = null) + { + $class = __NAMESPACE__ . '\\' . ucfirst($type) . 'Element'; + if (class_exists($class)) { + /** @var BaseFormElement $element */ + $element = new $class($name); + if ($attributes !== null) { + $element->addAttributes($attributes); + } + + return $element; + } else { + throw new InvalidArgumentException(sprintf( + 'Unable to create Form Element, no such type: %s', + $type + )); + } + } + + /** + * @param FormElementContainer $form + */ + public function addElementsFrom(FormElementContainer $form) + { + foreach ($form->getElements() as $name => $element) { + $this->addElement($element); + } + } + + public function setDefaultElementDecorator(BaseHtmlElement $decorator) + { + $this->defaultElementDecorator = $decorator; + + return $this; + } + + public function hasDefaultElementDecorator() + { + return $this->defaultElementDecorator !== null; + } + + /** + * @return BaseHtmlElement + */ + public function getDefaultElementDecorator() + { + return $this->defaultElementDecorator; + } +} diff --git a/library/vendor/ipl/Html/FormElement/HiddenElement.php b/library/vendor/ipl/Html/FormElement/HiddenElement.php new file mode 100644 index 00000000..9c440419 --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/HiddenElement.php @@ -0,0 +1,8 @@ +getAttributes()->registerAttributeCallback('type', [$this, 'getTypeAttribute']); + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @return Attribute + */ + public function getTypeAttribute() + { + return new Attribute('type', $this->getType()); + } +} diff --git a/library/vendor/ipl/Html/FormElement/SelectElement.php b/library/vendor/ipl/Html/FormElement/SelectElement.php new file mode 100644 index 00000000..964966c5 --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/SelectElement.php @@ -0,0 +1,46 @@ +getAttributes()->registerAttributeCallback( + 'options', + null, + [$this, 'setOptions'] + ); + } + + /** + * @param array $options + * @return $this + */ + public function setOptions(array $options) + { + foreach ($options as $value => $label) { + $this->options[$value] = new SelectOption($value, $label); + } + + return $this; + } + + protected function assemble() + { + $currentValue = $this->getValue(); + foreach ($this->options as $value => $option) { + if ($value == $currentValue) { + $option->getAttributes()->set('selected', true); + } + + $this->add($option); + } + } +} diff --git a/library/vendor/ipl/Html/FormElement/SelectOption.php b/library/vendor/ipl/Html/FormElement/SelectOption.php new file mode 100644 index 00000000..7ef50548 --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/SelectOption.php @@ -0,0 +1,43 @@ +add($label); + $this->getAttributes()->add('value', $value); + } + + /** + * @param $label + * @return $this + */ + public function setLabel($label) + { + $this->setContent($label); + + return $this; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/library/vendor/ipl/Html/FormElement/SubmitElement.php b/library/vendor/ipl/Html/FormElement/SubmitElement.php new file mode 100644 index 00000000..8ed79926 --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/SubmitElement.php @@ -0,0 +1,47 @@ +buttonLabel = $label; + + return $this; + } + + /** + * @return string + */ + public function getButtonLabel() + { + if ($this->buttonLabel === null) { + return $this->getName(); + } else { + return $this->buttonLabel; + } + } + + /** + * @return mixed|static + */ + public function getValueAttribute() + { + return new Attribute('value', $this->getButtonLabel()); + } + + /** + * @return bool + */ + public function hasBeenPressed() + { + return $this->getButtonLabel() === $this->getValue(); + } +} diff --git a/library/vendor/ipl/Html/FormElement/TextElement.php b/library/vendor/ipl/Html/FormElement/TextElement.php new file mode 100644 index 00000000..560d4c21 --- /dev/null +++ b/library/vendor/ipl/Html/FormElement/TextElement.php @@ -0,0 +1,8 @@ +settings = [ + 'max' => $max + ]; + } + } + + public function isValid($value) + { + if ($value > $this->getSetting('max', PHP_INT_MAX)) { + $this->clearMessages(); + + return true; + } else { + $this->addMessage( + $this->translate('%s is not greater than %d'), + $value, + $this->getSetting('max', PHP_INT_MAX) + ); + + return false; + } + } +} diff --git a/library/vendor/ipl/Validator/MessageContainer.php b/library/vendor/ipl/Validator/MessageContainer.php new file mode 100644 index 00000000..202fd441 --- /dev/null +++ b/library/vendor/ipl/Validator/MessageContainer.php @@ -0,0 +1,38 @@ +messages; + } + + public function addMessage($message) + { + $args = func_get_args(); + array_shift($args); + if (empty($args)) { + $this->messages[] = $message; + } else { + $this->messages[] = vsprintf($message, $args); + } + + return $this; + } + + public function setMessages(array $messages) + { + $this->messages = $messages; + + return $this; + } + + public function clearMessages() + { + return $this->setMessages([]); + } +} diff --git a/library/vendor/ipl/Validator/SimpleValidator.php b/library/vendor/ipl/Validator/SimpleValidator.php new file mode 100644 index 00000000..4ee7efe4 --- /dev/null +++ b/library/vendor/ipl/Validator/SimpleValidator.php @@ -0,0 +1,24 @@ +settings = $settings; + } + + public function getSetting($name, $default = null) + { + if (array_key_exists($name, $this->settings)) { + return $this->settings[$name]; + } else { + return $default; + } + } +} diff --git a/library/vendor/ipl/Validator/ValidatorInterface.php b/library/vendor/ipl/Validator/ValidatorInterface.php new file mode 100644 index 00000000..8ac33f29 --- /dev/null +++ b/library/vendor/ipl/Validator/ValidatorInterface.php @@ -0,0 +1,18 @@ +