From d390a6c26f4adf686759ba42de8c9b6f5e87d33a Mon Sep 17 00:00:00 2001 From: Oliver Hader <oliver@typo3.org> Date: Fri, 2 Jun 2017 13:55:40 +0200 Subject: [PATCH] [BUGFIX] Editing all records after deleting one throws exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If some record has been removed in the list module and after that the "edit marked" button is clicked, an exception is thrown concerning the just removed record. The list of records to be edited is now determined from the visible elements of the list. Resolves: #75966 Releases: master, 8.7 Change-Id: I2c77dd2d92cda038a1009c318a2ee6650bd82963 Reviewed-on: https://review.typo3.org/53031 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: Susanne Moog <susanne.moog@typo3.org> Reviewed-by: Jasmina Ließmann <code@frauliessmann.de> Tested-by: Jasmina Ließmann <code@frauliessmann.de> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- .../Classes/RecordList/DatabaseRecordList.php | 87 ++++++++++--------- .../Resources/Public/JavaScript/Recordlist.js | 68 +++++++++++++-- 2 files changed, 107 insertions(+), 48 deletions(-) diff --git a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php index c4a8a641ce6a..a6b986665365 100644 --- a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php +++ b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php @@ -915,19 +915,23 @@ class DatabaseRecordList extends AbstractDatabaseRecordList $id_orig = $this->id; $this->id = $row['pid']; } + + $tagAttributes = [ + 'class' => ['t3js-entity'], + 'data-table' => $table, + ]; + // Add special classes for first and last row - $rowSpecial = ''; if ($cc == 1 && $indent == 0) { - $rowSpecial .= ' firstcol'; + $tagAttributes['class'][] = 'firstcol'; } if ($cc == $this->totalRowCount || $cc == $this->iLimit) { - $rowSpecial .= ' lastcol'; + $tagAttributes['class'][] = 'lastcol'; } - - $row_bgColor = ' class="' . $rowSpecial . '"'; - // Overriding with versions background color if any: - $row_bgColor = $row['_CSSCLASS'] ? ' class="' . $row['_CSSCLASS'] . '"' : $row_bgColor; + if (!empty($row['_CSSCLASS'])) { + $tagAttributes['class'] = [$row['_CSSCLASS']]; + } // Incr. counter. $this->counter++; // The icon with link @@ -1033,7 +1037,18 @@ class DatabaseRecordList extends AbstractDatabaseRecordList ) { $theData['_l10nparent_'] = $row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']]; } - $rowOutput .= $this->addElement(1, $theIcon, $theData, $row_bgColor); + + $tagAttributes = array_map( + function ($attributeValue) { + if (is_array($attributeValue)) { + return implode(' ', $attributeValue); + } + return $attributeValue; + }, + $tagAttributes + ); + + $rowOutput .= $this->addElement(1, $theIcon, $theData, GeneralUtility::implodeAttributes($tagAttributes, true)); // Finally, return table row element: return $rowOutput; } @@ -1129,16 +1144,12 @@ class DatabaseRecordList extends AbstractDatabaseRecordList $spriteIcon = $this->iconFactory->getIcon('actions-edit-copy', Icon::SIZE_SMALL)->render(); $cells['copyMarked'] = $this->linkClipboardHeaderIcon($spriteIcon, $table, 'setCB', '', $lang->getLL('clip_selectMarked')); // The "edit marked" link: - $editIdList = implode(',', $currentIdList); - $editIdList = '\'+editList(' . GeneralUtility::quoteJSvalue($table) . ',' . GeneralUtility::quoteJSvalue($editIdList) . ')+\''; - $params = 'edit[' . $table . '][' . $editIdList . ']=edit'; - $onClick = BackendUtility::editOnClick('', '', -1); - $onClickArray = explode('?', $onClick, 2); - $lastElement = array_pop($onClickArray); - $onClickArray[] = $params . '&' . $lastElement; - $onClick = implode('?', $onClickArray); - $cells['edit'] = '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) . '" title="' - . htmlspecialchars($lang->getLL('clip_editMarked')) . '">' + $editUri = BackendUtility::getModuleUrl('record_edit') + . '&edit[' . $table . '][{entityIdentifiers:editList}]=edit' + . '&returnUrl={T3_THIS_LOCATION}'; + $cells['edit'] = '<a class="btn btn-default t3js-record-edit-multiple" href="#"' + . ' data-uri="' . htmlspecialchars($editUri) . '"' + . ' title="' . htmlspecialchars($lang->getLL('clip_editMarked')) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>'; // The "Delete marked" link: $cells['delete'] = $this->linkClipboardHeaderIcon( @@ -1215,19 +1226,17 @@ class DatabaseRecordList extends AbstractDatabaseRecordList } // If the table can be edited, add link for editing ALL SHOWN fields for all listed records: if ($permsEdit && $this->table && is_array($currentIdList)) { - $editIdList = implode(',', $currentIdList); + $entityIdentifiers = 'entityIdentifiers'; if ($this->clipNumPane()) { - $editIdList = '\'+editList(' . GeneralUtility::quoteJSvalue($table) . ',' . GeneralUtility::quoteJSvalue($editIdList) . ')+\''; + $entityIdentifiers .= ':editList'; } - $params = 'edit[' . $table . '][' . $editIdList . ']=edit&columnsOnly=' . implode(',', $this->fieldArray); - // we need to build this uri differently, otherwise GeneralUtility::quoteJSvalue messes up the edit list function - $onClick = BackendUtility::editOnClick('', '', -1); - $onClickArray = explode('?', $onClick, 2); - $lastElement = array_pop($onClickArray); - $onClickArray[] = $params . '&' . $lastElement; - $onClick = implode('?', $onClickArray); - $icon .= '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) - . '" title="' . htmlspecialchars($lang->getLL('editShownColumns')) . '">' + $editUri = BackendUtility::getModuleUrl('record_edit') + . '&edit[' . $table . '][{' . $entityIdentifiers . '}]=edit' + . '&columnsOnly=' . implode(',', $this->fieldArray) + . '&returnUrl={T3_THIS_LOCATION}'; + $icon .= '<a class="btn btn-default t3js-record-edit-multiple" href="#"' + . ' data-uri="' . htmlspecialchars($editUri) . '"' + . ' title="' . htmlspecialchars($lang->getLL('editShownColumns')) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>'; $icon = '<div class="btn-group" role="group">' . $icon . '</div>'; } @@ -1261,20 +1270,18 @@ class DatabaseRecordList extends AbstractDatabaseRecordList // If the table can be edited, add link for editing THIS field for all // listed records: if ($this->isEditable($table) && $permsEdit && $GLOBALS['TCA'][$table]['columns'][$fCol]) { - $editIdList = implode(',', $currentIdList); + $entityIdentifiers = 'entityIdentifiers'; if ($this->clipNumPane()) { - $editIdList = '\'+editList(' . GeneralUtility::quoteJSvalue($table) . ',' . GeneralUtility::quoteJSvalue($editIdList) . ')+\''; + $entityIdentifiers .= ':editList'; } - $params = 'edit[' . $table . '][' . $editIdList . ']=edit&columnsOnly=' . $fCol; - // we need to build this uri differently, otherwise GeneralUtility::quoteJSvalue messes up the edit list function - $onClick = BackendUtility::editOnClick('', '', -1); - $onClickArray = explode('?', $onClick, 2); - $lastElement = array_pop($onClickArray); - $onClickArray[] = $params . '&' . $lastElement; - $onClick = implode('?', $onClickArray); + $editUri = BackendUtility::getModuleUrl('record_edit') + . '&edit[' . $table . '][{' . $entityIdentifiers . '}]=edit' + . '&columnsOnly=' . $fCol + . '&returnUrl={T3_THIS_LOCATION}'; $iTitle = sprintf($lang->getLL('editThisColumn'), $sortLabel); - $theData[$fCol] .= '<a class="btn btn-default" href="#" onclick="' . htmlspecialchars($onClick) - . '" title="' . htmlspecialchars($iTitle) . '">' + $theData[$fCol] .= '<a class="btn btn-default t3js-record-edit-multiple" href="#"' + . ' data-uri="' . htmlspecialchars($editUri) . '"' + . ' title="' . htmlspecialchars($iTitle) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render() . '</a>'; } if (strlen($theData[$fCol]) > 0) { diff --git a/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js b/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js index d1a5dc179de8..e421d1ad5b43 100644 --- a/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js +++ b/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js @@ -19,23 +19,23 @@ define(['jquery', 'TYPO3/CMS/Backend/Storage', 'TYPO3/CMS/Backend/Icons'], funct 'use strict'; /** - * - * @type {{identifier: {toggle: string, icons: {collapse: string, expand: string}}}} + * @type {Object} * @exports TYPO3/CMS/Recordlist/Recordlist */ var Recordlist = { identifier: { + entity: '.t3js-entity', toggle: '.t3js-toggle-recordlist', icons: { collapse: 'actions-view-list-collapse', - expand: 'actions-view-list-expand' + expand: 'actions-view-list-expand', + editMultiple: '.t3js-record-edit-multiple' } } }; /** - * - * @param {Event} e + * @param {MouseEvent} e */ Recordlist.toggleClick = function(e) { e.preventDefault(); @@ -67,9 +67,61 @@ define(['jquery', 'TYPO3/CMS/Backend/Storage', 'TYPO3/CMS/Backend/Icons'], funct }); }; - $(function() { - $(document).on('click', Recordlist.identifier.toggle, Recordlist.toggleClick); - }); + /** + * Handles editing multiple records. + * + * @param {MouseEvent} event + */ + Recordlist.onEditMultiple = function(event) { + event.preventDefault(); + + var $tableContainer, tableName, entityIdentifiers, uri, patterns; + + $tableContainer = $(this).closest('[data-table]'); + if ($tableContainer.length === 0) { + return; + } + + uri = $(this).data('uri'); + tableName = $tableContainer.data('table'); + entityIdentifiers = $tableContainer + .find(Recordlist.identifier.entity + '[data-uid][data-table="' + tableName + '"]') + .map(function(index, entity) { return $(entity).data('uid'); }) + .toArray() + .join(','); + + patterns = uri.match(/{[^}]+}/g); + $.each(patterns, function(patternIndex, pattern) { + var expression = pattern.substr(1, pattern.length - 2); + var pipes = expression.split(':'); + var name = pipes.shift(); + var value; + + switch (name) { + case 'entityIdentifiers': + value = entityIdentifiers; + break; + case 'T3_THIS_LOCATION': + value = T3_THIS_LOCATION; + break; + default: + return; + } + + $.each(pipes, function(pipeIndex, pipe) { + if (pipe === 'editList') { + value = editList(tableName, value); + } + }); + + uri = uri.replace(pattern, value); + }); + + window.location.href = uri; + }; + + $(document).on('click', Recordlist.identifier.toggle, Recordlist.toggleClick); + $(document).on('click', Recordlist.identifier.icons.editMultiple, Recordlist.onEditMultiple); return Recordlist; }); -- GitLab