[BUGFIX] Editing all records after deleting one throws exception 31/53031/9
authorOliver Hader <oliver@typo3.org>
Fri, 2 Jun 2017 11:55:40 +0000 (13:55 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Fri, 2 Jun 2017 13:28:01 +0000 (15:28 +0200)
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>
typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js

index c4a8a64..a6b9866 100644 (file)
@@ -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) {
index d1a5dc1..e421d1a 100644 (file)
@@ -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;
 });