Commit 84b36b73 authored by Carsten  Rose's avatar Carsten Rose

Merge branch 'F5695-Multiform' into 'master'

F5695 multiform

See merge request !186
parents 57330aac 9bc3c9c1
Pipeline #2507 passed with stages
in 2 minutes and 35 seconds
Neuer Build
===========
* release: Wird ein *Tag* vergeben (egal welcher Branch) der mit 'v' beginnt, erzeugt das automatisch einen Build - https://w3.math.uzh.ch/qfq/release.
* snapshot: Jeder Commit (egal welcher Branch) erzeugt einen Snapshot - https://w3.math.uzh.ch/qfq/snapshot.
* nightly: Nach einem Commit auf Branch 'master' tagsueber, wird um 23:55 ein 'nightly' Build erstellt - https://w3.math.uzh.ch/qfq/nightly.
......
......@@ -4136,10 +4136,14 @@ Parameter: slaveId
Note:
* `{{slaveId}}` can be used in any query of the current *FormElement*.
* `{{slaveId:V}}` can be used in any query of the current *FormElement*.
* If the `action`-*FormElement* name exist as a column in the master record: Update that column *automatically* with the
recent slaveId
* After an INSERT the `last_insert_id()` becomes the *slaveId*).
* After an INSERT the `last_insert_id()` becomes the *{{slaveId:V}}*.
* `fillStoreVar` is fired first, than `slaveId`.
* If `slaveId` is known in `fillStoreVar`, set: `slaveId={{someId:V}}`.
Parameter: sqlBefore / sqlInsert / sqlUpdate / sqlDelete / sqlAfter
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
......@@ -4348,10 +4352,63 @@ Action
in one of the specified required FEs.
.. _multi-language-form:
.. _multi-form:
Multi Language Form
-------------------
Multi Form
----------
`Multi Forms` are like a regular form with the difference that the shown FormElements are repeated for *each* selected record
(defined by `multiSql`).
+------------------+----------------------------------+------------------------------------------------+
| Name | | |
+==================+==================================+================================================+
| multiSql | {{!SELECT id, name FROM Person}} | Query to select MulitForm records |
+------------------+----------------------------------+------------------------------------------------+
| multiMgsNoRecord | Default: No data | Message shown if `multiSql` selects no records |
+------------------+----------------------------------+------------------------------------------------+
The Form is shown as a HTML table.
* `multiSql`: Selects the records where the defined FormElements will work on each.
* A uniq column 'id' or '_id' (not shown) is mandatory and has to reflect an existing record id in table `primary table`.
* Additional columns, defined in `multiSql`, will be shown on the form in the same line, before the FormElements.
`
Simple
======
General:
* It's not possible to create new records in simple mode, only existing records can be used.
Form:
* Per row, the STORE_RECORD is filled with the whole record of the primary table, referenced
by `multiSql.id`.
FormElement:
* The FormElement.name represents a column of the defined primary table.
* The existing values of such FormElements are automatically loaded.
* No further definition is required.
Advanced
========
* The `FormElement.name` do not have to be a column of the primary table.
* If `FormElement.name` is not a column of the primary table, the insert/update/delete SQL statement has to be
extra defined.
.. _multiple-languages:
Multiple languages
------------------
QFQ Forms might be configured for up to 5 different languages. Per language there is one extra field in the *Form editor*.
Which field represents which language is configured in configuration_.
......
......@@ -21,10 +21,13 @@ class QfqController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* @return string
* @throws \CodeException
* @throws \UserFormException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \UserFormException
*/
public function showAction() {
......
......@@ -149,6 +149,11 @@ class BuildFormBootstrap extends AbstractBuildForm {
$class = ['tab-content', $this->formSpec[F_CLASS_BODY]];
if ($pill == '') {
$class[] = 'col-md-12';
$class[] = 'qfq-form-body'; // Make an outline on form body
if ($title == '') {
$class[] = 'qfq-form-no-title';
}
}
$html .= "<div " . Support::doAttribute('class', $class) . ">";
......@@ -714,6 +719,54 @@ EOF;
return $html;
}
/**
* Wrap content with $wrapArray or, if specified use $formElement[$wrapName]. Inject $htmlId in wrap.
*
* Result:
* - if $bsColumns==0 and empty $formElement[$wrapName]: no wrap
* - if $formElement[$wrapName] is given: wrap with that one. Else: wrap with $wrapArray
* - if $htmlId is give, inject it in $wrap.
*
* @param array $formElement Complete FormElement, especially some FE_WRAP
* @param string $htmlElement Content to wrap.
* @param string $wrapName FE_WRAP_ROW, FE_WRAP_LABEL, FE_WRAP_INPUT, FE_WRAP_NOTE
* @param int $bsColumns
* @param array $wrapArray System wide Defaults: [ 'open wrap', 'close wrap' ]
* @param string $htmlId
* @param string $class
*
* @return string Wrapped $htmlElement
* @throws \CodeException
* @throws \UserFormException
*/
private function customWrap(array $formElement, $htmlElement, $wrapName, $bsColumns, array $wrapArray, $htmlId = '', $class = '') {
// If $bsColumns==0: do not wrap with default.
if ($bsColumns == '0') {
$wrapArray[0] = '';
$wrapArray[1] = '';
}
// If there is a 'per FormElement'-wrap, take it.
if (isset($formElement[$wrapName])) {
$wrapArray = explode('|', $formElement[$wrapName], 2);
}
if (count($wrapArray) != 2) {
throw new \UserFormException("Need open & close wrap token for FormElement.parameter" . $wrapName . " - E.g.: <div ...>|</div>", ERROR_MISSING_VALUE);
}
if ($wrapArray[0] != '') {
$wrapArray[0] = Support::insertAttribute($wrapArray[0], 'id', $htmlId);
$wrapArray[0] = Support::insertAttribute($wrapArray[0], 'class', $class); // might be problematic, if there is already a 'class' defined.
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.
}
}
return $wrapArray[0] . $htmlElement . $wrapArray[1];
}
/**
* @param array $formElement Complete FormElement, especially some FE_WRAP
* @param string $htmlElement Content to wrap.
......@@ -775,55 +828,6 @@ EOF;
return $html;
}
/**
* Wrap content with $wrapArray or, if specified use $formElement[$wrapName]. Inject $htmlId in wrap.
*
* Result:
* - if $bsColumns==0 and empty $formElement[$wrapName]: no wrap
* - if $formElement[$wrapName] is given: wrap with that one. Else: wrap with $wrapArray
* - if $htmlId is give, inject it in $wrap.
*
* @param array $formElement Complete FormElement, especially some FE_WRAP
* @param string $htmlElement Content to wrap.
* @param string $wrapName FE_WRAP_ROW, FE_WRAP_LABEL, FE_WRAP_INPUT, FE_WRAP_NOTE
* @param int $bsColumns
* @param array $wrapArray System wide Defaults: [ 'open wrap', 'close wrap' ]
* @param string $htmlId
* @param string $class
*
* @return string Wrapped $htmlElement
* @throws \CodeException
* @throws \UserFormException
*/
private function customWrap(array $formElement, $htmlElement, $wrapName, $bsColumns, array $wrapArray, $htmlId = '', $class = '') {
// If $bsColumns==0: do not wrap with default.
if ($bsColumns == '0') {
$wrapArray[0] = '';
$wrapArray[1] = '';
}
// If there is a 'per FormElement'-wrap, take it.
if (isset($formElement[$wrapName])) {
$wrapArray = explode('|', $formElement[$wrapName], 2);
}
if (count($wrapArray) != 2) {
throw new \UserFormException("Need open & close wrap token for FormElement.parameter" . $wrapName . " - E.g.: <div ...>|</div>", ERROR_MISSING_VALUE);
}
if ($wrapArray[0] != '') {
$wrapArray[0] = Support::insertAttribute($wrapArray[0], 'id', $htmlId);
$wrapArray[0] = Support::insertAttribute($wrapArray[0], 'class', $class); // might be problematic, if there is already a 'class' defined.
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.
}
}
return $wrapArray[0] . $htmlElement . $wrapArray[1];
}
/**
* @param $formElement
* @param $elementHtml
......@@ -891,11 +895,9 @@ EOF;
$attribute = ($formElement[FE_MODE] == FE_MODE_HIDDEN) ? ' style="display: none;"' : '';
$attribute .= Support::doAttribute('id', $formElement[FE_HTML_ID]);
return Support::wrapTag("<span name='qfq-subrecord' $attribute>", $html);
}
/**
* Builds complete 'form'.
*
......@@ -908,11 +910,14 @@ EOF;
* @throws \CodeException
* @throws \DbException
* @throws \DownloadException
* @throws \UserFormException
* @throws \UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \UserFormException
* @throws \UserReportException
*/
public function process($mode, $htmlElementNameIdZero = false, $latestFeSpecNative = array()) {
......
......@@ -165,7 +165,7 @@ const ERROR_USER_NOT_LOGGED_IN = 1017;
const ERROR_USER_LOGGED_IN = 1018;
const ERROR_FORM_FORBIDDEN = 1019;
const ERROR_FORM_UNKNOWN_PERMISSION_MODE = 1020;
const ERROR_MULTI_SQL_MISSING = 1021;
const ERROR_TOKEN_MISSING = 1022;
const ERROR_RECURSION_TOO_DEEP = 1023;
const ERROR_CHECKBOXMODE_UNKNOWN = 1024;
......@@ -926,6 +926,8 @@ const F_EXTRA_DELETE_FORM = 'extraDeleteForm';
const F_FINAL_DELETE_FORM = 'finalDeleteForm';
const F_DIRTY_MODE = 'dirtyMode';
const F_NOTE_INTERNAL = 'noteInternal';
const F_MULTI_SQL = 'multiSql';
const F_MULTI_COL_ID = 'id';
const F_SUBMIT_BUTTON_TEXT = 'submitButtonText';
const F_BUTTON_ON_CHANGE_CLASS = 'buttonOnChangeClass';
......@@ -1038,6 +1040,8 @@ const F_ORDER_COLUMN = 'orderColumn';
const F_ORDER_COLUMN_NAME = 'ord';
const F_SHOW_ID_IN_FORM_TITLE = SYSTEM_SHOW_ID_IN_FORM_TITLE;
const F_MULTI_MSG_NO_RECORD = 'multiMsgNoRecord';
const F_MULTI_MSG_NO_RECORD_TEXT = 'No data';
const F_REST_SQL_LIST = 'restSqlList';
const F_REST_SQL_DATA = 'restSqlData';
......
......@@ -96,8 +96,9 @@ class Evaluate {
public function parseArray($tokenArray, array $skip = array(), &$debugStack = array()) {
$arr = array();
// In case there is an Element 'fillStoreVar', process that first.
if (!empty($tokenArray[FE_FILL_STORE_VAR]) && is_string($tokenArray[FE_FILL_STORE_VAR])) {
// In case there is an Element 'fillStoreVar', process that first (if not defined to skip).
$flagSkipFillStoreVar = (array_search(FE_FILL_STORE_VAR, $skip) !== false);
if (!$flagSkipFillStoreVar && !empty($tokenArray[FE_FILL_STORE_VAR]) && is_string($tokenArray[FE_FILL_STORE_VAR])) {
$arr = $this->parse($tokenArray[FE_FILL_STORE_VAR], ROW_REGULAR, 0, $debugStack);
if (!empty($arr)) {
......@@ -145,6 +146,10 @@ class Evaluate {
*/
public function parse($line, $sqlMode = ROW_IMPLODE_ALL, $recursion = 0, &$debugStack = array(), &$foundInStore = '') {
if ($line === '') {
return '';
}
$flagTokenReplaced = false;
if ($recursion > 4) {
......
......@@ -68,6 +68,35 @@ class FormAction {
}
/**
* Parse $fillStoreVar and if something is given, add it to STORE_VAR.
*
* @param string $fillStoreVar
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
* @throws \UserReportException
*/
private function feFillStoreVar($fillStoreVar) {
if ($fillStoreVar != '') {
$this->store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, FE_FILL_STORE_VAR, STORE_SYSTEM); // debug
$rows = $this->evaluate->parse($fillStoreVar, ROW_EXPECT_0_1);
if (is_array($rows)) {
$this->store->appendToStore($rows, STORE_VAR);
} else {
if (!empty($rows)) {
throw new \UserFormException(json_encode(
[ERROR_MESSAGE_TO_USER => "Invalid statement for 'fillStoreVar'.",
ERROR_MESSAGE_TO_DEVELOPER => $fillStoreVar]), ERROR_INVALID_OR_MISSING_PARAMETER);
}
}
}
}
/**
* @param integer $recordId
* @param array $feSpecAction
......@@ -106,20 +135,8 @@ class FormAction {
continue;
}
if (isset($fe[FE_FILL_STORE_VAR])) {
$this->store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, FE_FILL_STORE_VAR, STORE_SYSTEM); // debug
$rows = $this->evaluate->parse($fe[FE_FILL_STORE_VAR], ROW_EXPECT_0_1);
if (is_array($rows)) {
$this->store->appendToStore($rows, STORE_VAR);
} else {
if (!empty($rows)) {
throw new \UserFormException(json_encode(
[ERROR_MESSAGE_TO_USER => "Invalid statement for 'fillStoreVar'.",
ERROR_MESSAGE_TO_DEVELOPER => $fe[FE_FILL_STORE_VAR]]), ERROR_INVALID_OR_MISSING_PARAMETER);
}
}
$fe[FE_FILL_STORE_VAR] = ''; // do not process the same later on.
}
$this->feFillStoreVar($fe[FE_FILL_STORE_VAR] ?? '');
$fe[FE_FILL_STORE_VAR] = ''; // do not process the same later on.
// Process templateGroup action elements
if (isset($fe[FE_ID_CONTAINER]) && $fe[FE_ID_CONTAINER] > 0) {
......@@ -186,25 +203,19 @@ class FormAction {
$this->sqlValidate($fe);
// If given: fire a sqlBefore query
$this->evaluate->parse($fe[FE_SQL_BEFORE]);
if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) {
$this->doSendMail($fe);
} else {
$rcTmp = $this->doSlave($fe, $recordId);
switch ($rcTmp) {
case ACTION_ELEMENT_MODIFIED:
case ACTION_ELEMENT_DELETED:
$rc = $rcTmp;
break;
default:
break;
}
}
// If given: fire a $sqlAfter query
$this->evaluate->parse($fe[FE_SQL_AFTER]);
$rcTmp = $this->doSqlBeforeSlaveAfter($fe, $recordId, true);
switch ($rcTmp) {
case ACTION_ELEMENT_MODIFIED:
case ACTION_ELEMENT_DELETED:
$rc = $rcTmp;
break;
default:
break;
}
}
return $rc;
......@@ -338,6 +349,7 @@ class FormAction {
* @param array $fe
* @param int $recordId
*
* @param bool $flagFeAction indicates of the FE are of type 'native' or 'action'.
* @return int ACTION_ELEMENT_MODIFIED if there are potential(!) changes on the DB like INSERT / UPDATE,
* ACTION_ELEMENT_NO_CHANGE if nothing happened
* ACTION_ELEMENT_DELETED: if a record has been deleted
......@@ -346,63 +358,81 @@ class FormAction {
* @throws \UserFormException
* @throws \UserReportException
*/
private function doSlave(array $fe, $recordId) {
public function doSqlBeforeSlaveAfter(array $fe, $recordId, $flagFeAction) {
$rcStatus = ACTION_ELEMENT_NO_CHANGE;
// Get the slaveId
$slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
$this->feFillStoreVar($fe[FE_FILL_STORE_VAR] ?? '');
if ($slaveId === '' && $fe[FE_NAME] !== '') {
// if the current action element has the same name as a real master record column: take that value as an id
$slaveId = $this->store->getVar($fe[FE_NAME], STORE_RECORD);
}
// slaveId might be used in sqlBefore: get it first.
if (isset($fe[FE_SLAVE_ID])) {
// Get the slaveId
$slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
if ($slaveId === '' || $slaveId === false) {
$slaveId = 0;
}
if ($flagFeAction && $slaveId === '' && $fe[FE_NAME] !== '') {
// If the current action element has the same name as a real master record column: take that value as an id.
$slaveId = $this->store->getVar($fe[FE_NAME], STORE_RECORD);
}
// Store the slaveId: it's used and replaced in the update statement.
$this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
if ($slaveId === '' || $slaveId === false) {
$slaveId = 0;
}
$doInsert = ($slaveId == 0);
$doUpdate = ($slaveId != 0);
$doDelete = ($slaveId != 0) && $fe[FE_SQL_DELETE] != '';
// Store the slaveId: it's used and replaced in the update statement.
$this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
} else {
$flagHonor = isset($fe[FE_SQL_HONOR_FORM_ELEMENTS]) && $fe[FE_SQL_HONOR_FORM_ELEMENTS] != '';
if ($flagHonor) {
$filled = $this->checkFormElements($fe[FE_SQL_HONOR_FORM_ELEMENTS]);
$doInsert = $filled && $doInsert;
$doUpdate = $filled && $doUpdate;
$doDelete = !$filled && $doDelete;
if (false !== ($id = $this->store->getVar(VAR_SLAVE_ID, STORE_VAR))) {
$slaveId = $id;
$fe[FE_SLAVE_ID] = $id;
}
}
// Fire slave query
if ($doInsert) {
$slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
// Store the slaveId: might be used later
$this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
$rcStatus = ACTION_ELEMENT_MODIFIED;
}
// If given: fire a sqlBefore query
$this->evaluate->parse($fe[FE_SQL_BEFORE]);
if ($doUpdate) {
$this->evaluate->parse($fe[FE_SQL_UPDATE]);
$rcStatus = ACTION_ELEMENT_MODIFIED;
}
if (isset($fe[FE_SLAVE_ID])) {
$doInsert = ($slaveId == 0);
$doUpdate = ($slaveId != 0);
$doDelete = ($slaveId != 0) && !empty($fe[FE_SQL_DELETE]);
// Fire a delete query
if ($doDelete) {
$this->evaluate->parse($fe[FE_SQL_DELETE]);
$slaveId = 0;
$rcStatus = ACTION_ELEMENT_DELETED;
}
if (!empty($fe[FE_SQL_HONOR_FORM_ELEMENTS])) {
$filled = $this->checkFormElements($fe[FE_SQL_HONOR_FORM_ELEMENTS]);
$doInsert = $filled && $doInsert;
$doUpdate = $filled && $doUpdate;
$doDelete = !$filled && $doDelete;
}
// Check if there is a column with the same name as the 'action'-FormElement.
if (false !== $this->store->getVar($fe[FE_NAME], STORE_RECORD)) {
// After an insert or update, propagate the (new) slave id to the master record.
$this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
// Fire slave query
if ($doInsert) {
$slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
// Store the slaveId: might be used later
$this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
$rcStatus = ACTION_ELEMENT_MODIFIED;
}
if ($doUpdate) {
$this->evaluate->parse($fe[FE_SQL_UPDATE]);
$rcStatus = ACTION_ELEMENT_MODIFIED;
}
// Fire a delete query
if ($doDelete) {
$this->evaluate->parse($fe[FE_SQL_DELETE]);
$slaveId = 0;
$rcStatus = ACTION_ELEMENT_DELETED;
}
// Check if there is a column with the same name as the 'action'-FormElement.
if ($flagFeAction && false !== $this->store->getVar($fe[FE_NAME], STORE_RECORD)) {
// After an insert or update, propagate the (new) slave id to the master record.
$this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
}
}
// If given: fire a $sqlAfter query
$this->evaluate->parse($fe[FE_SQL_AFTER]);
return $rcStatus;
}
......
......@@ -50,12 +50,18 @@ class HelperFormElement {
*/
public static function formElementSetDefault(array $elements, array $formSpec) {
// Do not add FE_SLAVE_ID - it's necessary to detect if a value is given or not.
$default = [FE_SQL_BEFORE => '', FE_SQL_INSERT => '', FE_SQL_UPDATE => '', FE_SQL_DELETE => '', FE_SQL_AFTER => ''];
foreach ($elements AS $key => $element) {
$elements[$key][FE_TG_INDEX] = 0;
unset($elements[$key][FE_ADMIN_NOTE]);
// $elements[$key][FE_DATA_REFERENCE] = '';
$elements[$key] = array_merge($default, $elements[$key]);
}
return $elements;
}
......@@ -81,7 +87,7 @@ class HelperFormElement {
// Check if some of the exploded keys conflict with existing keys
$checkKeys = array_keys($arr);
foreach ($checkKeys AS $checkKey) {
if (isset($element[$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);
......@@ -293,7 +299,7 @@ class HelperFormElement {
*/
public static function initActionFormElement(array $fe) {
$list = [FE_TYPE, FE_SLAVE_ID, FE_SQL_VALIDATE, FE_SQL_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE,
$list = [FE_TYPE, FE_SQL_VALIDATE, FE_SLAVE_ID, FE_SQL_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE,
FE_SQL_AFTER, FE_EXPECT_RECORDS, FE_REQUIRED_LIST, FE_MESSAGE_FAIL, FE_SENDMAIL_TO, FE_SENDMAIL_CC,
FE_SENDMAIL_BCC, FE_SENDMAIL_FROM, FE_SENDMAIL_SUBJECT, FE_SENDMAIL_REPLY_TO, FE_SENDMAIL_FLAG_AUTO_SUBMIT,
FE_SENDMAIL_GR_ID, FE_SENDMAIL_X_ID, FE_SENDMAIL_X_ID2, FE_SENDMAIL_X_ID3, FE_SENDMAIL_BODY_MODE,
......
......@@ -230,11 +230,14 @@ class QuickFormQuery {
* @throws \CodeException
* @throws \DbException
* @throws \DownloadException
* @throws \UserFormException
* @throws \UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \UserFormException
* @throws \UserReportException
*/
public function process() {
$html = '';
......@@ -323,17 +326,21 @@ class QuickFormQuery {
* @throws \CodeException
* @throws \DbException
* @throws \DownloadException
* @throws \UserFormException
* @throws \UserReportException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \UserFormException
* @throws \UserReportException
*/
private function doForm($formMode) {
$data = '';
$foundInStore = '';
$flagApiStructureReGroup = true;
$formModeNew = '';
$build = null;
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT . STORE_ZERO);
$this->setParameterLanguageFieldName();
......@@ -1012,7 +1019,7 @@ class QuickFormQuery {
unset($form[F_PARAMETER]);
// Save specific elements to be expanded later.
$parseLater = OnArray::getArrayItems($form, [F_FORWARD_PAGE, FE_FILL_STORE_VAR, F_REST_SQL_LIST, F_REST_SQL_DATA]);
$parseLater = OnArray::getArrayItems($form, [F_FORWARD_PAGE, FE_FILL_STORE_VAR, F_REST_SQL_LIST, F_REST_SQL_DATA, F_MULTI_SQL]);
$form[FE_FILL_STORE_VAR] = '';
$form[F_FORWARD_PAGE] = '';
$form[F_REST_SQL_LIST] = '';
......@@ -1432,6 +1439,7 @@ class QuickFormQuery {
Support::setIfNotSet($formSpec, F_ENTER_AS_SUBMIT, $this->store->getVar(SYSTEM_ENTER_AS_SUBMIT, STORE_SYSTEM));