Bug #70154
openExtbase 'sorting' l10nmode
0%
Description
When I use in the TCA ctrl-section of an item
'sortby' => 'sprting'
the translated items are on the same position as the default ones in the backend. But in the frontend the order is wrong.
When I add
protected $defaultOrderings = array( 'sorting' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING, );
in the repository the sorting worsk correct in the default language. But in another language the sorting is wrong. The problem is, that the sorting in the translated language is another number than in the default language, but it has to be the same because you can't move only the translated language. I hope you can understand the problem.
Files
Updated by Benni Mack about 9 years ago
- Target version changed from 6.2.15 to next-patchlevel
Updated by Mathias Schreiber about 9 years ago
- Target version deleted (
next-patchlevel)
Updated by Stephan Brun over 8 years ago
Same problem here. We have translated sys_categories. The default sorting is not present, although there are sorting arrows in the backend. After fix the defaultOrderings in the repository, the localized records get messed up, because they have their own sorting value This doesn't make sense because it is not possible to sort them separatly in the backend.
Tested with 6.2.19 and 7.6.4
At this time we have a workaround with hooks on processDataMap (update,insert,move) which override the sorting on translated records with the sorting of the default language. Not a nice solution, but it works.
Updated by Ralf Merz over 8 years ago
Hi,
we have the same problem here. No possibility to change the sorting value of localized records. If we move the (default) records, the values of the localizations do not get updated. So the order in the frontend is wrong.
@Stephan: Could you please provide the code of your workaround?
Thanks and regards
Ralf
Updated by Ralf Merz about 8 years ago
Hi,
are there any news about the sorting problem?
Thank you very much and regards,
Ralf
Updated by Mona Muzaffar over 7 years ago
- Related to Bug #72988: losing Localization when moving elements (Typo 7.6.2) added
Updated by Oliver Weiss almost 7 years ago
- TYPO3 Version changed from 6.2 to 8
Same here, still not fixed with 8.7
Updated by Julian Hofmann over 6 years ago
Manual workaround:
Disable "Localization view" and sort translation records below theirs parent.
Repeat this each time you're adding/moving a record.
Updated by Riccardo De Contardi about 6 years ago
- Category changed from Extbase to Extbase + l10n
Updated by Marc Hirdes over 4 years ago
- TYPO3 Version changed from 8 to 9
This problem still exists.
Updated by Markus Gerdes about 4 years ago
- Related to Bug #92093: Sorting of records in frontend with sys_language all (-1) and normal added
Updated by David Bruchmann about 4 years ago
- Related to Bug #85377: Wrong sorting in list module by fields which are relations added
Updated by David Bruchmann about 4 years ago
Added #85377 even if it's related to the backend.
Concerning frontend it might be annoying or cumbersome having to write own methods in the repository but in an own extension you had full control.
Updated by Marc Hirdes about 4 years ago
If it's not my extension like news it's really annoying. A fix would be to sync the sorting in the backend.
Updated by David Bruchmann about 4 years ago
I think it should be possible to override the repo by x-classing, but the code of news is quite complex, so it won't be an easy task.
Updated by Marc Hirdes about 4 years ago
The solution should be provided by default/core. If I don't have the possibility to change the sorting of a translated record, the sorting number should be the same as in the default language. Otherwise the sorting of the translated record should be ignored.
The different sorting number of a translated record makes no sence.
Updated by Marc Hirdes about 4 years ago
it should works like l10n_mode=exclude for the sorting field. This works if you show the field in the record and press save. But if you use the arrows or cut and paste in the list module it works not.
Updated by Marc Hirdes almost 4 years ago
A good hook is also missing. When using
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass']['cstemplates_move'] = \Clickstorm\CsTemplates\Hook\DataHandlerHook::class;
Then you have the use the whole code from
\TYPO3\CMS\Core\DataHandling\DataHandler->moveRecord_raw()
/** * @param string $table * @param int $uid * @param int $destPid * @param array $propArr * @param array $moveRec * @param int $resolvedPid * @param bool $recordWasMoved * @param DataHandler $dataHandler */ public function moveRecord($table, $uid, $destPid, $propArr, $moveRec, $resolvedPid, &$recordWasMoved, $dataHandler) { if (!BackendUtility::isTableLocalizable($table)) { return; } $languageFieldColumn = $GLOBALS['TCA'][$table]['ctrl']['languageField']; $transOrigPointerFieldColumn = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']; $sortColumn = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ?? ''; $fields = [$languageFieldColumn, $transOrigPointerFieldColumn]; $record = BackendUtility::getRecord($table,$uid,implode(',', $fields)); if($record[$languageFieldColumn] > 0 && $record[$transOrigPointerFieldColumn] > 0) { $recordOfDefaultLanguage = BackendUtility::getRecord($table,$record[$transOrigPointerFieldColumn],$sortColumn); $origDestPid = $destPid; // This is the actual pid of the moving to destination $resolvedPid = $dataHandler->resolvePid($table, $destPid); // Checking if the pid is negative, but no sorting row is defined. In that case, find the correct pid. Basically this check make the error message 4-13 meaning less... But you can always remove this check if you prefer the error instead of a no-good action (which is to move the record to its own page...) // $destPid>=0 because we must correct pid in case of versioning "page" types. if (($destPid < 0 && !$sortColumn) || $destPid >= 0) { $destPid = $resolvedPid; } // Prepare user defined objects (if any) for hooks which extend this function: $hookObjectsArr = []; foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] ?? [] as $className) { $hookObjectsArr[] = GeneralUtility::makeInstance($className); } // Timestamp field: $updateFields = []; if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) { $updateFields[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME']; } // Check if this is a translation of a page, if so then it just needs to be kept "sorting" in sync // Usually called from moveL10nOverlayRecords() if ($table === 'pages') { // Just return with $recordWasMoved == false -- so pages will be handled by the core return; } // Insert as first element on page (where uid = $destPid) if ($destPid >= 0) { if ($table !== 'pages' || $dataHandler->destNotInsideSelf($destPid, $uid)) { // Clear cache before moving [$parentUid] = BackendUtility::getTSCpid($table, $uid, ''); $dataHandler->registerRecordIdForPageCacheClearing($table, $uid, $parentUid); // Setting PID $updateFields['pid'] = $destPid; // Table is sorted by 'sortby', use sorting of record with default language if ($sortColumn && !isset($updateFields[$sortColumn])) { $updateFields[$sortColumn] = $recordOfDefaultLanguage['sorting']; } // Check for child records that have also to be moved $dataHandler->moveRecord_procFields($table, $uid, $destPid); // Create query for update: GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable($table) ->update($table, $updateFields, ['uid' => (int)$uid]); // Check for the localizations of that element // Dont use moveL10nOverlayRecords cause we handle the movement of L10nOverlayRecords //$dataHandler->moveL10nOverlayRecords($table, $uid, $destPid, $destPid); // Call post processing hooks: foreach ($hookObjectsArr as $hookObj) { if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) { $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $dataHandler); } } $this->getRecordHistoryStore($dataHandler)->moveRecord($table, $uid, ['oldPageId' => $propArr['pid'], 'newPageId' => $destPid, 'oldData' => $propArr, 'newData' => $updateFields]); if ($dataHandler->enableLogging) { // Logging... $oldpagePropArr = $dataHandler->getRecordProperties('pages', $propArr['pid']); if ($destPid != $propArr['pid']) { // Logged to old page $newPropArr = $dataHandler->getRecordProperties($table, $uid); $newpagePropArr = $dataHandler->getRecordProperties('pages', $destPid); $dataHandler->log($table, $uid, 4, $destPid, 0, 'Moved record \'%s\' (%s) to page \'%s\' (%s)', 2, [$propArr['header'], $table . ':' . $uid, $newpagePropArr['header'], $newPropArr['pid']], $propArr['pid']); // Logged to new page $dataHandler->log($table, $uid, 4, $destPid, 0, 'Moved record \'%s\' (%s) from page \'%s\' (%s)', 3, [$propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']], $destPid); } else { // Logged to new page $dataHandler->log($table, $uid, 4, $destPid, 0, 'Moved record \'%s\' (%s) on page \'%s\' (%s)', 4, [$propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']], $destPid); } } // Clear cache after moving $dataHandler->registerRecordIdForPageCacheClearing($table, $uid); $dataHandler->fixUniqueInPid($table, $uid); $this->fixUniqueInSite($table, (int)$uid, $dataHandler); if ($table === 'pages') { $this->fixUniqueInSiteForSubpages((int)$uid, $dataHandler); } // Set recordWasMoved, so record wont be moved after this hook again $recordWasMoved = true; } elseif ($dataHandler->enableLogging) { $destPropArr = $dataHandler->getRecordProperties('pages', $destPid); $dataHandler->log($table, $uid, 4, 0, 1, 'Attempt to move page \'%s\' (%s) to inside of its own rootline (at page \'%s\' (%s))', 10, [$propArr['header'], $uid, $destPropArr['header'], $destPid], $propArr['pid']); } } elseif ($sortColumn) { // Put after another record // Table is being sorted // Save the position to which the original record is requested to be moved $originalRecordDestinationPid = $destPid; $sortInfo = $dataHandler->getSortNumber($table, $uid, $destPid); // Setting the destPid to the new pid of the record. $destPid = $sortInfo['pid']; // If not an array, there was an error (which is already logged) if (is_array($sortInfo)) { if ($table !== 'pages' || $dataHandler->destNotInsideSelf($destPid, $uid)) { // clear cache before moving $dataHandler->registerRecordIdForPageCacheClearing($table, $uid); // We now update the pid and sortnumber (if not set for page translations) $updateFields['pid'] = $destPid; if (!isset($updateFields[$sortColumn])) { // Get updated record of default language and set sorting number $recordOfDefaultLanguage = BackendUtility::getRecord($table,$record[$transOrigPointerFieldColumn],$sortColumn); $updateFields[$sortColumn] = $recordOfDefaultLanguage['sorting']; } // Check for child records that have also to be moved $dataHandler->moveRecord_procFields($table, $uid, $destPid); // Create query for update: GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable($table) ->update($table, $updateFields, ['uid' => (int)$uid]); // Check for the localizations of that element // Dont use moveL10nOverlayRecords cause we handle the movement of L10nOverlayRecords //$dataHandler->moveL10nOverlayRecords($table, $uid, $destPid, $originalRecordDestinationPid); // Call post processing hooks: foreach ($hookObjectsArr as $hookObj) { if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) { $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $dataHandler); } } $this->getRecordHistoryStore($dataHandler)->moveRecord($table, $uid, ['oldPageId' => $propArr['pid'], 'newPageId' => $destPid, 'oldData' => $propArr, 'newData' => $updateFields]); if ($dataHandler->enableLogging) { // Logging... $oldpagePropArr = $dataHandler->getRecordProperties('pages', $propArr['pid']); if ($destPid != $propArr['pid']) { // Logged to old page $newPropArr = $dataHandler->getRecordProperties($table, $uid); $newpagePropArr = $dataHandler->getRecordProperties('pages', $destPid); $dataHandler->log($table, $uid, 4, 0, 0, 'Moved record \'%s\' (%s) to page \'%s\' (%s)', 2, [$propArr['header'], $table . ':' . $uid, $newpagePropArr['header'], $newPropArr['pid']], $propArr['pid']); // Logged to old page $dataHandler->log($table, $uid, 4, 0, 0, 'Moved record \'%s\' (%s) from page \'%s\' (%s)', 3, [$propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']], $destPid); } else { // Logged to old page $dataHandler->log($table, $uid, 4, 0, 0, 'Moved record \'%s\' (%s) on page \'%s\' (%s)', 4, [$propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']], $destPid); } } // Clear cache after moving $dataHandler->registerRecordIdForPageCacheClearing($table, $uid); $dataHandler->fixUniqueInPid($table, $uid); $this->fixUniqueInSite($table, (int)$uid, $dataHandler); if ($table === 'pages') { $this->fixUniqueInSiteForSubpages((int)$uid, $dataHandler); } // Set recordWasMoved, so record wont be moved after this hook again $recordWasMoved = true; } elseif ($dataHandler->enableLogging) { $destPropArr = $dataHandler->getRecordProperties('pages', $destPid); $dataHandler->log($table, $uid, 4, 0, 1, 'Attempt to move page \'%s\' (%s) to inside of its own rootline (at page \'%s\' (%s))', 10, [$propArr['header'], $uid, $destPropArr['header'], $destPid], $propArr['pid']); } } else { $dataHandler->log($table, $uid, 4, 0, 1, 'Attempt to move record \'%s\' (%s) to after another record, although the table has no sorting row.', 13, [$propArr['header'], $table . ':' . $uid], $propArr['event_pid']); } } }
This also not works for new records.
Better would be to provide a Hook in
\TYPO3\CMS\Core\DataHandling\DataHandler->getSortNumber()
Best would be to fix this problem in the core.
Updated by Marc Hirdes almost 4 years ago
Ok, current workaround
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['cstemplates_move_fix'] = \Clickstorm\CsTemplates\Hook\DataHandlerHook::class;
<?php declare(strict_types=1); namespace Clickstorm\CsTemplates\Hook; /* * This file is part of the "cs_templates" Extension for TYPO3 CMS. * * For the full copyright and license information, please read the * LICENSE.txt file that was distributed with this source code. * * (c) 2020 clickstorm GmbH */ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\Utility\GeneralUtility; /** * use until https://forge.typo3.org/issues/70154 is solved * * Class DataHandlerHook */ class DataHandlerHook { /** * fix sorting of translated records, sync them with default * * @param string $command * @param string $table * @param int $uid * @param $value * @param DataHandler $pObj * @param bool $pasteUpdate * @param array $pasteDatamap */ public function processCmdmap_postProcess(string $command, string $table, int $uid, $value, DataHandler &$pObj, bool $pasteUpdate, array $pasteDatamap) { // first check if table is localizable, languageField and transOrigPointerField are set if(BackendUtility::isTableLocalizable($table)) { $sortColumn = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ?? ''; $languageFieldColumn = $GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? ''; $transOrigPointerFieldColumn = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? ''; $deleteColumn = $GLOBALS['TCA'][$table]['ctrl']['delete'] ?? ''; // if table has a sort column if($sortColumn) { // get pid of record $record = BackendUtility::getRecord($table,$uid,'pid'); // if record is not deleted if($record) { $pid = $record['pid']; // fetch all uids of translated records with sorting number of default language $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); // include hidden records $queryBuilder->getRestrictions()->removeAll(); $queryBuilder ->select('l10n.uid', 'default.' . $sortColumn) ->from($table, 'l10n') ->leftJoin( 'l10n', $table, 'default', $queryBuilder->expr()->eq( 'default.uid', $queryBuilder->quoteIdentifier('l10n.' . $transOrigPointerFieldColumn) ) ) ->where($queryBuilder->expr()->eq('l10n.pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT))) ->andWhere($queryBuilder->expr()->gt('l10n.' . $languageFieldColumn, 0)) ->andWhere($queryBuilder->expr()->gt('l10n.' . $transOrigPointerFieldColumn, 0)); if ($deleteColumn) { $queryBuilder->andWhere($queryBuilder->expr()->eq('l10n.' . $deleteColumn, 0)); } $l10nSortingValues = $queryBuilder->execute()->fetchAll(); // update all translated records of current pid with sorting number of default language foreach ($l10nSortingValues as $l10nSortingValue) { $queryBuilder->resetQueryParts(); $queryBuilder ->update($table) ->set($sortColumn, $l10nSortingValue[$sortColumn]) ->where( $queryBuilder->expr()->eq('uid', $l10nSortingValue['uid']) ) ->execute(); } } } } } }
Updated by Marc Hirdes almost 4 years ago
The workaround above works well for me. Maybe provide sth. like this in core? It makes no sence to have a different sorting value for translated records in a connected mode.
Updated by Anonymous over 3 years ago
Marc Hirdes wrote in #note-23:
The workaround above works well for me. Maybe provide sth. like this in core? It makes no sence to have a different sorting value for translated records in a connected mode.
You need to provide/submit a patch yourself and get some reviewers. The core team is too busy with other - far more important - topics. Sarcasm intended.
Updated by Riccardo De Contardi over 3 years ago
- Related to Bug #86059: Wrong sorting for localized entries in page module with 8.7.16 and higher added