Commit a8085f6d authored by Carsten  Rose's avatar Carsten Rose
Browse files

Refactor: own class Checkobox

parent 913b7f3a
Pipeline #2869 failed with stages
in 1 minute and 51 seconds
...@@ -1328,7 +1328,7 @@ const FE_ORDER_INTERVAL_DEFAULT = '10'; ...@@ -1328,7 +1328,7 @@ const FE_ORDER_INTERVAL_DEFAULT = '10';
const FE_ORDER_COLUMN = 'orderColumn'; const FE_ORDER_COLUMN = 'orderColumn';
const FE_DND_TABLE = 'dndTable'; const FE_DND_TABLE = 'dndTable';
CONST FE_TMP_CLASS_OPTION = '_classOption'; const FE_TMP_CLASS_OPTION = '_classOption';
const MODE_ENCODE = 'encode'; const MODE_ENCODE = 'encode';
const MODE_DECODE = 'decode'; const MODE_DECODE = 'decode';
......
<?php
/**
* Created by PhpStorm.
* User: crose
* Date: 12/5/19
* Time: 11:09 AM
*/
namespace IMATHUZH\Qfq\Core\Form;
//use IMATHUZH\Qfq\Core\Database\Database;
//use IMATHUZH\Qfq\Core\Helper\HelperFile;
//use IMATHUZH\Qfq\Core\Helper\HelperFormElement;
//use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
//use IMATHUZH\Qfq\Core\Helper\Ldap;
//use IMATHUZH\Qfq\Core\Helper\Logger;
//use IMATHUZH\Qfq\Core\Helper\OnArray;
//use IMATHUZH\Qfq\Core\Helper\Sanitize;
use IMATHUZH\Qfq\Core\Helper\Support;
//use IMATHUZH\Qfq\Core\Report\Link;
//use IMATHUZH\Qfq\Core\Report\Report;
//use IMATHUZH\Qfq\Core\Store\Sip;
use IMATHUZH\Qfq\Core\Helper\HelperFormElement;
use IMATHUZH\Qfq\Core\Store\Store;
/**
* Class Checkbox
* @package IMATHUZH\Qfq\Core\Form
*/
class Checkbox {
/**
* @var Store
*/
private $store = null;
/**
* Builds HTML 'checkbox' element.
*
* Checkboxes will only be submitted, if they are checked. Therefore, a hidden element with the unchecked value
* will be transferred first.
*
* Format: <input type="hidden" name="$htmlFormElementName" value="$valueUnChecked">
* <input name="$htmlFormElementName" type="checkbox" [autofocus="autofocus"]
* [required="required"] [disabled="disabled"] value="<value>" [checked="checked"] >
*
* @param array $formElement
* @param string $htmlFormElementName
* @param string $value
* @param array $json
* @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE*
*
* @return string
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function build(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
$itemKey = array();
$itemValue = array();
// Fill $itemKey & $itemValue
HelperFormElement::getKeyValueListFromSqlEnumSpec($formElement, $itemKey, $itemValue);
// Get fallback, if 'checkBoxMode' is not defined:
if (!isset($formElement['checkBoxMode'])) {
// This fallback is problematic if 'set' or 'enum' has 2 : defaults to single but maybe multi is meant.
$formElement['checkBoxMode'] = (count($itemKey) > 2) ? 'multi' : 'single';
}
if ($formElement['checkBoxMode'] === 'multi') {
} else {
// Fill meaningful defaults to parameter: checked|unchecked (CHECKBOX_VALUE_CHECKED|CHECKBOX_VALUE_UNCHECKED)
$this->prepareCheckboxCheckedUncheckedValue($itemKey, $formElement);
}
$attributeBase = HelperFormElement::getAttributeFeMode($formElement[FE_MODE]);
$attributeBase .= Support::doAttribute('type', $formElement[FE_TYPE]);
$attributeBase .= HelperFormElement::getAttributeList($formElement, [F_FE_DATA_PATTERN_ERROR, F_FE_DATA_REQUIRED_ERROR, F_FE_DATA_MATCH_ERROR, F_FE_DATA_ERROR]);
switch ($formElement['checkBoxMode']) {
case 'single':
$html = $this->buildCheckboxSingle($formElement, $htmlFormElementName, $attributeBase, $value, $json);
break;
case 'multi';
$html = $this->buildCheckboxMulti($formElement, $htmlFormElementName, $attributeBase, $value, $itemKey, $itemValue, $json);
break;
default:
throw new \UserFormException('checkBoxMode: \'' . $formElement['checkBoxMode'] . '\' is unknown.', ERROR_CHECKBOXMODE_UNKNOWN);
}
$formElement = HelperFormElement::prepareExtraButton($formElement, false);
return $html . $formElement[FE_TMP_EXTRA_BUTTON_HTML] . $this->getHelpBlock() . $formElement[FE_INPUT_EXTRA_BUTTON_INFO];
}
/**
* For CheckBox's with only one checkbox: if no parameter:checked|unchecked is defined, take defaults:
*
* checked: first Element in $itemKey
* unchecked: ''
*
* @param array $itemKey
* @param array $formElement
*
* @throws \CodeException
* @throws \UserFormException
*/
private function prepareCheckboxCheckedUncheckedValue(array $itemKey, array &$formElement) {
if (!isset($formElement[FE_CHECKBOX_CHECKED])) {
if (isset($itemKey[0])) {
// First element in $itemKey list
$formElement[FE_CHECKBOX_CHECKED] = $itemKey[0];
} else {
// Take column default value
$formElement[FE_CHECKBOX_CHECKED] = $this->store->getVar($formElement[FE_NAME], STORE_TABLE_DEFAULT);
}
}
// unchecked
if (!isset($formElement[FE_CHECKBOX_UNCHECKED])) {
if (isset($itemKey[1])) {
$formElement[FE_CHECKBOX_UNCHECKED] = ($itemKey[0] === $formElement[FE_CHECKBOX_CHECKED]) ? $itemKey[1] : $itemKey[0];
} else {
$formElement[FE_CHECKBOX_UNCHECKED] = '';
}
}
if ($formElement[FE_CHECKBOX_CHECKED] === $formElement[FE_CHECKBOX_UNCHECKED]) {
throw new \UserFormException('FormElement: type=checkbox - checked and unchecked can\'t be the same: ' . $formElement[FE_CHECKBOX_CHECKED], ERROR_CHECKBOX_EQUAL);
}
}
}
\ No newline at end of file
...@@ -17,6 +17,11 @@ use IMATHUZH\Qfq\Core\Store\Store; ...@@ -17,6 +17,11 @@ use IMATHUZH\Qfq\Core\Store\Store;
*/ */
class HelperFormElement { class HelperFormElement {
/**
* @var Store
*/
protected $store = null;
/** /**
* Expand column $keyName to row array as virtual columns. * Expand column $keyName to row array as virtual columns.
* E.g.: [ 'id' => '1', 'name' => 'John', 'parameter' => 'detail=xId:grId\nShowEmptyAtStart=1' ] becomes: * E.g.: [ 'id' => '1', 'name' => 'John', 'parameter' => 'detail=xId:grId\nShowEmptyAtStart=1' ] becomes:
...@@ -88,9 +93,9 @@ class HelperFormElement { ...@@ -88,9 +93,9 @@ class HelperFormElement {
$checkKeys = array_keys($arr); $checkKeys = array_keys($arr);
foreach ($checkKeys AS $checkKey) { foreach ($checkKeys AS $checkKey) {
if (!empty($element[$checkKey])) { if (!empty($element[$checkKey])) {
$store = Store::getInstance(); self::$store = Store::getInstance();
$store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($element), STORE_SYSTEM); self::$store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($element), STORE_SYSTEM);
$store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, $keyName, STORE_SYSTEM); self::$store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, $keyName, STORE_SYSTEM);
throw new \UserFormException("Found reserved keyname '$checkKey'", ERROR_RESERVED_KEY_NAME); throw new \UserFormException("Found reserved keyname '$checkKey'", ERROR_RESERVED_KEY_NAME);
} }
} }
...@@ -361,12 +366,12 @@ class HelperFormElement { ...@@ -361,12 +366,12 @@ class HelperFormElement {
*/ */
public static function prepareExtraButton(array $formElement, $showInline) { public static function prepareExtraButton(array $formElement, $showInline) {
$store = Store::getInstance(); self::$store = Store::getInstance();
$infoSymbolInside = $store->getVar(SYSTEM_EXTRA_BUTTON_INFO_INLINE, STORE_SYSTEM); $infoSymbolInside = self::$store->getVar(SYSTEM_EXTRA_BUTTON_INFO_INLINE, STORE_SYSTEM);
$infoSymbolOutside = $store->getVar(SYSTEM_EXTRA_BUTTON_INFO_BELOW, STORE_SYSTEM); $infoSymbolOutside = self::$store->getVar(SYSTEM_EXTRA_BUTTON_INFO_BELOW, STORE_SYSTEM);
if (SYSTEM_EXTRA_BUTTON_INFO_POSITION_BELOW == $store->getVar(SYSTEM_EXTRA_BUTTON_INFO_POSITION, STORE_SYSTEM)) { if (SYSTEM_EXTRA_BUTTON_INFO_POSITION_BELOW == self::$store->getVar(SYSTEM_EXTRA_BUTTON_INFO_POSITION, STORE_SYSTEM)) {
$showInline = false; $showInline = false;
} }
...@@ -560,4 +565,229 @@ EOF; ...@@ -560,4 +565,229 @@ EOF;
return $data == '' || $data == '1'; return $data == '' || $data == '1';
} }
/**
* Look for key/value list (in this order, first match counts) in
* a) `sql1`
* b) `parameter:itemList`
* c) table.column definition
*
* Copies the found keys to &$itemKey and the values to &$itemValue
* If there are no &$itemKey, copy &$itemValue to &$itemKey.
*
* @param array $formElement
* @param array $itemKey
* @param array $itemValue
*
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public static function getKeyValueListFromSqlEnumSpec(array $formElement, array &$itemKey, array &$itemValue) {
$fieldType = '';
$itemKey = array();
$itemValue = array();
self::$store = Store::getInstance();
// Call getItemsForEnumOrSet() only if there a corresponding column really exist.
if (false !== self::$store->getVar($formElement[FE_NAME], STORE_TABLE_COLUMN_TYPES)) {
$itemValue = self::getItemsForEnumOrSet($formElement[FE_NAME], $fieldType);
}
if (is_array($formElement[FE_SQL1])) {
if (count($formElement[FE_SQL1]) > 0) {
$keys = array_keys($formElement[FE_SQL1][0]);
$itemKey = array_column($formElement[FE_SQL1], 'id');
// If there is no column 'id' and at least two columns in total
if (count($itemKey) === 0 && count($keys) >= 2) {
$itemKey = array_column($formElement[FE_SQL1], $keys[0]);
}
$itemValue = array_column($formElement[FE_SQL1], 'label');
// If there is no column 'label' (e.g.: SHOW tables)
if (count($itemValue) === 0) {
$idx = count($keys) == 1 ? 0 : 1;
$itemValue = array_column($formElement[FE_SQL1], $keys[$idx]);
}
}
} elseif (isset($formElement['itemList']) && strlen($formElement['itemList']) > 0) {
$arr = KeyValueStringParser::parse($formElement['itemList'], ':', ',', KVP_IF_VALUE_EMPTY_COPY_KEY);
$itemValue = array_values($arr);
$itemKey = array_keys($arr);
} elseif ($fieldType === 'enum' || $fieldType === 'set') {
// already done at the beginning with '$this->getItemsForEnumOrSet($formElement[FE_NAME], $fieldType);'
} elseif (isset($formElement[FE_CHECKBOX_CHECKED]) && $formElement[FE_TYPE] == FE_TYPE_CHECKBOX) {
// Nothing to do here.
} else {
throw new \UserFormException("Missing definition (- nothing found in 'sql1', 'parameter:itemList', 'enum-' or 'set-definition'", ERROR_MISSING_ITEM_LIST);
}
if (count($itemKey) === 0) {
$itemKey = $itemValue;
}
// Process 'emptyHide' before 'emptyItemAtStart' / 'emptyItemAtEnd': than 'emptyItem*' are still possible.
if (isset($formElement['emptyHide'])) {
$itemKey = OnArray::removeEmptyElementsFromArray($itemKey);
$itemValue = OnArray::removeEmptyElementsFromArray($itemValue);
}
if (isset($formElement[FE_EMPTY_ITEM_AT_START])) {
$placeholder = isset($formElement[FE_PLACEHOLDER]) ? $formElement[FE_PLACEHOLDER] : '';
array_unshift($itemKey, '');
array_unshift($itemValue, $placeholder);
}
if (isset($formElement[FE_EMPTY_ITEM_AT_END])) {
$itemKey[] = '';
$itemValue[] = '';
}
}
/**
* Get the attribute definition list of an enum or set column. For strings, get the default value.
* Return elements as an array.
*
* @param string $column
* @param string $fieldType
*
* @return array
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
private static function getItemsForEnumOrSet($column, &$fieldType) {
self::$store = Store::getInstance();
// Get column definition
$fieldTypeDefinition = self::$store->getVar($column, STORE_TABLE_COLUMN_TYPES);
if ($fieldTypeDefinition === false) {
throw new \UserFormException("Column '$column' in primary table.", ERROR_DB_UNKNOWN_COLUMN);
}
$length = strlen($fieldTypeDefinition);
// enum('... set('
switch (substr($fieldTypeDefinition, 0, 4)) {
case 'enum':
$startPosition = 5;
break;
case 'set(':
$startPosition = 4;
break;
default:
$fieldType = 'string';
return array();
}
// enum('a','b','c', ...) >> [ 'a', 'b', 'c', ... ]
// set('a','b','c', ...) >> [ 'a', 'b', 'c', ... ]
$values = substr($fieldTypeDefinition, $startPosition, $length - $startPosition - 1);
$items = OnArray::trimArray(explode(',', $values), "'");
$fieldType = substr($fieldTypeDefinition, 0, $startPosition - 1);
return $items;
}
/**
* Set corresponding html attributes readonly/required/disabled, based on $formElement[FE_MODE].
*
* @param string $feMode
*
* @param bool $cssDisable
* @return string
* @throws \CodeException
* @throws \UserFormException
*/
public static function getAttributeFeMode($feMode, $cssDisable = true) {
$attribute = '';
self::getFeMode($feMode, $hidden, $disabled, $required);
switch ($feMode) {
case FE_MODE_HIDDEN:
case FE_MODE_SHOW:
case FE_MODE_SHOW_REQUIRED:
break;
case FE_MODE_REQUIRED:
case FE_MODE_READONLY:
$attribute .= Support::doAttribute($feMode, $feMode);
break;
default:
throw new \UserFormException("Unknown mode '$feMode'", ERROR_UNKNOWN_MODE);
break;
}
// Attributes: data-...
$attribute .= Support::doAttribute(DATA_HIDDEN, $hidden);
if ($cssDisable) {
$attribute .= Support::doAttribute(DATA_DISABLED, $disabled);
}
$attribute .= Support::doAttribute(DATA_REQUIRED, $required);
return $attribute;
}
/**
* Depending of $feMode set variables $hidden, $disabled, $required to 'yes' or 'no'.
*
* @param string $feMode
* @param string $hidden
* @param string $disabled
* @param string $required
*
* @throws \UserFormException
*/
public static function getFeMode($feMode, &$hidden, &$disabled, &$required) {
$hidden = 'no';
$disabled = 'no';
$required = 'no';
switch ($feMode) {
case FE_MODE_SHOW:
case FE_MODE_SHOW_REQUIRED:
break;
case FE_MODE_REQUIRED:
$required = 'yes';
break;
case FE_MODE_READONLY:
$disabled = 'yes'; // convert the UI status 'readonly' to the HTML/CSS status 'disabled'.
break;
case FE_MODE_HIDDEN:
$hidden = 'yes';
break;
default:
throw new \UserFormException("Unknown mode '$feMode'", ERROR_UNKNOWN_MODE);
break;
}
}
/**
* Builds a HTML attribute list, based on $attributeList.
*
* E.g.: attributeList: [ 'type', 'autofocus' ]
* generates: 'type="$formElement[FE_TYPE]" autofocus="$formElement[FE_AUTOFOCUS]" '
*
* @param array $formElement
* @param array $attributeList
* @param bool $flagOmitEmpty
*
* @return string
* @throws \CodeException
*/
public static function getAttributeList(array $formElement, array $attributeList, $flagOmitEmpty = true) {
$attribute = '';
foreach ($attributeList as $item) {
if (isset($formElement[$item]))
$attribute .= Support::doAttribute(strtolower($item), $formElement[$item], $flagOmitEmpty);
}
return $attribute;
}
} }
\ No newline at end of file
...@@ -395,135 +395,6 @@ class BuildFormPlainTest extends AbstractDatabaseTest { ...@@ -395,135 +395,6 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
'" data-error="' . $formElement[F_FE_DATA_ERROR] . '" data-hidden="no" data-required="no" class="form-control" ><div class="help-block with-errors hidden"></div>', $result); '" data-error="' . $formElement[F_FE_DATA_ERROR] . '" data-hidden="no" data-required="no" class="form-control" ><div class="help-block with-errors hidden"></div>', $result);
} }
/**
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function testGetKeyValueListFromSqlEnumSpec() {
$form = array();
$formElement = array();
$this->templateFormNFormElement($form, $formElement);
$formElement['name'] = 'deleted';
$build = new BuildFormPlain($form, array(), [$formElement], $this->dbArray);
$keys = array();
$values = array();
// Spec Enum
$expect = ['yes', 'no'];
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expect, $keys);
$this->assertEquals($expect, $values);
// Spec Enum + emptyItemAtStart
$expect = ['', 'yes', 'no'];
$formElement[FE_EMPTY_ITEM_AT_START] = '';
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expect, $keys);
$this->assertEquals($expect, $values);
// Spec Enum + emptyItemAtEnd
$expect = ['', 'yes', 'no', ''];
$formElement[FE_EMPTY_ITEM_AT_END] = '';
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expect, $keys);
$this->assertEquals($expect, $values);
// clean
unset($formElement[FE_EMPTY_ITEM_AT_START]);
unset($formElement[FE_EMPTY_ITEM_AT_END]);
// listItem: only value
$expect = ['a', 'b', 'c'];
$formElement['itemList'] = 'a,b,c';
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expect, $keys);
$this->assertEquals($expect, $values);
// listItem: key/value
$expectKeys = ['A', 'B', 'C'];
$expectValues = ['a', 'b', 'c'];
$formElement['itemList'] = 'A:a,B:b,C:c';
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expectKeys, $keys);
$this->assertEquals($expectValues, $values);
// listItem: key/value + emptyItemAtEnd
$formElement['emptyItemAtEnd'] = '';
$expectKeys = ['A', 'B', 'C', ''];
$expectValues = ['a', 'b', 'c', ''];
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expectKeys, $keys);
$this->assertEquals($expectValues, $values);
unset($formElement['emptyItemAtEnd']);
// SQL: any content is fine, but should always be the same to do checks
$formElement['sql1'] = $this->dbArray[DB_INDEX_DEFAULT]->sql('SELECT name FROM Form AS f ORDER BY f.id LIMIT 3');
// $expect = ['form', 'formElement', 'phpunit_person'];
$expect = ['form', 'formElement', 'copyForm'];
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expect, $keys);
$this->assertEquals($expect, $values);
// SQL (one column, no keyword) + emptyItemAtStart
$formElement[FE_EMPTY_ITEM_AT_START] = '';
$formElement['sql1'] = $this->dbArray[DB_INDEX_DEFAULT]->sql('SELECT name FROM Form AS f ORDER BY f.id LIMIT 3');
// $expect = ['', 'form', 'formElement', 'phpunit_person'];
$expect = ['', 'form', 'formElement', 'copyForm'];
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expect, $keys);
$this->assertEquals($expect, $values);
unset($formElement[FE_EMPTY_ITEM_AT_START]);
// SQL (4 columns, none 'id' nor 'label') - Take the first two columns
$expectKeys = ['100', '200', '300'];
$expectValues = ['basic', 'formelement', 'layout'];
$formElement['sql1'] = $this->dbArray[DB_INDEX_DEFAULT]->sql('SELECT ord, name, created, modified FROM FormElement AS fe ORDER BY fe.id LIMIT 3');
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expectKeys, $keys);
$this->assertEquals($expectValues, $values);
// SQL (4 columns, none 'id', one 'label' ) - Take the first and the fourth two columns
$expectKeys = ['100', '200', '300'];
$expectValues = ['basic', 'formelement', 'layout'];
$formElement['sql1'] = $this->dbArray[DB_INDEX_DEFAULT]->sql('SELECT ord, created, modified, name AS label FROM FormElement AS fe ORDER BY fe.id LIMIT 3');
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expectKeys, $keys);
$this->assertEquals($expectValues, $values);
// SQL (4 columns, none 'id', one 'label' ) - Take the first and the fourth two columns
$expectKeys = ['1', '2', '3'];
$expectValues = ['basic', 'formelement', 'layout'];
$formElement['sql1'] = $this->dbArray[DB_INDEX_DEFAULT]->sql('SELECT ord, created, modified, name AS label, id FROM FormElement AS fe ORDER BY fe.id LIMIT 3');
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
$this->assertEquals($expectKeys, $keys);
$this->assertEquals($expectValues, $values);
}
/**
* @expectedException \UserFormException
*
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function testGetKeyValueListFromSqlEnumSpecException() {
$form = array();
$formElement = array();
$this->templateFormNFormElement($form, $formElement);
$build = new BuildFormPlain($form, array(), [$formElement], $this->dbArray);
$formElement['name'] = 'noteInternal';
$keys = array();
$values = array();
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
}
/** /**
* @throws \CodeException * @throws \CodeException
......
...@@ -217,6 +217,137 @@ class HelperFormElementTest extends TestCase { ...@@ -217,6 +217,137 @@ class HelperFormElementTest extends TestCase {
$this->assertEquals('{"red":18,"green":52,"blue":86}', $result, "Expect white"); $this->assertEquals('{"red":18,"green":52,"blue":86}', $result, "Expect white");
} }
//Todo Tests wieder aktivieren
// /**