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

First version Multi Form rendered via table

parent 1e336417
Pipeline #2395 passed with stages
in 2 minutes and 53 seconds
......@@ -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() {
......
......@@ -194,6 +194,112 @@ abstract class AbstractBuildForm {
abstract public function fillWrap();
/**
* @param $filter
* @param $modeCollectFe
* @param array $rcJson
* @return string
* @throws \CodeException
* @throws \DbException
* @throws \DownloadException
* @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 buildMultiForm($filter, $modeCollectFe, array $rcJson) {
$htmlElements = '';
$rcJson = array();
$parentRecords = $this->evaluate->parse($this->formSpec[F_MULTI_SQL], ROW_REGULAR);
// No rows: nothing to do.
if (empty($parentRecords)) {
//TODO Meldung konfigurierbar machen.
return 'No data';
}
// Check for 'id' or '_id' as column name
$idName = F_MULTI_COL_ID;
if (isset($parentRecords[0]['_' . F_MULTI_COL_ID])) {
$idName = '_' . F_MULTI_COL_ID;
}
// Check that an column 'id' is given
if (!isset($parentRecords[0][$idName])) {
throw new \UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Missing column "_' . F_MULTI_COL_ID . '"', ERROR_MESSAGE_TO_DEVELOPER => $this->formSpec[F_MULTI_SQL]]),
ERROR_INVALID_OR_MISSING_PARAMETER);
}
// Per row, iterate over all form elements
foreach ($parentRecords as $row) {
$this->store->setStore($row, STORE_PARENT_RECORD, true);
$jsonTmp = array();
$feTmp = $this->feSpecNative;
$leftColumns = $this->buildMultiFormLeftColumns($row);
$rightInputs = $this->elements($row[$idName], $filter, 0, $jsonTmp, $modeCollectFe,
false, STORE_USE_DEFAULT, FORM_LOAD, true);
$htmlElements .= Support::wrapTag('<tr>', $leftColumns . $rightInputs);
$this->feSpecNative = $feTmp;
$rcJson[] = $jsonTmp;
}
//TODO <th>: 'class' konfigurierbar machen. Spalten ausblendbar machen. 'link' Klasse unterstuetzen.
$tableHead = Support::wrapTag('<tr>', $this->buildMultiFormTableHead($parentRecords[0]));
//TODO <table>: 'class' konfigurierbar machen (tablesorter)
//TODO <thead>: 'class' konfigurierbar machen (sticky)
return '<table class="table"><thead>' . $tableHead . '</thead><tbody>' . $htmlElements . '</tbody></table>';
}
/**
* @param array $row
* @return string
*/
private function buildMultiFormLeftColumns(array $row) {
$line = '';
// Collect columns
foreach ($row as $key => $value) {
if (($key[0] ?? '') != '_') {
$line .= "<td>$value</td>";
}
}
return $line;
}
/**
* @param array $row
* @return string
*/
private function buildMultiFormTableHead(array $row) {
$line = '';
// Collect columns
foreach ($row as $key => $value) {
if (($key[0] ?? '') != '_') {
$line .= "<th>$key</th>";
}
}
// Collect label from FormElements
foreach ($this->feSpecNative as $fe) {
$line .= '<th>' . $fe[FE_LABEL] . '</th>';
}
return $line;
}
/**
* Builds complete 'form'. Depending of form specification, the layout will be 'plain' / 'table' / 'bootstrap'.
*
......@@ -206,17 +312,19 @@ abstract class AbstractBuildForm {
* @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()) {
$htmlHead = '';
$htmlTail = '';
$htmlT3vars = '';
$htmlSubrecords = '';
$htmlElements = '';
$json = array();
......@@ -240,32 +348,10 @@ abstract class AbstractBuildForm {
$filter = $this->getProcessFilter();
if ($this->formSpec[F_MULTI_SQL] !== '') {
$parentRecords = $this->evaluate->parse($this->formSpec[F_MULTI_SQL], ROW_REGULAR);
if (!empty($parentRecords)) {
// Check for 'id' or '_id' as column name
$idName = F_MULTI_COL_ID;
if (isset($parentRecords[0]['_' . F_MULTI_COL_ID])) {
$idName = '_' . F_MULTI_COL_ID;
}
// Check that an column 'id' is given
if (!isset($parentRecords[0][$idName])) {
throw new \UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Missing column "_' . F_MULTI_COL_ID . '"', ERROR_MESSAGE_TO_DEVELOPER => $this->formSpec[F_MULTI_SQL]]),
ERROR_INVALID_OR_MISSING_PARAMETER);
}
foreach ($parentRecords as $row) {
$this->store->setStore($row, STORE_PARENT_RECORD, true);
$jsonTmp = array();
$htmlElements .= $this->elements($row[$idName], $filter, 0, $jsonTmp, $modeCollectFe);
$json[] = $jsonTmp;
}
}
} else {
// Form type
if ($this->formSpec[F_MULTI_SQL] === '') {
// Regular Form
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
if (!($recordId == '' || is_numeric($recordId))) {
throw new \UserFormException(
......@@ -285,19 +371,24 @@ abstract class AbstractBuildForm {
// Via 'element-update'
$json[][API_ELEMENT_UPDATE][DIRTY_RECORD_HASH_MD5][API_ELEMENT_ATTRIBUTE]['value'] = $md5;
}
} else {
// Multi Form
if ($mode === FORM_LOAD) {
$htmlElements = $this->buildMultiForm($filter, $modeCollectFe, $json);
}
}
// <form>
if ($mode === FORM_LOAD) {
$htmlT3vars = $this->prepareT3VarsForSave();
$htmlTail = $this->tail();
$htmlSubrecords = $this->doSubrecords();
}
$htmlHidden = $this->buildAdditionalFormElements();
$htmlSip = $this->buildHiddenSip($json);
return ($mode === FORM_LOAD) ? $htmlHead . $htmlHidden . $htmlElements . $htmlSip . $htmlT3vars . $htmlTail . $htmlSubrecords : $json;
return ($mode === FORM_LOAD) ? $htmlHead . $htmlHidden . $htmlElements . $htmlSip . $htmlT3vars . $htmlTail : $json;
}
/**
......@@ -541,11 +632,14 @@ abstract class AbstractBuildForm {
* @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 processReportSyntax($value) {
......@@ -579,6 +673,7 @@ abstract class AbstractBuildForm {
return $value;
}
/**
* Process all FormElements in $this->feSpecNative: Collect and return all HTML code & JSON.
*
......@@ -590,20 +685,24 @@ abstract class AbstractBuildForm {
* @param bool $htmlElementNameIdZero
* @param string $storeUseDefault
* @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
* @param bool $flagMulti
*
* @return string
* @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 elements($recordId, $filter, $feIdContainer, array &$json,
$modeCollectFe = FLAG_DYNAMIC_UPDATE, $htmlElementNameIdZero = false,
$storeUseDefault = STORE_USE_DEFAULT, $mode = FORM_LOAD) {
$storeUseDefault = STORE_USE_DEFAULT, $mode = FORM_LOAD, $flagMulti = false) {
$html = '';
// The following 'FormElement.parameter' will never be used during load (fe.type='upload'). FE_PARAMETER has been already expanded.
......@@ -680,11 +779,10 @@ abstract class AbstractBuildForm {
// $fe[FE_MODE_SQL] = '';
// }
if ($flagOutput === true) {
if ($flagOutput === true && !$flagMulti) {
$this->fillWrapLabelInputNote($formElement[FE_BS_LABEL_COLUMNS], $formElement[FE_BS_INPUT_COLUMNS], $formElement[FE_BS_NOTE_COLUMNS]);
}
$value = '';
Support::setIfNotSet($formElement, FE_VALUE);
if (is_array($formElement[FE_VALUE])) {
......@@ -747,7 +845,7 @@ abstract class AbstractBuildForm {
if ($flagOutput) {
// debugStack as Tooltip
if ($this->showDebugInfoFlag) {
if ($this->showDebugInfoFlag && !$flagMulti) {
if (count($debugStack) > 0) {
$elementHtml .= Support::doTooltip($formElement[FE_HTML_ID] . HTML_ID_EXTENSION_TOOLTIP, implode("\n", $debugStack));
}
......@@ -759,8 +857,9 @@ abstract class AbstractBuildForm {
$elementHtml .= Support::wrapTag("<a class='hidden " . CLASS_FORM_ELEMENT_EDIT . "' href='$feEditUrl' $titleAttr>", $icon);
}
// Construct Marshaller Name: buildRow
$buildRowName = 'buildRow' . $this->buildRowName[$formElement[FE_TYPE]];
// Construct Marshaller Name: buildRow...
$tmpName = $flagMulti ? 'MultiElement' : $this->buildRowName[$formElement[FE_TYPE]];
$buildRowName = 'buildRow' . $tmpName;
$html .= $formElement[FE_HTML_BEFORE] . $this->$buildRowName($formElement, $elementHtml, $htmlFormElementName) . $formElement[FE_HTML_AFTER];
}
......@@ -774,6 +873,21 @@ abstract class AbstractBuildForm {
return $html;
}
/**
* @param array $formElement
* @param $elementHtml
*
* @return string
*/
public function buildRowMultiElement(array $formElement, $elementHtml) {
$before = ($formElement[FE_HTML_BEFORE] == '') ? '<td>' : $formElement[FE_HTML_BEFORE];
$after = ($formElement[FE_HTML_AFTER] == '') ? '</td>' : $formElement[FE_HTML_AFTER];
return $before . $elementHtml . $after;
}
/**
* Checks if LDAP search is requested.
* Yes: prepare configuration and fire the query.
......@@ -2884,7 +2998,7 @@ abstract class AbstractBuildForm {
$flagWidthLimit = true;
$control[SUBRECORD_COLUMN_MAX_LENGTH][$columnName] = SUBRECORD_COLUMN_DEFAULT_MAX_LENGTH;
// a) 'City@maxLength=40', b) 'Status@icon', c) 'Mailto@maxLength=80@nostrip'
// a) 'City|maxLength=40', b) 'Status|icon', c) 'Mailto@maxLength=80|nostrip'
$arr = KeyValueStringParser::parse($columnName, '=', '|', KVP_IF_VALUE_EMPTY_COPY_KEY);
foreach ($arr as $attribute => $value) {
switch ($attribute) {
......
......@@ -714,6 +714,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 +823,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 +890,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 +905,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()) {
......
......@@ -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,11 +326,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
*/
private function doForm($formMode) {
$data = '';
......@@ -1598,11 +1604,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
*/
private function doReport() {
......@@ -1700,11 +1709,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 saveForm() {
if ($this->store->getVar(REPORT_SAVE, STORE_SIP . STORE_ZERO) == '1') {
......@@ -1725,11 +1737,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 updateForm() {
......@@ -1745,11 +1760,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 dragAndDrop() {
......@@ -1884,11 +1902,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 delete() {
......@@ -2005,11 +2026,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 rest(array $restId, array $restForm) {
......
......@@ -822,7 +822,7 @@ thead.qfq-sticky th, thead.qfq-sticky td {
position: sticky;
top: 0;
background-color: white;
z-index: 9999;
z-index: 1000;
}
thead.qfq-sticky td {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment