Commit 51ba996d authored by Carsten  Rose's avatar Carsten Rose
Browse files

Merge branch 'B7639_subrecordDragNDrop' into 'master'

B7639 subrecord drag n drop

See merge request !202
parents 6fa7350b eeb27b78
Pipeline #2753 passed with stages
in 5 minutes and 22 seconds
...@@ -3791,17 +3791,23 @@ The following parameters can be used in the `parameter` field to customize/activ ...@@ -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). * *orderColumn*: The dedicated order column in the specified dndTable (needs to match a column in the table definition).
Default is `ord`. 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 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: Tips:
* If you want to deactivate a drag-and-drop that QFQ automatically renders, set the `orderColumn` to a non-existing column. * 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. 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) * In order to evaluate the `sql1` query dynamically during a drag-and-drop event, the STORE_RECORD (with the current subrecord)
is loaded. Currently, SIP parameters and other variables are not supplied to be evaluated during a drag-and-drop event. is loaded.
This may be added later upon request. * 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 * 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. most likely a problem with evaluating the `sql1` query at runtime.
......
...@@ -2810,7 +2810,12 @@ abstract class AbstractBuildForm { ...@@ -2810,7 +2810,12 @@ abstract class AbstractBuildForm {
// Handle DragAndDrop // Handle DragAndDrop
$dndAttributes = ''; $dndAttributes = '';
if ($hasDragAndDrop) { 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; $numColumns = 0;
if (count($formElement[FE_SQL1]) > 0) { if (count($formElement[FE_SQL1]) > 0) {
$numColumns = count($formElement[FE_SQL1][0]) + (int)$flagDelete + (int)($flagNew || $flagEdit); $numColumns = count($formElement[FE_SQL1][0]) + (int)$flagDelete + (int)($flagNew || $flagEdit);
...@@ -2818,6 +2823,7 @@ abstract class AbstractBuildForm { ...@@ -2818,6 +2823,7 @@ abstract class AbstractBuildForm {
$dataDndApi = DND_SUBRECORD_ID . '=' . $formElement[FE_ID]; $dataDndApi = DND_SUBRECORD_ID . '=' . $formElement[FE_ID];
$dataDndApi .= '&' . DND_SUBRECORD_FORM_ID . '=' . $this->store->getVar('id', STORE_RECORD); $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_INTERVAL . '=' . $orderInterval;
$dataDndApi .= '&' . FE_ORDER_COLUMN . '=' . $orderColumn; $dataDndApi .= '&' . FE_ORDER_COLUMN . '=' . $orderColumn;
$dataDndApi .= '&' . FE_DND_TABLE . '=' . $dndTable; $dataDndApi .= '&' . FE_DND_TABLE . '=' . $dndTable;
......
...@@ -1317,6 +1317,7 @@ const FE_TYPE_PILL = 'pill'; ...@@ -1317,6 +1317,7 @@ const FE_TYPE_PILL = 'pill';
const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime. const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime.
const FE_ORDER_INTERVAL = 'orderInterval'; const FE_ORDER_INTERVAL = 'orderInterval';
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';
...@@ -1888,7 +1889,9 @@ const DND_COLUMN_ORD_NEW = 'ordNew'; ...@@ -1888,7 +1889,9 @@ const DND_COLUMN_ORD_NEW = 'ordNew';
const DND_DATA_DND_API = 'data-dnd-api'; 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_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_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_ORD_HTML_ID_PREFIX = 'qfq-dnd-ord-id-';
const DND_FORM_SIP_VALUES = '_sipForm';
// Application Test: SELENIUM // Application Test: SELENIUM
const ATTRIBUTE_DATA_REFERENCE = 'data-reference'; const ATTRIBUTE_DATA_REFERENCE = 'data-reference';
......
...@@ -272,6 +272,9 @@ class Evaluate { ...@@ -272,6 +272,9 @@ class Evaluate {
private function inlineDataDndApi($arrToken, $dbIndex, &$foundInStore) { private function inlineDataDndApi($arrToken, $dbIndex, &$foundInStore) {
$token = OnString::trimQuote(trim(implode(' ', $arrToken))); $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)) { if (empty($token)) {
throw new \UserReportException('Missing form name for "data-dnd-api"', ERROR_MISSING_FORM); throw new \UserReportException('Missing form name for "data-dnd-api"', ERROR_MISSING_FORM);
} }
...@@ -358,7 +361,7 @@ class Evaluate { ...@@ -358,7 +361,7 @@ class Evaluate {
$type = OnString::stripFirstCharIf('_', $arrToken[$countToken - 1]); $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' array_pop($arrToken); // remove 'as'
switch (strtolower($type)) { switch (strtolower($type)) {
......
...@@ -122,28 +122,35 @@ class DragAndDrop { ...@@ -122,28 +122,35 @@ class DragAndDrop {
$ord = $orderInterval; $ord = $orderInterval;
$ordDragOld = -1; $ordDragOld = -1;
$data = array(); $data = array();
$nameId = false;
$nameOrd = false;
// Reorder. Get index for 'drag' and 'hover' // Reorder. Get index for 'drag' and 'hover'
foreach ($rows as $key => $row) { foreach ($rows as $key => $row) {
// the dragged element: skip old position. if ($nameId === false) {
if ($row[DND_COLUMN_ID] == $dragId) { $nameId = $this->getFinalColumnName(DND_COLUMN_ID, $row);
$ordDragOld = $row[DND_COLUMN_ORD]; $nameOrd = $this->getFinalColumnName(DND_COLUMN_ORD, $row);
}
// The dragged element: skip old position.
if ($row[$nameId] == $dragId) {
$ordDragOld = $row[$nameOrd];
continue; continue;
} }
// the dragged element: new position. // The dragged element: new position.
if ($row[DND_COLUMN_ID] == $hoverId) { if ($row[$nameId] == $hoverId) {
switch ($setTo) { switch ($setTo) {
case DND_SET_TO_BEFORE: case DND_SET_TO_BEFORE:
$data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data); $data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data);
$ord += $orderInterval; $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; break;
case DND_SET_TO_AFTER: 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; $ord += $orderInterval;
$data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data); $data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data);
break; break;
...@@ -152,7 +159,7 @@ class DragAndDrop { ...@@ -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); throw new \CodeException(json_encode([ERROR_MESSAGE_TO_USER => 'Unknown "setTo" string', ERROR_MESSAGE_TO_DEVELOPER => "Token found: " . $setTo]), ERROR_UNKNOWN_TOKEN);
} }
} else { } 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; $ord += $orderInterval;
} }
...@@ -160,6 +167,25 @@ class DragAndDrop { ...@@ -160,6 +167,25 @@ class DragAndDrop {
return $data; 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 $tableName
* @param string $orderColumn * @param string $orderColumn
......
...@@ -1802,20 +1802,35 @@ class QuickFormQuery { ...@@ -1802,20 +1802,35 @@ class QuickFormQuery {
$json = ""; $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) { if ($dndSubrecordId > 0) {
// Subrecord DragAndDrop // Subrecord DragAndDrop
$subrecord = $this->dbArray[$this->dbIndexQfq]->sql(SQL_FORM_ELEMENT_BY_ID, ROW_REGULAR, [$dndSubrecordId]); $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); $recordId = $this->store::getVar(DND_SUBRECORD_FORM_ID, STORE_SIP . STORE_ZERO);
$this->store->fillStoreWithRecord('Form', $recordId, $this->dbArray[$this->dbIndexQfq]);
$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]); $dndOrderSql = $this->evaluate->parse($subrecord[0][FE_SQL1]);
foreach ($dndOrderSql as $i => $row) {
foreach ($row as $key => $value) { // Restore STORE_SIP
if (substr($key, 0, 1) === '_') { $this->store::setStore($save, STORE_SIP, true);
$dndOrderSql[$i][substr($key, 1)] = $value;
} // 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 = [ $dummyFormSpec = [
F_ORDER_INTERVAL => $this->store->getVar(FE_ORDER_INTERVAL, STORE_SIP . STORE_ZERO), F_ORDER_INTERVAL => $this->store->getVar(FE_ORDER_INTERVAL, STORE_SIP . STORE_ZERO),
......
...@@ -536,15 +536,21 @@ class Store { ...@@ -536,15 +536,21 @@ class Store {
/** /**
* Fills the STORE_SIP. Reads therefore specified SIP, decode the values and stores them in STORE_SIP. * Fills the STORE_SIP. Reads therefore specified SIP, decode the values and stores them in STORE_SIP.
* *
* @param bool|string $s
* @throws \CodeException * @throws \CodeException
* @throws \UserFormException * @throws \UserFormException
* @throws \UserReportException * @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 ($s !== false) {
// if session is given, copy values to store // if session is given, copy values to store
$param = self::$sip->getVarsFromSip($s); $param = self::$sip->getVarsFromSip($s);
......
Supports Markdown
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