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

Merge branch 'master' into b9789-earlyRelease

parents 00e4e5cb 722df7da
Pipeline #3044 failed with stages
in 7 minutes and 8 seconds
......@@ -53,6 +53,7 @@ composer.lock
/extension/Resources/Public/Css
/extension/Resources/Public/fonts
/extension/Resources/Public/JavaScript
/extension/Tests/selenium/tmp
/fonts
/js
/node_modules
......@@ -63,3 +64,7 @@ composer.lock
/javascript/src/.vscode
/javascript/src/npm-debug.log
/docker/chromedriver
/docker/geckodriver
/docker/run_qfq_docker.output
......@@ -65,7 +65,12 @@ selenium:
- mkdir "$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 "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/
.. ==================================================
.. Header hierachy
.. Header hierarchy
.. ==
.. --
.. ^^
......@@ -36,6 +36,56 @@ Features
Bug Fixes
^^^^^^^^^
Version 19.12.0
---------------
Date: <date>
Notes
^^^^^
* Switch the whole homepage to readonly: FormModeGlobal and STORE_USER
Features
^^^^^^^^
* TinyMCE: grey out controls when readonly.
* Mockup:
* Update files to get CSS & JS files from their own directory structure (not an installed QFQ extension).
* Add fontawesome, tablesorter to mockup 'formCheckbox.html'.
* CI: Download selenium logs (only failed) under artifacts.
* Dev: .gitignore: exclude some docker & selenium.
* Merge Selenium Python Checks into Master.
* #9686 / html decode and sanitize an export filename to become the 'save as'-filename.
* #9666 / min-width for extraButtonInfo.
* FormEditor: optimize minWidth for 'rowLabelInputNote' field.
Bug Fixes
^^^^^^^^^
* #9720 / Checkbox dynamic update varoious settings:
* Multi Plain Vertical & Horizontal, Checkbox Multi BS.
* Fixed that label of 'checkbox' are bold and label of 'checkbox-inline' are normal.
* #7974 / TinyMCE: ReadOnly.
* #9424 / modeSql: skip if it starts with '#'.
* #9531 / File Upload Required.
* #9674 / Select Required Dynamic Update.
* #9678 / textarea now trigger DynamicUpdate.
* #9679 / FormModeGlobal: add STORE_USER - system wide readonly.
* #9690 / Select Required.
* #9691 / Checkbox: dynamic update > readonly. HTML ID for checkbox elements. Dynamic update switch 'readonly' for 'checkbox plain multi' and 'radio plain multi'.
* #9692 / Keyboard Select Checkbox.
* #9720 / Checkbox: Various setups with dynamic update.
* #9733 / Identiy different tabs. Record lock for same tab will always be granted.
* #9734 / Fix 'dirty lock release' - leaving a dirty form without closing, leaves a stale lock record. Added a releaselock() before window.unload. Dirty remove on goBack.
* #9735 / File Delete: no dirty trigger.
* Download / PDF merge: skip leading errors, interpret only 'Could not merge encrypted files'.
* DragAndDrop broken: after refactoring Support.php, the dragAndDrop was broken - missed init of '$store'.
Version 19.11.3
---------------
......@@ -49,14 +99,17 @@ Features
* #8886 / Check pattern: after focus lost.
* #9655 / Checkboxes and radios now defined with a min-width in horizontal plain mode.
* #9617:
* #9617 / formModeGlobal:
* Two modes now implemented 'requiredOff' and 'requiredOffButMark'.
* formModeGlobal: renamed skipRequiredCheck to requiredOffButMark .
* Keep required marks after save.
* Stop hiding helpblocks per default, set with class qfq-only-active-error.
* Two modes of 'formModeGlobal' available: 'requiredOff' and 'requiredOffButMark'.
* 'requiredOffButMark':
* Radio: new class 'qfq-disabled' if readonly is set. softer blue. mark disabled - changed hover. text in darker orange. simple-error renamed to qfq-notify - removed box around error.
* Renamed temporary 'skipRequiredCheck' to 'requiredOffButMark'.
* Keep required marks after save.
* Stop hiding helpblocks per default, set with class qfq-only-active-error.
* Radio: new class 'qfq-disabled' if readonly is set. Softer blue. Mark disabled - changed hover. Text in darker orange.
Simple-error renamed to qfq-notify - removed box around error.
* New default class for 'form' tag: 'qfq-notify'.
* Manual.rst: Add info for 'letter-no-break'.
* Add validator.js to list of used packages.
......@@ -86,21 +139,17 @@ Date: 25.11.2019
Notes
^^^^^
* Fantastic new formModeGlobal=skipRequiredCheck: fill's '{{allRequiredGiven:V}}' before save to 1 (all given) else 0.
* Enhance formModeGlobal=requiredOff/-ButMark (temporarily skipRequiredCheck): fill's '{{allRequiredGiven:V}}' before
save to 1 (all given) else 0.
Offers user to save form, even if not all required data are given and offers application logic to check easily if all
required fields has been fillled.
* formModeGlobal=requiredOff is now deprecated but will still work.
required fields has been filled.
Features
^^^^^^^^
* #9526 / Mark required fields more visible
* #9526 / Mark required fields more visible.
* #9617 / Improve 'formModeGlobal=requiredOff'.
* New mode name 'skipRequiredCheck'.
* 'requiredOff' is replaced by 'skipRequiredCheck' and now deprecated.
* Add documentation for 'skipRequiredCheck:S' & 'allRequiredGiven:V'
* Feature `Form.formModeGlobal` implemented - STORE_SIP overwrites form definition.
* New STORE_VAR variable 'allRequiredGiven'. Becomes '1' if all required fields are given, else 0.
......@@ -114,11 +163,11 @@ Bug Fixes
* #7639 / subrecord drag n drop:
* `orderInterval` has not been respected.
* Update Manual.rst
* Update Manual.rst.
* Fake STORE_SIP so it can be used during processing sql1.
* The record, currently loaded into form, is available via STORE_RECORD.
* Check for id/_id and ord/_ord
* Throw meaningful exception if missing 'id' or 'ord'
* Check for id/_id and ord/_ord.
* Throw meaningful exception if missing 'id' or 'ord'.
* Fixes bug that no mime_type_content is called if there is on file.
* Fix broken regex101 url.
......
......@@ -54,12 +54,12 @@ Neue Versionsnummer
* Update the version number in this document (topic 6)
* Commit & Push new version changes to master branch:
New version 19.11.3
New version 19.12.0
6) **New Tag**:
git tag v19.11.3
git push -u origin v19.11.3
git tag v19.12.0
git push -u origin v19.12.0
7) Tickets:
* Schliessen und der QFQ Version zuweisen.
......
......@@ -518,6 +518,13 @@ On `"conflict"` the Client opens the alert as modal dialog (user can't change an
form' button.
On `"conflict_allow_force"` the Client opens the alert non-modal (default).
### tabUniqId
Every tab get's a uniq id (timestamp) on page load: window.name.
This 'tabUniqId' is saved in dirty record on lock acquire.
On page reload, when the 'release' comes after 'acquire' (unwished async behaviour), the locking is skipped (if same user
session) - on reload there is no variable 'tabUniqId'. On real lock acquire, the tab ID is compared and will be denied
if not matching. The 'tabUniqId' might not work in IE - doesn't matter: it's a seldom special situation.
### Drag And Drop (sort)
......
**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 @@
.. ;;
.. ,,
..
.. --------------------------------------------------
.. --------------------------------------------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
.. Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html
.. Italic *italic*
......@@ -7618,7 +7618,7 @@ A dedicated `Form`, without any `FormElements`, is used to define the reorder lo
Fields:
* 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:
......@@ -7663,6 +7663,7 @@ QFQ CSS Classes
* `qfq-100` - assigned to different tags, makes an element 'width: 100%'.
* `qfq-left`- assigned to different tags, Text align left.
* `qfq-sticky` - assigned to `<thead>`, makes the header sticky.
* `qfq-badge`, `qfq-badge-error`, `qfq-badge-warning`, `qfq-badge-success`, `qfq-badge-info`, `qfq-badge-invers` - colorized BS3 badges.
* `letter-no-break` - assigned to a `div` will protect a paragraph (CSS: page-break-before: avoid;) not to break around
a page border (converted to PDF via wkhtml). Take care that `qfq-letter.css` is included in TypoScript setup.
......
......@@ -36,6 +36,57 @@ Features
Bug Fixes
^^^^^^^^^
Version 19.12.0
---------------
Date: 17.12.2019
Notes
^^^^^
* Switch the whole homepage to readonly: FormModeGlobal and STORE_USER
Features
^^^^^^^^
* TinyMCE: grey out controls when readonly.
* Mockup:
* Update files to get CSS & JS files from their own directory structure (not an installed QFQ extension).
* Add fontawesome, tablesorter to mockup 'formCheckbox.html'.
* CI: Download selenium logs (only failed) under artifacts.
* Dev: .gitignore: exclude some docker & selenium.
* Merge Selenium Python Checks into Master.
* #9686 / html decode and sanitize an export filename to become the 'save as'-filename.
* #9666 / min-width for extraButtonInfo.
* FormEditor: optimize minWidth for 'rowLabelInputNote' field.
Bug Fixes
^^^^^^^^^
* #9720 / Checkbox dynamic update varoious settings:
* Multi Plain Vertical & Horizontal, Checkbox Multi BS.
* Fixed that label of 'checkbox' are bold and label of 'checkbox-inline' are normal.
* #7974 / TinyMCE: ReadOnly.
* #9424 / modeSql: skip if it starts with '#'.
* #9531 / File Upload Required.
* #9674 / Select Required Dynamic Update.
* #9678 / textarea now trigger DynamicUpdate.
* #9679 / FormModeGlobal: add STORE_USER - system wide readonly.
* #9690 / Select Required.
* #9691 / Checkbox: dynamic update > readonly. HTML ID for checkbox elements. Dynamic update switch 'readonly' for 'checkbox plain multi' and 'radio plain multi'.
* #9692 / Keyboard Select Checkbox.
* #9720 / Checkbox: Various setups with dynamic update.
* #9733 / Identiy different tabs. Record lock for same tab will always be granted.
* #9734 / Fix 'dirty lock release' - leaving a dirty form without closing, leaves a stale lock record. Added a releaselock() before window.unload. Dirty remove on goBack.
* #9735 / File Delete: no dirty trigger.
* Download / PDF merge: skip leading errors, interpret only 'Could not merge encrypted files'.
* DragAndDrop broken: after refactoring Support.php, the dragAndDrop was broken - missed init of '$store'.
Version 19.11.3
---------------
......
......@@ -21,8 +21,8 @@
; you can use in 'conf.py'
project = QFQ - Quick Form Query
version = 19.11
release = 19.11.3
version = 19.12
release = 19.12.0
t3author = Carsten Rose
copyright = since 2017 by the author
......
......@@ -440,6 +440,16 @@ module.exports = function (grunt) {
expand: 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/',
expand: true,
......@@ -448,6 +458,15 @@ module.exports = function (grunt) {
],
dest: typo3_fonts,
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;
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.
}
// 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];
......@@ -807,7 +812,7 @@ EOF;
// Label
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],
......
......@@ -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_CHECKBOX_CHECKED = 'checked';
const FE_CHECKBOX_UNCHECKED = 'unchecked';
const FE_ITEM_LIST = 'itemList';
const FE_RECORD_DESTINATION_TABLE = 'recordDestinationTable';
const FE_RECORD_SOURCE_TABLE = 'recordSourceTable';
const FE_TRANSLATE_ID_COLUMN = 'translateIdColumn';
......@@ -1328,6 +1329,8 @@ const FE_ORDER_INTERVAL_DEFAULT = '10';
const FE_ORDER_COLUMN = 'orderColumn';
const FE_DND_TABLE = 'dndTable';
const FE_TMP_CLASS_OPTION = '_classOption';
const MODE_ENCODE = 'encode';
const MODE_DECODE = 'decode';
const MODE_NONE = 'none';
......@@ -1812,6 +1815,7 @@ const DIRTY_API_ACTION_EXTEND = 'extend';
const LOCK_NOT_FOUND = 0;
const LOCK_FOUND_OWNER = 1;
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
const AUTOCRON_MAX_AGE_MINUTES = 10;
......@@ -1949,3 +1953,20 @@ const SETTING_TABLESORTER_MODE = 'mode';
const SETTING_TABLESORTER_MODE_DELETE = 'delete';
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(
"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 @@
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\Helper\OnArray;
use IMATHUZH\Qfq\Core\Store\Client;
use IMATHUZH\Qfq\Core\Store\Session;
use IMATHUZH\Qfq\Core\Store\Sip;
use IMATHUZH\Qfq\Core\Helper\OnArray;
use IMATHUZH\Qfq\Core\Store\Store;
/**
* Class Dirty
......@@ -27,7 +26,7 @@ use IMATHUZH\Qfq\Core\Helper\OnArray;
class Dirty {
/**
* @var Database instantiated class
* @var Database[] - Array of Database instantiated class
*/
protected $dbArray = null;
......@@ -134,7 +133,7 @@ class Dirty {
switch ($this->client[API_LOCK_ACTION]) {
case API_LOCK_ACTION_LOCK:
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;
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);
......@@ -153,10 +152,13 @@ class Dirty {
* @param array $tableVars
* @param string $recordHashMd5
*
* @param $tabUniqId
* @return array
* @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];
$primaryKey = $tableVars[F_PRIMARY_KEY];
......@@ -179,10 +181,14 @@ class Dirty {
$answer = [API_STATUS => 'success', API_MESSAGE => ''];
} else {
// 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 {
$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;
......@@ -197,6 +203,8 @@ class Dirty {
*
* @return array DirtyRecord or empty array.
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
private function getRecordDirty($tableName, $recordId) {
......@@ -219,6 +227,9 @@ class Dirty {
*
* @param $primaryKey
* @return array
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
private function conflict(array $recordDirty, $currentFormDirtyMode, $primaryKey) {
$status = API_ANSWER_STATUS_CONFLICT;
......@@ -263,9 +274,13 @@ class Dirty {
* @param string $feUser
* @param string $recordHashMd5
*
* @param $tabUniqId
* @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];
$primaryKey = $tableVars[F_PRIMARY_KEY];
......@@ -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.
$expire = date('Y-m-d H:i:s', strtotime("+" . $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] - 1 . " seconds"));
// Write 'dirty' record
$this->dbArray[$this->dbIndexQfq]->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `expire`, `recordHashMd5`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
"VALUES ( ?,?,?,?,?,?,?,?,?,? )", ROW_REGULAR,
[$s, $tableName, $recordId, $expire, $recordHashMd5, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode,
$this->dbArray[$this->dbIndexQfq]->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `expire`, `recordHashMd5`, `tabUniqId`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
"VALUES ( ?,?,?,?,?,?,?,?,?,?,? )", ROW_REGULAR,
[$s, $tableName, $recordId, $expire, $recordHashMd5, $tabUniqId, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode,
$this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]);
return [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '',
......@@ -295,6 +310,9 @@ class Dirty {
* @param string $recordHashMd5 - timestamp e.g. '2017-07-27 14:06:56'
* @param $rcMd5
* @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) {
......@@ -319,6 +337,8 @@ class Dirty {
*
* @return int LOCK_NOT_FOUND | LOCK_FOUND_OWNER | LOCK_FOUND_CONFLICT,
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
public function getCheckDirty($tableName, $recordId, array &$recordDirty, &$msg) {
......@@ -334,6 +354,11 @@ class Dirty {
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]) {
$msgUser = "you";
} else {
......@@ -364,6 +389,7 @@ class Dirty {
* @param bool $flagCheckModifiedFirst
* @return array
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
public function checkDirtyAndRelease($formMode, $lockTimeout, $dirtyMode, $tableName, $primaryKey, $recordId, $flagCheckModifiedFirst = false) {
......@@ -446,6 +472,8 @@ class Dirty {
* @param int $recordDirtyId
*
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
private function deleteDirtyRecord($recordDirtyId) {
......
......@@ -17,6 +17,11 @@ use IMATHUZH\Qfq\Core\Store\Store;
*/
class HelperFormElement {
/**
* @var Store
*/
private static $store = null;
/**
* Expand column $keyName to row array as virtual columns.
* E.g.: [ 'id' => '1', 'name' => 'John', 'parameter' => 'detail=xId:grId\nShowEmptyAtStart=1' ] becomes:
......@@ -88,9 +93,9 @@ class HelperFormElement {
$checkKeys = array_keys($arr);
foreach ($checkKeys AS $checkKey) {
if (!empty($element[$checkKey])) {
$store = Store::getInstance();
$store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($element), STORE_SYSTEM);
$store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, $keyName, STORE_SYSTEM);
self::$store = Store::getInstance();
self::$store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($element), STORE_SYSTEM);
self::$store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, $keyName, STORE_SYSTEM);
throw new \UserFormException("Found reserved keyname '$checkKey'", ERROR_RESERVED_KEY_NAME);
}
}
......@@ -361,12 +366,12 @@ class HelperFormElement {
*/
public static function prepareExtraButton(array $formElement, $showInline) {
$store = Store::getInstance();
self::$store = Store::getInstance();
$infoSymbolInside = $store->getVar(SYSTEM_EXTRA_BUTTON_INFO_INLINE, STORE_SYSTEM);
$infoSymbolOutside = $store->getVar(SYSTEM_EXTRA_BUTTON_INFO_BELOW, STORE_SYSTEM);
$infoSymbolInside = self::$store->getVar(SYSTEM_EXTRA_BUTTON_INFO_INLINE, 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;
}
......@@ -560,4 +565,299 @@ EOF;
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 &$it