modules-forms-torunn/module.php

256 lines
7.2 KiB
PHP
Raw Normal View History

2020-03-18 12:29:36 +01:00
<?php
use Lewp\{Module, Resolve, VarFolder};
use Lewp\Forms\Torunn;
2020-03-18 12:29:36 +01:00
return new class extends Module
2020-03-18 12:29:36 +01:00
{
const OPTIONS_ELEMENTS = 'elements';
const OPTIONS_ELEMENT = 'element';
const OPTIONS_LABEL = 'label';
const OPTIONS_DATA = 'data';
const OPTIONS_CALLBACK_ON_SUBMIT = 'on_submit';
const OPTIONS_SUBFORM = 'is_subform';
2020-03-18 12:29:36 +01:00
// element specific
const OPTIONS_SHOW_PLACEHOLDER = 'show_placeholder';
const OPTIONS_TYPE = 'type';
const OPTIONS_CONTENT = 'content';
// form generics
const OPTIONS_METHOD = 'method';
const OPTIONS_ACTION = 'action';
private $non_attribute_options = [
self::OPTIONS_ELEMENTS => false,
self::OPTIONS_ELEMENT => false,
self::OPTIONS_CONTENT => false,
self::OPTIONS_LABEL => false,
2020-03-18 12:29:36 +01:00
];
private $element_id = 0;
private function canContainData(array $element)
{
$non_data_elements = [
[
self::OPTIONS_ELEMENT => "button"
],
[
self::OPTIONS_ELEMENT => "fieldset"
],
[
self::OPTIONS_ELEMENT => "legend"
],
2020-03-18 12:29:36 +01:00
[
self::OPTIONS_ELEMENT => "input",
self::OPTIONS_TYPE => "submit",
],
];
foreach ($non_data_elements as $non) {
if (count(array_intersect($non, $element)) == count($non)) {
return false;
}
}
return true;
}
private function generateFormElementName(bool $increase_element_id): string
{
return implode(
'-',
[
$this->getModuleName(),
$this->getExecutionCount(),
($increase_element_id) ? $this->element_id++ : $this->element_id,
]
);
}
private function createFormElementGroup(array $element)
{
if (!isset($element[self::OPTIONS_LABEL])) {
return $this->createFormElement($element);
} else {
$label = $this->createLabel($element);
}
$wrapper = $this->createAndSetupElement(
'div',
'',
[
'class' => 'element-container'
]
);
$wrapper->appendChild($label);
$form_element = $this->createFormElement($element);
$wrapper->appendChild($form_element);
return $wrapper;
}
2020-03-18 12:29:36 +01:00
private function createFormElement(array $element)
{
$element += [self::OPTIONS_CONTENT => ''];
if ($this->canContainData($element)) {
// if no label given, the element id should be increased
$element["name"] = $this->generateFormElementName(
true//!array_key_exists($element[self::OPTIONS_LABEL])
);
$element["id"] = $element["name"];
2020-03-18 12:29:36 +01:00
} else {
unset($element["name"]);
}
if (is_array($element[self::OPTIONS_CONTENT])) {
$content_element = $element[self::OPTIONS_CONTENT];
$content = $this->createAndSetupElement(
$content_element[self::OPTIONS_ELEMENT],
$content_element[self::OPTIONS_CONTENT] ?? '',
array_diff_key($content_element, $this->non_attribute_options)
);
$form_element = $this->createAndSetupElement(
$element[self::OPTIONS_ELEMENT],
'',
array_diff_key($element, $this->non_attribute_options)
);
$form_element->appendChild($content);
} else {
$form_element = $this->createAndSetupElement(
$element[self::OPTIONS_ELEMENT],
$element[self::OPTIONS_CONTENT],
array_diff_key($element, $this->non_attribute_options)
);
}
2020-03-18 12:29:36 +01:00
foreach ($element[self::OPTIONS_ELEMENTS] as $element) {
$form_element->appendChild($this->createFormElementGroup($element));
2020-03-18 12:29:36 +01:00
}
return $form_element;
}
private function createLabel(array $element)
{
$label = $this->createAndSetupElement(
'label',
$element[self::OPTIONS_LABEL],
[
'for' => $this->generateFormElementName(false),
]
);
return $label;
}
private function setupForm(\DOMNode $form, array $elements)
2020-03-18 12:29:36 +01:00
{
foreach ($elements as $element) {
$form->appendChild($this->createFormElementGroup($element));
2020-03-18 12:29:36 +01:00
}
}
private function collectFormKeys()
{
2020-03-18 12:29:36 +01:00
return preg_grep(
'/' .
2020-03-18 12:29:36 +01:00
$this->getModuleName()
. '-'
. $this->getExecutionCount()
. '-\d+'
. '/',
2020-03-18 12:29:36 +01:00
array_keys($_POST)
);
}
private function isSubmitted()
{
return count($this->collectFormKeys()) > 0;
}
private function extractData()
2020-03-18 12:29:36 +01:00
{
if (!$this->isSubmitted()) {
return [];
2020-03-18 12:29:36 +01:00
}
$extracted_data = [];
2020-03-18 12:29:36 +01:00
$keys = $this->collectFormKeys();
sort($keys, SORT_NUMERIC);
foreach ($keys as $key) {
$extracted_data [] = $_POST[$key];
2020-03-18 12:29:36 +01:00
}
return $extracted_data;
2020-03-18 12:29:36 +01:00
}
private function processAction($action)
{
if ($action === '/') {
return '/' . $this->getLanguage();
2020-03-18 12:29:36 +01:00
}
if (strpos($action, '/') === 0) {
return '/' . $this->getLanguage() . $action;
2020-03-18 12:29:36 +01:00
}
return implode('/', [
'',
$this->getLanguage(),
($action ?? Resolve::idToUri($this->getPageId())),
]);
}
public function run(array $options = []) : bool
{
// add default options
$options += [
self::OPTIONS_DATA => [],
self::OPTIONS_SUBFORM => false,
self::OPTIONS_CALLBACK_ON_SUBMIT => function ($data) {
return true;
},
];
// extract data that has been submitted
$submitted_data = $this->extractData();
if (// there is data that has been submitted, and callback returns false, stop module
!empty($submitted_data)
&& !$options[self::OPTIONS_CALLBACK_ON_SUBMIT]($submitted_data)
) {
return false;
} else {
$options[self::OPTIONS_DATA] = $submitted_data;
}
// setup form attributes if required
if ($options[self::OPTIONS_SUBFORM]) {
$form = $this;
} else {
$form = $this->createAndSetupElement(
'form',
'',
[
'method' => $options[self::OPTIONS_METHOD] ?? 'POST',
'action' => $this->processAction($options[self::OPTIONS_ACTION]) ?? '/',
]
);
$this->appendChild($form);
}
2020-03-18 12:29:36 +01:00
$this->setupForm($form, $options[self::OPTIONS_ELEMENTS]);
// if data is present, filter the correct keys and fill the form
if (!empty($options[self::OPTIONS_DATA])) {
Torunn::fill($form, $options[self::OPTIONS_DATA]);
}
2020-03-18 12:29:36 +01:00
if ($this->isSubmitted()) {
return false;
}
return true;
}
public function onWebsocketRequest(
\Lewp\Websocket\Message $message
) : \Lewp\Websocket\Message {
}
};