Skip to content

Commit

Permalink
[BUGFIX] Fix workspace pagination
Browse files Browse the repository at this point in the history
Since the workspace pagination took also child records
into account, which are however never being displayed
without their parent, the view broke as soon as such child
record would have been the first item on the next or the last
item on the previous page.

Therefore, the "total" count, used by the JavaScript pagination
to calculate the amount of pages, does not longer take any
child records into account.

The data array, containing the records for the requested page,
is now filled until the limit of records is reached with only parent
records. Therefore, the actual record items count can differ from
the proposed limit, since the child records are added without
increasing the corresponding counter.

The same functionality does also take place on calculation of the
offset value. This means, the offset provided by the pagination is
e.g. 30, but the backend side offset must be 30 + the amount of
child records in the previous 30 parent records.

Another special case is now also handled properly: When the last
record to be added is a parent record, having child records, they
need to be fetched recursively as well. Otherwise this would lead
to the same bug as before, since such record would then be the
first record on the next page, which does not work.

Resolves: #93645
Releases: master, 10.4
Change-Id: I742486fc2bf2a47d6a4f19bd4fae51b21d498074
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/68519
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
  • Loading branch information
o-ba authored and lolli42 committed Mar 24, 2021
1 parent 8fe7baa commit f8e4b44
Showing 1 changed file with 105 additions and 13 deletions.
118 changes: 105 additions & 13 deletions typo3/sysext/workspaces/Classes/Service/GridDataService.php
Expand Up @@ -121,7 +121,10 @@ public function generateGridListFromVersions($versions, $parameter, $currentWork
$data = [];
$data['data'] = [];
$this->generateDataArray($versions, $filterTxt);
$data['total'] = count($this->dataArray);
// Only count parent records for pagination
$data['total'] = count(array_filter($this->dataArray, static function ($element) {
return (int)($element[self::GridColumn_CollectionLevel] ?? 0) === 0;
}));
$data['data'] = $this->getDataArray($start, $limit);
return $data;
}
Expand Down Expand Up @@ -304,23 +307,14 @@ protected function resolveDataArrayDependencies()
*/
protected function getDataArray($start, $limit)
{
$dataArrayPart = [];
$dataArrayCount = count($this->dataArray);
$start = $this->calculateStartWithCollections($start);
$end = ($start + $limit < $dataArrayCount ? $start + $limit : $dataArrayCount);

// Ensure that there are numerical indexes
$this->dataArray = array_values($this->dataArray);
for ($i = $start; $i < $end; $i++) {
$dataArrayPart[] = $this->dataArray[$i];
}

// Ensure that collections are not cut for the pagination
if (!empty($this->dataArray[$i][self::GridColumn_Collection])) {
$collectionIdentifier = $this->dataArray[$i][self::GridColumn_Collection];
for ($i = $i + 1; $i < $dataArrayCount && $collectionIdentifier === $this->dataArray[$i][self::GridColumn_Collection]; $i++) {
$dataArrayPart[] = $this->dataArray[$i];
}
}
// Fill the data array part
$dataArrayPart = $this->fillDataArrayPart($start, $end);

// Trigger a PSR-14 event
$event = new GetVersionedDataEvent($this, $this->dataArray, $start, $limit, $dataArrayPart);
Expand Down Expand Up @@ -664,6 +658,104 @@ protected function getSystemLanguageValue($id, $pageId, $key)
return $value;
}

/**
* Calculate the "real" start value by also taking collection children into account
*
* @param int $start
* @return int
*/
protected function calculateStartWithCollections(int $start): int
{
// The recordsCount is the real record items count, while the
// parentRecordsCount only takes the parent records into account
$recordsCount = $parentRecordsCount = 0;
while ($parentRecordsCount < $start) {
// Loop over the dataArray until we found enough parent records
$item = $this->dataArray[$recordsCount];
if (($item[self::GridColumn_CollectionLevel] ?? 0) === 0) {
// In case the current parent record is the last one ($start is reached),
// ensure its collection children are counted as well.
if (($parentRecordsCount + 1) === $start && (int)($item[self::GridColumn_Collection] ?? 0) !== 0) {
// By not providing the third parameter, we only count the collection children recursively
$this->addCollectionChildrenRecursive($item, $recordsCount);
}
// Only increase the parent records count in case $item is a parent record
$parentRecordsCount++;
}
// Always increase the record items count
$recordsCount++;
}

return $recordsCount;
}

/**
* Fill the data array part until enough parent records are found ($end is reached).
* Also adds the related collection children, but without increasing the corresponding
* parent records count.
*
* @param int $start
* @param int $end
* @return array
*/
private function fillDataArrayPart(int $start, int $end): array
{
// Initialize empty data array part
$dataArrayPart = [];
// The recordsCount is the real record items count, while the
// parentRecordsCount only takes the parent records into account.
$itemsCount = $parentRecordsCount = $start;
while ($parentRecordsCount < $end) {
// Loop over the dataArray until we found enough parent records
$item = $this->dataArray[$itemsCount];
// Add the item to the $dataArrayPart
$dataArrayPart[] = $item;
if (($item[self::GridColumn_CollectionLevel] ?? 0) === 0) {
// In case the current parent record is the last one ($end is reached),
// ensure its collection children are added as well.
if (($parentRecordsCount + 1) === $end && (int)($item[self::GridColumn_Collection] ?? 0) !== 0) {
// Add collection children recursively
$this->addCollectionChildrenRecursive($item, $itemsCount, $dataArrayPart);
}
// Only increase the parent records count in case $item is a parent record
$parentRecordsCount++;
}
// Always increase the record items count
$itemsCount++;
}
return $dataArrayPart;
}

/**
* Add collection children to the data array part recursively
*
* @param array $item
* @param int $recordsCount
* @param array $dataArrayPart
*/
protected function addCollectionChildrenRecursive(array $item, int &$recordsCount, array &$dataArrayPart = []): void
{
$collectionParent = (string)$item[self::GridColumn_CollectionCurrent];
foreach ($this->dataArray as $element) {
if ((string)($element[self::GridColumn_CollectionParent] ?? '') === $collectionParent) {
// Increase the "real" record items count
$recordsCount++;
// Fetch the children from the dataArray using the current record items
// count. This is possible since the dataArray is already sorted.
$child = $this->dataArray[$recordsCount];
// In case $dataArrayPart is not given, just count the item
if ($dataArrayPart !== []) {
// Add the children
$dataArrayPart[] = $child;
}
// In case the $child is also a collection, add it's children as well (recursively)
if ((int)($child[self::GridColumn_Collection] ?? 0) !== 0) {
$this->addCollectionChildrenRecursive($child, $recordsCount, $dataArrayPart);
}
}
}
}

/**
* Gets all available system languages.
*
Expand Down

0 comments on commit f8e4b44

Please sign in to comment.