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

Merge branch 'B9720CheckboxVariousSetups' into 'master'

B9720 checkbox various setups

See merge request !221
parents 2baf2b77 dabd2b82
Pipeline #2985 passed with stages
in 3 minutes and 39 seconds
...@@ -53,6 +53,7 @@ composer.lock ...@@ -53,6 +53,7 @@ composer.lock
/extension/Resources/Public/Css /extension/Resources/Public/Css
/extension/Resources/Public/fonts /extension/Resources/Public/fonts
/extension/Resources/Public/JavaScript /extension/Resources/Public/JavaScript
/extension/Tests/selenium/tmp
/fonts /fonts
/js /js
/node_modules /node_modules
...@@ -63,3 +64,7 @@ composer.lock ...@@ -63,3 +64,7 @@ composer.lock
/javascript/src/.vscode /javascript/src/.vscode
/javascript/src/npm-debug.log /javascript/src/npm-debug.log
/docker/chromedriver
/docker/geckodriver
/docker/run_qfq_docker.output
...@@ -65,7 +65,12 @@ selenium: ...@@ -65,7 +65,12 @@ selenium:
- mkdir "$SELENIUM_LOGS_PATH/$CI_COMMIT_SHORT_SHA" - mkdir "$SELENIUM_LOGS_PATH/$CI_COMMIT_SHORT_SHA"
- cp extension/Tests/selenium/selenium_logs/* "$SELENIUM_LOGS_PATH/$CI_COMMIT_SHORT_SHA/" - cp extension/Tests/selenium/selenium_logs/* "$SELENIUM_LOGS_PATH/$CI_COMMIT_SHORT_SHA/"
- echo "Selenium Logs copied to $SELENIUM_LOGS_PATH/$CI_COMMIT_SHORT_SHA/" - echo "Selenium Logs copied to $SELENIUM_LOGS_PATH/$CI_COMMIT_SHORT_SHA/"
- echo "Or download result (log/screenshot) in gitlab under CI/CD > Pipelines <job> > right side 'Artifacts'"
artifacts:
expire_in: 1 week
paths:
- extension/Tests/selenium/selenium_logs/
**To run/update SELENIUM test, please check docker/README.md.**
Create new Tests
================
* Set up new forms and reports.
* Dump new database.
* Copy dump to become now initial database
* Update SELENIUM Python tests
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.. ;; .. ;;
.. ,, .. ,,
.. ..
.. -------------------------------------------------- .. --------------------------------------------used to the update the records specified ------
.. Best Practice T3 reST: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/CheatSheet.html .. Best Practice T3 reST: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/CheatSheet.html
.. Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html .. Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html
.. Italic *italic* .. Italic *italic*
...@@ -7618,7 +7618,7 @@ A dedicated `Form`, without any `FormElements`, is used to define the reorder lo ...@@ -7618,7 +7618,7 @@ A dedicated `Form`, without any `FormElements`, is used to define the reorder lo
Fields: Fields:
* Name: <custom form name> - used in Part 1 in the `_data-dnd-api` variable. * Name: <custom form name> - used in Part 1 in the `_data-dnd-api` variable.
* Table: <table with the element records> - used to the update the records specified by `dragAndDropOrderSql`. * Table: <table with the element records> - used to update the records specified by `dragAndDropOrderSql`.
* Parameter: * Parameter:
......
...@@ -440,6 +440,16 @@ module.exports = function (grunt) { ...@@ -440,6 +440,16 @@ module.exports = function (grunt) {
expand: true, expand: true,
flatten: true flatten: true
}, },
{
cwd: 'node_modules/font-awesome/css/',
src: [
'font-awesome.min.css'
],
dest: 'css/',
filter: 'isFile',
expand: true,
flatten: true
},
{ {
cwd: 'node_modules/font-awesome/fonts/', cwd: 'node_modules/font-awesome/fonts/',
expand: true, expand: true,
...@@ -448,6 +458,15 @@ module.exports = function (grunt) { ...@@ -448,6 +458,15 @@ module.exports = function (grunt) {
], ],
dest: typo3_fonts, dest: typo3_fonts,
flatten: true flatten: true
},
{
cwd: 'node_modules/font-awesome/fonts/',
expand: true,
src: [
'*'
],
dest: 'fonts/',
flatten: true
} }
] ]
}, },
......
This diff is collapsed.
This diff is collapsed.
...@@ -772,6 +772,11 @@ EOF; ...@@ -772,6 +772,11 @@ EOF;
if ($wrapName == FE_WRAP_LABEL) { if ($wrapName == FE_WRAP_LABEL) {
$wrapArray[0] = Support::insertAttribute($wrapArray[0], 'style', 'text-align: ' . $formElement[F_FE_LABEL_ALIGN] . ';'); // might be problematic, if there is already a 'class' defined. $wrapArray[0] = Support::insertAttribute($wrapArray[0], 'style', 'text-align: ' . $formElement[F_FE_LABEL_ALIGN] . ';'); // might be problematic, if there is already a 'class' defined.
} }
// Insert 'required="required"' for checkboxes and radios
if ($wrapName == FE_WRAP_INPUT && $formElement[FE_MODE] == FE_MODE_REQUIRED &&
($formElement[FE_TYPE] == FE_TYPE_CHECKBOX || $formElement[FE_TYPE] == FE_TYPE_RADIO)) {
$wrapArray[0] = Support::insertAttribute($wrapArray[0], 'required', 'required'); // might be problematic, if there is already a 'class' defined.
}
} }
return $wrapArray[0] . $htmlElement . $wrapArray[1]; return $wrapArray[0] . $htmlElement . $wrapArray[1];
...@@ -807,7 +812,7 @@ EOF; ...@@ -807,7 +812,7 @@ EOF;
// Label // Label
if ($formElement[FE_BS_LABEL_COLUMNS] != '0') { if ($formElement[FE_BS_LABEL_COLUMNS] != '0') {
$htmlLabel = $this->buildLabel($htmlFormElementName, $formElement[FE_LABEL], $addClassRequired[FE_LABEL] ?? ''); $htmlLabel = HelperFormElement::buildLabel($htmlFormElementName, $formElement[FE_LABEL], $addClassRequired[FE_LABEL] ?? '');
} }
$html .= $this->customWrap($formElement, $htmlLabel, FE_WRAP_LABEL, $formElement[FE_BS_LABEL_COLUMNS], $html .= $this->customWrap($formElement, $htmlLabel, FE_WRAP_LABEL, $formElement[FE_BS_LABEL_COLUMNS],
......
...@@ -1252,6 +1252,7 @@ const FE_INPUT_AUTOCOMPLETE = 'autocomplete'; ...@@ -1252,6 +1252,7 @@ const FE_INPUT_AUTOCOMPLETE = 'autocomplete';
const FE_TMP_EXTRA_BUTTON_HTML = '_extraButtonHtml'; // will be filled on the fly during building extrabutton const FE_TMP_EXTRA_BUTTON_HTML = '_extraButtonHtml'; // will be filled on the fly during building extrabutton
const FE_CHECKBOX_CHECKED = 'checked'; const FE_CHECKBOX_CHECKED = 'checked';
const FE_CHECKBOX_UNCHECKED = 'unchecked'; const FE_CHECKBOX_UNCHECKED = 'unchecked';
const FE_ITEM_LIST = 'itemList';
const FE_RECORD_DESTINATION_TABLE = 'recordDestinationTable'; const FE_RECORD_DESTINATION_TABLE = 'recordDestinationTable';
const FE_RECORD_SOURCE_TABLE = 'recordSourceTable'; const FE_RECORD_SOURCE_TABLE = 'recordSourceTable';
const FE_TRANSLATE_ID_COLUMN = 'translateIdColumn'; const FE_TRANSLATE_ID_COLUMN = 'translateIdColumn';
...@@ -1328,6 +1329,8 @@ const FE_ORDER_INTERVAL_DEFAULT = '10'; ...@@ -1328,6 +1329,8 @@ 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 MODE_ENCODE = 'encode'; const MODE_ENCODE = 'encode';
const MODE_DECODE = 'decode'; const MODE_DECODE = 'decode';
const MODE_NONE = 'none'; const MODE_NONE = 'none';
...@@ -1812,6 +1815,7 @@ const DIRTY_API_ACTION_EXTEND = 'extend'; ...@@ -1812,6 +1815,7 @@ const DIRTY_API_ACTION_EXTEND = 'extend';
const LOCK_NOT_FOUND = 0; const LOCK_NOT_FOUND = 0;
const LOCK_FOUND_OWNER = 1; const LOCK_FOUND_OWNER = 1;
const LOCK_FOUND_CONFLICT = 2; const LOCK_FOUND_CONFLICT = 2;
const TAB_UNIQ_ID = 'tabUniqId'; // Currently only only a uniq identifier: no values stored behind the identifier - might change.
// AutoCron // AutoCron
const AUTOCRON_MAX_AGE_MINUTES = 10; const AUTOCRON_MAX_AGE_MINUTES = 10;
...@@ -1949,3 +1953,20 @@ const SETTING_TABLESORTER_MODE = 'mode'; ...@@ -1949,3 +1953,20 @@ const SETTING_TABLESORTER_MODE = 'mode';
const SETTING_TABLESORTER_MODE_DELETE = 'delete'; const SETTING_TABLESORTER_MODE_DELETE = 'delete';
const SETTING_TABLESORTER_CLEAR = 'Clear'; const SETTING_TABLESORTER_CLEAR = 'Clear';
// Object: Type
const T_LABEL = 't_label';
const T_INPUT = 't_input';
const T_NOTE = 't_note';
const T_ = 't_note';
// Object: Item
const I_TYPE = 'type';
const I_ID = 'id';
const I_VALUE = 'value';
const I_CLASS = 'class';
const I_ATTRIBUTE = 'attribute';
// Object: extra for checkboxes
const I_CHECKED = 'checked';
const I_UNCHECKED = 'unchecked';
...@@ -185,6 +185,10 @@ $UPDATE_ARRAY = array( ...@@ -185,6 +185,10 @@ $UPDATE_ARRAY = array(
"ALTER TABLE `Setting` CHANGE `created` `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; ", "ALTER TABLE `Setting` CHANGE `created` `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; ",
], ],
'19.12.0' => [
"ALTER TABLE `Dirty` ADD `tabUniqId` VARCHAR(32) NOT NULL AFTER `recordHashMd5`;",
],
); );
......
This diff is collapsed.
...@@ -8,13 +8,12 @@ ...@@ -8,13 +8,12 @@
namespace IMATHUZH\Qfq\Core\Form; namespace IMATHUZH\Qfq\Core\Form;
use IMATHUZH\Qfq\Core\Store\Session;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Database\Database; use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Helper\OnArray;
use IMATHUZH\Qfq\Core\Store\Client; use IMATHUZH\Qfq\Core\Store\Client;
use IMATHUZH\Qfq\Core\Store\Session;
use IMATHUZH\Qfq\Core\Store\Sip; use IMATHUZH\Qfq\Core\Store\Sip;
use IMATHUZH\Qfq\Core\Helper\OnArray; use IMATHUZH\Qfq\Core\Store\Store;
/** /**
* Class Dirty * Class Dirty
...@@ -27,7 +26,7 @@ use IMATHUZH\Qfq\Core\Helper\OnArray; ...@@ -27,7 +26,7 @@ use IMATHUZH\Qfq\Core\Helper\OnArray;
class Dirty { class Dirty {
/** /**
* @var Database instantiated class * @var Database[] - Array of Database instantiated class
*/ */
protected $dbArray = null; protected $dbArray = null;
...@@ -134,7 +133,7 @@ class Dirty { ...@@ -134,7 +133,7 @@ class Dirty {
switch ($this->client[API_LOCK_ACTION]) { switch ($this->client[API_LOCK_ACTION]) {
case API_LOCK_ACTION_LOCK: case API_LOCK_ACTION_LOCK:
case API_LOCK_ACTION_EXTEND: case API_LOCK_ACTION_EXTEND:
$answer = $this->acquireDirty($recordId, $tableVars, $this->client[DIRTY_RECORD_HASH_MD5]); $answer = $this->acquireDirty($recordId, $tableVars, $this->client[DIRTY_RECORD_HASH_MD5], $this->client[TAB_UNIQ_ID]);
break; break;
case API_LOCK_ACTION_RELEASE: case API_LOCK_ACTION_RELEASE:
$answer = $this->checkDirtyAndRelease(FORM_SAVE, $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS], $tableVars[F_DIRTY_MODE], $tableVars[F_TABLE_NAME], $tableVars[F_PRIMARY_KEY], $recordId); $answer = $this->checkDirtyAndRelease(FORM_SAVE, $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS], $tableVars[F_DIRTY_MODE], $tableVars[F_TABLE_NAME], $tableVars[F_PRIMARY_KEY], $recordId);
...@@ -153,10 +152,13 @@ class Dirty { ...@@ -153,10 +152,13 @@ class Dirty {
* @param array $tableVars * @param array $tableVars
* @param string $recordHashMd5 * @param string $recordHashMd5
* *
* @param $tabUniqId
* @return array * @return array
* @throws \CodeException * @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
private function acquireDirty($recordId, array $tableVars, $recordHashMd5) { private function acquireDirty($recordId, array $tableVars, $recordHashMd5, $tabUniqId) {
$tableName = $tableVars[F_TABLE_NAME]; $tableName = $tableVars[F_TABLE_NAME];
$primaryKey = $tableVars[F_PRIMARY_KEY]; $primaryKey = $tableVars[F_PRIMARY_KEY];
...@@ -179,10 +181,14 @@ class Dirty { ...@@ -179,10 +181,14 @@ class Dirty {
$answer = [API_STATUS => 'success', API_MESSAGE => '']; $answer = [API_STATUS => 'success', API_MESSAGE => ''];
} else { } else {
// No dirty record found. // No dirty record found.
$answer = $this->writeDirty($this->client[SIP_SIP], $recordId, $tableVars, $feUser, $rcMd5); $answer = $this->writeDirty($this->client[SIP_SIP], $recordId, $tableVars, $feUser, $rcMd5, $tabUniqId);
} }
} else { } else {
$answer = $this->conflict($recordDirty, $formDirtyMode, $primaryKey); if ($tabUniqId == $recordDirty[TAB_UNIQ_ID]) {
$answer = [API_STATUS => 'success', API_MESSAGE => ''];
} else {
$answer = $this->conflict($recordDirty, $formDirtyMode, $primaryKey);
}
} }
return $answer; return $answer;
...@@ -197,6 +203,8 @@ class Dirty { ...@@ -197,6 +203,8 @@ class Dirty {
* *
* @return array DirtyRecord or empty array. * @return array DirtyRecord or empty array.
* @throws \CodeException * @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
private function getRecordDirty($tableName, $recordId) { private function getRecordDirty($tableName, $recordId) {
...@@ -219,6 +227,9 @@ class Dirty { ...@@ -219,6 +227,9 @@ class Dirty {
* *
* @param $primaryKey * @param $primaryKey
* @return array * @return array
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
private function conflict(array $recordDirty, $currentFormDirtyMode, $primaryKey) { private function conflict(array $recordDirty, $currentFormDirtyMode, $primaryKey) {
$status = API_ANSWER_STATUS_CONFLICT; $status = API_ANSWER_STATUS_CONFLICT;
...@@ -263,9 +274,13 @@ class Dirty { ...@@ -263,9 +274,13 @@ class Dirty {
* @param string $feUser * @param string $feUser
* @param string $recordHashMd5 * @param string $recordHashMd5
* *
* @param $tabUniqId
* @return array * @return array
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
private function writeDirty($s, $recordId, array $tableVars, $feUser, $recordHashMd5) { private function writeDirty($s, $recordId, array $tableVars, $feUser, $recordHashMd5, $tabUniqId) {
$tableName = $tableVars[F_TABLE_NAME]; $tableName = $tableVars[F_TABLE_NAME];
$primaryKey = $tableVars[F_PRIMARY_KEY]; $primaryKey = $tableVars[F_PRIMARY_KEY];
...@@ -276,9 +291,9 @@ class Dirty { ...@@ -276,9 +291,9 @@ class Dirty {
# Dirty workaround: setting the 'expired timestamp' minus 1 second guarantees that the client ask for relock always if the timeout is expired. # Dirty workaround: setting the 'expired timestamp' minus 1 second guarantees that the client ask for relock always if the timeout is expired.
$expire = date('Y-m-d H:i:s', strtotime("+" . $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] - 1 . " seconds")); $expire = date('Y-m-d H:i:s', strtotime("+" . $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] - 1 . " seconds"));
// Write 'dirty' record // Write 'dirty' record
$this->dbArray[$this->dbIndexQfq]->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `expire`, `recordHashMd5`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " . $this->dbArray[$this->dbIndexQfq]->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `expire`, `recordHashMd5`, `tabUniqId`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
"VALUES ( ?,?,?,?,?,?,?,?,?,? )", ROW_REGULAR, "VALUES ( ?,?,?,?,?,?,?,?,?,?,? )", ROW_REGULAR,
[$s, $tableName, $recordId, $expire, $recordHashMd5, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode, [$s, $tableName, $recordId, $expire, $recordHashMd5, $tabUniqId, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode,
$this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]); $this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]);
return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '',
...@@ -295,6 +310,9 @@ class Dirty { ...@@ -295,6 +310,9 @@ class Dirty {
* @param string $recordHashMd5 - timestamp e.g. '2017-07-27 14:06:56' * @param string $recordHashMd5 - timestamp e.g. '2017-07-27 14:06:56'
* @param $rcMd5 * @param $rcMd5
* @return bool true if $recordHashMd5 is different from current record md5 hash. * @return bool true if $recordHashMd5 is different from current record md5 hash.
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
private function isRecordModified($tableName, $primaryKey, $recordId, $recordHashMd5, &$rcMd5) { private function isRecordModified($tableName, $primaryKey, $recordId, $recordHashMd5, &$rcMd5) {
...@@ -319,6 +337,8 @@ class Dirty { ...@@ -319,6 +337,8 @@ class Dirty {
* *
* @return int LOCK_NOT_FOUND | LOCK_FOUND_OWNER | LOCK_FOUND_CONFLICT, * @return int LOCK_NOT_FOUND | LOCK_FOUND_OWNER | LOCK_FOUND_CONFLICT,
* @throws \CodeException * @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
public function getCheckDirty($tableName, $recordId, array &$recordDirty, &$msg) { public function getCheckDirty($tableName, $recordId, array &$recordDirty, &$msg) {
...@@ -334,6 +354,11 @@ class Dirty { ...@@ -334,6 +354,11 @@ class Dirty {
return LOCK_NOT_FOUND; return LOCK_NOT_FOUND;
} }
// 'Reload Tab' don't send a tab ID - Possible lock conflict will be skipped here and a) pops up later, or b) is not a conflict if it's the same tab.
if (!isset($this->client[TAB_UNIQ_ID]) && $recordDirty[DIRTY_QFQ_USER_SESSION_COOKIE] == $this->client[CLIENT_COOKIE_QFQ]) {
return LOCK_NOT_FOUND;
}
if ($recordDirty[DIRTY_QFQ_USER_SESSION_COOKIE] == $this->client[CLIENT_COOKIE_QFQ]) { if ($recordDirty[DIRTY_QFQ_USER_SESSION_COOKIE] == $this->client[CLIENT_COOKIE_QFQ]) {
$msgUser = "you"; $msgUser = "you";
} else { } else {
...@@ -364,6 +389,7 @@ class Dirty { ...@@ -364,6 +389,7 @@ class Dirty {
* @param bool $flagCheckModifiedFirst * @param bool $flagCheckModifiedFirst
* @return array * @return array
* @throws \CodeException * @throws \CodeException
* @throws \DbException
* @throws \UserFormException * @throws \UserFormException
*/ */
public function checkDirtyAndRelease($formMode, $lockTimeout, $dirtyMode, $tableName, $primaryKey, $recordId, $flagCheckModifiedFirst = false) { public function checkDirtyAndRelease($formMode, $lockTimeout, $dirtyMode, $tableName, $primaryKey, $recordId, $flagCheckModifiedFirst = false) {
...@@ -446,6 +472,8 @@ class Dirty { ...@@ -446,6 +472,8 @@ class Dirty {
* @param int $recordDirtyId * @param int $recordDirtyId
* *
* @throws \CodeException * @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/ */
private function deleteDirtyRecord($recordDirtyId) { private function deleteDirtyRecord($recordDirtyId) {
......
...@@ -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
*/
private static $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,299 @@ EOF; ...@@ -560,4 +565,299 @@ 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[FE_ITEM_LIST]) && strlen($formElement[FE_ITEM_LIST]) > 0) {
$arr = KeyValueStringParser::parse($formElement[FE_ITEM_LIST], ':', ',', 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);
}