diff --git a/Documentation/Manual.rst b/Documentation/Manual.rst index ae45b6ad697182664c4e367ba7af154e36a61bb1..af5a64dc44a353708d2e6e3d4017c66a81478502 100644 --- a/Documentation/Manual.rst +++ b/Documentation/Manual.rst @@ -3791,17 +3791,23 @@ The following parameters can be used in the `parameter` field to customize/activ * *orderColumn*: The dedicated order column in the specified dndTable (needs to match a column in the table definition). Default is `ord`. -If `dndTable` is an actual table with a column `orderColumn`, QFQ automatically applies drag-and-drop logic +If `dndTable` is a table with a column `orderColumn`, QFQ automatically applies drag-and-drop logic to the rendered subrecord. It does so by using the subrecord field *sql1*. The `sql1` query should -therefore include both a column id (or _id) and ord (or _ord). +include a column `id` (or `_id`) and a column `ord` (or `_ord`). E.g.:: + + FE.sql1 = {{!SELECT p.id AS _id, p.ord AS _ord, p.name FROM Person WHERE p.email!='' ORDER BY p.ord}} Tips: * If you want to deactivate a drag-and-drop that QFQ automatically renders, set the `orderColumn` to a non-existing column. E.g., `orderColumn = nonExistingColumn`. This will deactivate drag-and-drop. -* In order to evaluate the `sql1` query dynamically during a drag-and-drop event, the fill store R (with the current form) - is loaded. Currently, SIP parameters and other variables are not supplied to be evaluated during a drag-and-drop event. - This may be added later upon request. +* In order to evaluate the `sql1` query dynamically during a drag-and-drop event, the STORE_RECORD (with the current subrecord) + is loaded. +* The stores STORE_RECORD, STORE_SIP and STORE_SYSTEM are supported during a drag-and-drop event and can be used in FE.sql1 query. + + * STORE_SIP: SIP values on form load + * STORE_RECORD: values of the current record loaded in the form. + * If the subrecord is rendered with drag-and-drop active, but the order is not affected upon reload, there is most likely a problem with evaluating the `sql1` query at runtime. diff --git a/extension/Classes/Core/AbstractBuildForm.php b/extension/Classes/Core/AbstractBuildForm.php index 21a14d1abe948f77209e2ff8d65144b1be7fb829..5a1468a0a86d86e61c17f42022425889e37da2f6 100644 --- a/extension/Classes/Core/AbstractBuildForm.php +++ b/extension/Classes/Core/AbstractBuildForm.php @@ -2810,7 +2810,12 @@ abstract class AbstractBuildForm { // Handle DragAndDrop $dndAttributes = ''; if ($hasDragAndDrop) { - $orderInterval = 10; + + $orderInterval = $formElement[FE_ORDER_INTERVAL] ?? FE_ORDER_INTERVAL_DEFAULT; + if ($orderInterval == '' || !is_numeric($orderInterval)) { + $orderInterval = FE_ORDER_INTERVAL_DEFAULT; + } + $numColumns = 0; if (count($formElement[FE_SQL1]) > 0) { $numColumns = count($formElement[FE_SQL1][0]) + (int)$flagDelete + (int)($flagNew || $flagEdit); @@ -2818,6 +2823,7 @@ abstract class AbstractBuildForm { $dataDndApi = DND_SUBRECORD_ID . '=' . $formElement[FE_ID]; $dataDndApi .= '&' . DND_SUBRECORD_FORM_ID . '=' . $this->store->getVar('id', STORE_RECORD); + $dataDndApi .= '&' . DND_SUBRECORD_FORM_TABLE . '=' . $this->formSpec[F_TABLE_NAME]; $dataDndApi .= '&' . FE_ORDER_INTERVAL . '=' . $orderInterval; $dataDndApi .= '&' . FE_ORDER_COLUMN . '=' . $orderColumn; $dataDndApi .= '&' . FE_DND_TABLE . '=' . $dndTable; diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php index 26805b6dc7da0303bdf359dd928e0b37b122b165..73d3cb6dcce3628cbe23e003fa5319cb7a93fd04 100644 --- a/extension/Classes/Core/Constants.php +++ b/extension/Classes/Core/Constants.php @@ -1317,6 +1317,7 @@ const FE_TYPE_PILL = 'pill'; const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime. const FE_ORDER_INTERVAL = 'orderInterval'; +const FE_ORDER_INTERVAL_DEFAULT = '10'; const FE_ORDER_COLUMN = 'orderColumn'; const FE_DND_TABLE = 'dndTable'; @@ -1888,7 +1889,9 @@ const DND_COLUMN_ORD_NEW = 'ordNew'; const DND_DATA_DND_API = 'data-dnd-api'; const DND_SUBRECORD_ID = 'dnd-subrecord-id'; // Internal qualifier used to communicate with dnd api for subrecord dnd const DND_SUBRECORD_FORM_ID = 'dnd-subrecord-form-id'; +const DND_SUBRECORD_FORM_TABLE = 'dnd-subrecord-form-table'; const DND_ORD_HTML_ID_PREFIX = 'qfq-dnd-ord-id-'; +const DND_FORM_SIP_VALUES = '_sipForm'; // Application Test: SELENIUM const ATTRIBUTE_DATA_REFERENCE = 'data-reference'; diff --git a/extension/Classes/Core/Evaluate.php b/extension/Classes/Core/Evaluate.php index 633cccbfee927c622731278dd5916b61bfc6fc9c..e28e23fb21823045062468c6bb1f11ebb3389d48 100644 --- a/extension/Classes/Core/Evaluate.php +++ b/extension/Classes/Core/Evaluate.php @@ -272,6 +272,9 @@ class Evaluate { private function inlineDataDndApi($arrToken, $dbIndex, &$foundInStore) { $token = OnString::trimQuote(trim(implode(' ', $arrToken))); + + # Include current SIP store, to fetch SIP parameter later. + $token .= '&' . DND_FORM_SIP_VALUES . '=' . $this->store::getVar(SIP_SIP, STORE_SIP); if (empty($token)) { throw new \UserReportException('Missing form name for "data-dnd-api"', ERROR_MISSING_FORM); } @@ -358,7 +361,7 @@ class Evaluate { $type = OnString::stripFirstCharIf('_', $arrToken[$countToken - 1]); - array_pop($arrToken); // remove 'link' | 'data-dnd-api' | 'ablesorter-view-saver' + array_pop($arrToken); // remove 'link' | 'data-dnd-api' | 'tablesorter-view-saver' array_pop($arrToken); // remove 'as' switch (strtolower($type)) { diff --git a/extension/Classes/Core/Form/DragAndDrop.php b/extension/Classes/Core/Form/DragAndDrop.php index 19aea5eb1d6fe48d006b8c5a6804676e0460779d..12abb01775d7e1b63134bf5e34895e822b00ffbb 100644 --- a/extension/Classes/Core/Form/DragAndDrop.php +++ b/extension/Classes/Core/Form/DragAndDrop.php @@ -122,28 +122,35 @@ class DragAndDrop { $ord = $orderInterval; $ordDragOld = -1; $data = array(); + $nameId = false; + $nameOrd = false; // Reorder. Get index for 'drag' and 'hover' foreach ($rows as $key => $row) { - // the dragged element: skip old position. - if ($row[DND_COLUMN_ID] == $dragId) { - $ordDragOld = $row[DND_COLUMN_ORD]; + if ($nameId === false) { + $nameId = $this->getFinalColumnName(DND_COLUMN_ID, $row); + $nameOrd = $this->getFinalColumnName(DND_COLUMN_ORD, $row); + } + + // The dragged element: skip old position. + if ($row[$nameId] == $dragId) { + $ordDragOld = $row[$nameOrd]; continue; } - // the dragged element: new position. - if ($row[DND_COLUMN_ID] == $hoverId) { + // The dragged element: new position. + if ($row[$nameId] == $hoverId) { switch ($setTo) { case DND_SET_TO_BEFORE: $data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data); $ord += $orderInterval; - $data = $this->setNewOrder($tableName, $orderColumn, $row[DND_COLUMN_ID], $row[DND_COLUMN_ORD], $ord, $data); + $data = $this->setNewOrder($tableName, $orderColumn, $row[$nameId], $row[$nameOrd], $ord, $data); break; case DND_SET_TO_AFTER: - $data = $this->setNewOrder($tableName, $orderColumn, $row[DND_COLUMN_ID], $row[DND_COLUMN_ORD], $ord, $data); + $data = $this->setNewOrder($tableName, $orderColumn, $row[$nameId], $row[$nameOrd], $ord, $data); $ord += $orderInterval; $data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data); break; @@ -152,7 +159,7 @@ class DragAndDrop { throw new \CodeException(json_encode([ERROR_MESSAGE_TO_USER => 'Unknown "setTo" string', ERROR_MESSAGE_TO_DEVELOPER => "Token found: " . $setTo]), ERROR_UNKNOWN_TOKEN); } } else { - $data = $this->setNewOrder($tableName, $orderColumn, $row[DND_COLUMN_ID], $row[DND_COLUMN_ORD], $ord, $data); + $data = $this->setNewOrder($tableName, $orderColumn, $row[$nameId], $row[$nameOrd], $ord, $data); } $ord += $orderInterval; } @@ -160,6 +167,25 @@ class DragAndDrop { return $data; } + /** + * Check if there is a column called '_' . $name. If yes, return that name, else return $name. + * If none is found, throw an exception. + * + * @param $name + * @param $row + * @return string + * @throws \UserFormException + */ + private function getFinalColumnName($name, $row) { + + if (!isset($row[$name]) && !isset($row['_' . $name])) { + throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Missing column '$name' or '_$name'", ERROR_MESSAGE_TO_DEVELOPER => "Check your DND SQL statement"]), + ERROR_MISSING_REQUIRED_PARAMETER); + } + + return isset($row['_' . $name]) ? '_' . $name : $name; + } + /** * @param string $tableName * @param string $orderColumn diff --git a/extension/Classes/Core/QuickFormQuery.php b/extension/Classes/Core/QuickFormQuery.php index 54d3151d2d13bb2700c5a31a4257da69f1affd4d..9a26bca13e766aab1b9690edb3a4f5883c1c9d5b 100644 --- a/extension/Classes/Core/QuickFormQuery.php +++ b/extension/Classes/Core/QuickFormQuery.php @@ -1802,20 +1802,35 @@ class QuickFormQuery { $json = ""; - $dndSubrecordId = $this->store->getVar(DND_SUBRECORD_ID, STORE_SIP . STORE_CLIENT . STORE_ZERO); + $dndSubrecordId = $this->store::getVar(DND_SUBRECORD_ID, STORE_SIP . STORE_CLIENT . STORE_ZERO); if ($dndSubrecordId > 0) { // Subrecord DragAndDrop $subrecord = $this->dbArray[$this->dbIndexQfq]->sql(SQL_FORM_ELEMENT_BY_ID, ROW_REGULAR, [$dndSubrecordId]); - $recordId = $this->store->getVar(DND_SUBRECORD_FORM_ID, STORE_SIP . STORE_ZERO); - $this->store->fillStoreWithRecord('Form', $recordId, $this->dbArray[$this->dbIndexQfq]); + $recordId = $this->store::getVar(DND_SUBRECORD_FORM_ID, STORE_SIP . STORE_ZERO); + + $tableName = $this->store::getVar(DND_SUBRECORD_FORM_TABLE, STORE_SIP); +// $this->store->fillStoreWithRecord('form', $recordId, $this->dbArray[$this->dbIndexQfq]); + $this->store->fillStoreWithRecord($tableName, $recordId, $this->dbArray[$this->dbIndexQfq]); + + $formSip = $this->store::getVar(DND_FORM_SIP_VALUES, STORE_SIP); + // Backup STORE_SIP + $save = $this->store::getStore(STORE_SIP); + // Fake STORE_SIP + $this->store::fillStoreSip($formSip); + // Fire query, which might use SIP Vars $dndOrderSql = $this->evaluate->parse($subrecord[0][FE_SQL1]); - foreach ($dndOrderSql as $i => $row) { - foreach ($row as $key => $value) { - if (substr($key, 0, 1) === '_') { - $dndOrderSql[$i][substr($key, 1)] = $value; - } - } - } + + // Restore STORE_SIP + $this->store::setStore($save, STORE_SIP, true); + + // Copy all '_...' columns to '...' (without the dash) +// foreach ($dndOrderSql as $i => $row) { +// foreach ($row as $key => $value) { +// if (substr($key, 0, 1) === '_') { +// $dndOrderSql[$i][substr($key, 1)] = $value; +// } +// } +// } $dummyFormSpec = [ F_ORDER_INTERVAL => $this->store->getVar(FE_ORDER_INTERVAL, STORE_SIP . STORE_ZERO), diff --git a/extension/Classes/Core/Store/Store.php b/extension/Classes/Core/Store/Store.php index aa4d732f4c0d1444a8654b44d5e2a6b64bbe3b69..7e6454092e48d22c38e352ab589ac476739fdf45 100644 --- a/extension/Classes/Core/Store/Store.php +++ b/extension/Classes/Core/Store/Store.php @@ -536,15 +536,21 @@ class Store { /** * Fills the STORE_SIP. Reads therefore specified SIP, decode the values and stores them in STORE_SIP. * + * @param bool|string $s * @throws \CodeException * @throws \UserFormException * @throws \UserReportException */ - private static function fillStoreSip() { + public static function fillStoreSip($s = false) { - self::$sip = new Sip(self::$phpUnit); + if (self::$sip === null) { + self::$sip = new Sip(self::$phpUnit); + } + + if ($s === false) { + $s = self::getVar(CLIENT_SIP, STORE_CLIENT); + } - $s = self::getVar(CLIENT_SIP, STORE_CLIENT); if ($s !== false) { // if session is given, copy values to store $param = self::$sip->getVarsFromSip($s);