Commit ff67e516 authored by Andreas Fernandez's avatar Andreas Fernandez
Browse files

Revert "[FEATURE] Add button to select all records"

This reverts commit 7ef32708.

The original patch does not work well with a huge record set as the
processing time per request is too high. Also, the recover button
for a single record does not work anymore, as the action is not
called properly via AJAX.

Resolves: #84700
Related: #81310
Releases: master
Change-Id: I4ba5d2982ef57c62d03f76df3ae5bf2ca3c9bdf8
Reviewed-on: https://review.typo3.org/56637


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
parent 2bb767c0
.. include:: ../../Includes.txt
==================================================================
Feature: #81310 - Add button to select all records in EXT:recycler
==================================================================
See :issue:`81310`
Description
===========
A new button to select all records from all pages in EXT:recycler has been added.
.. index:: Backend, ext:recycler
......@@ -50,10 +50,12 @@ class DeletedRecordsController
* Transforms the rows for the deleted records
*
* @param array $deletedRowsArray Array with table as key and array with all deleted rows
* @param int $totalDeleted Number of deleted records in total
* @return array JSON array
*/
public function transform($deletedRowsArray)
public function transform($deletedRowsArray, $totalDeleted)
{
$total = 0;
$jsonArray = [
'rows' => []
];
......@@ -63,6 +65,7 @@ class DeletedRecordsController
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
foreach ($deletedRowsArray as $table => $rows) {
$total += count($deletedRowsArray[$table]);
foreach ($rows as $row) {
$pageTitle = $this->getPageTitle((int)$row['pid']);
$backendUserName = $this->getBackendUser((int)$row[$GLOBALS['TCA'][$table]['ctrl']['cruser_id']]);
......@@ -89,29 +92,7 @@ class DeletedRecordsController
}
}
}
return $jsonArray;
}
/**
* Transforms the rows for the deleted records
*
* @param array $deletedRowsArray Array with table as key and array with all deleted rows
* @return array JSON array
*/
public function transformSmallAddTotal(array $deletedRowsArray): array
{
$jsonArray = [];
$total = 0;
if (is_array($deletedRowsArray)) {
foreach ($deletedRowsArray as $table => $rows) {
foreach ($rows as $row) {
$key = $table . ':' . $row['uid'];
$jsonArray['rows'][$key] = 1;
$total++;
}
}
}
$jsonArray['total'] = $total;
$jsonArray['total'] = $totalDeleted;
return $jsonArray;
}
......
......@@ -54,7 +54,7 @@ class RecyclerAjaxController
$this->conf['filterTxt'] = GeneralUtility::_GP('filterTxt') ? GeneralUtility::_GP('filterTxt') : '';
$this->conf['startUid'] = GeneralUtility::_GP('startUid') ? (int)GeneralUtility::_GP('startUid') : 0;
$this->conf['depth'] = GeneralUtility::_GP('depth') ? (int)GeneralUtility::_GP('depth') : 0;
$this->conf['records'] = json_decode(GeneralUtility::_GP('records') ? GeneralUtility::_GP('records') : '[]', true);
$this->conf['records'] = GeneralUtility::_GP('records') ? GeneralUtility::_GP('records') : null;
$this->conf['recursive'] = GeneralUtility::_GP('recursive') ? (bool)GeneralUtility::_GP('recursive') : false;
}
......@@ -94,13 +94,11 @@ class RecyclerAjaxController
$deletedRowsArray = $model->getDeletedRows();
$model = GeneralUtility::makeInstance(DeletedRecords::class);
$model->loadData($this->conf['startUid'], $this->conf['table'], $this->conf['depth'], null, $this->conf['filterTxt']);
$deletedRowsArrayAll = $model->getDeletedRows();
$totalDeleted = $model->getTotalCount($this->conf['startUid'], $this->conf['table'], $this->conf['depth'], $this->conf['filterTxt']);
/* @var $controller DeletedRecordsController */
$controller = GeneralUtility::makeInstance(DeletedRecordsController::class);
$recordsArray = $controller->transform($deletedRowsArray);
$recordsArrayAll = $controller->transformSmallAddTotal($deletedRowsArrayAll);
$recordsArray = $controller->transform($deletedRowsArray, $totalDeleted);
$modTS = $this->getBackendUser()->getTSConfig('mod.recycler');
$allowDelete = $this->getBackendUser()->isAdmin() ? true : (bool)$modTS['properties']['allowDelete'];
......@@ -111,8 +109,7 @@ class RecyclerAjaxController
$view->assign('total', $recordsArray['total']);
$content = [
'rows' => $view->render(),
'totalItems' => $recordsArrayAll['total'],
'allTheRows' => $recordsArrayAll['rows']
'totalItems' => $recordsArray['total']
];
break;
case 'undoRecords':
......
......@@ -52,7 +52,7 @@ class RecyclerModuleController
/**
* @var int
*/
protected $recordsPageLimit = 25;
protected $recordsPageLimit = 50;
/**
* @var int
......
......@@ -18,18 +18,6 @@
<trans-unit id="button.delete">
<source>Delete</source>
</trans-unit>
<trans-unit id="button.selectall">
<source>Select all records from all pages</source>
</trans-unit>
<trans-unit id="button.deselectall">
<source>Deselect all</source>
</trans-unit>
<trans-unit id="button.selectallamount">
<source>Select all records ({0}) from all pages</source>
</trans-unit>
<trans-unit id="button.selectallamountrest">
<source>Select the rest of the records ({0}) from all pages</source>
</trans-unit>
<trans-unit id="button.deleteselected">
<source>Delete {0} records</source>
</trans-unit>
......
......@@ -44,44 +44,21 @@
</tbody>
</table>
</div>
<div class="progress progress-bar-notice alert-loading" style="display: none">
<div class="t3js-progressbar progress-bar progress-bar-striped active m-3"
role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"
style="width: 100%; height:25px; ">
<span class="sr-only">Loading...</span>
</div>
</div>
<div>
<div>
<button class="btn btn-default disabled" data-action="selectall">
<core:icon identifier="actions-document-select" />
<span class="text">
<f:translate key="LLL:EXT:recycler/Resources/Private/Language/locallang.xlf:button.selectall"/>
</span>
</button>
<button class="btn btn-default disabled" data-action="deselectall">
<button class="btn btn-default disabled" data-action="massundo">
<core:icon identifier="actions-edit-undo" />
<span class="text">
<f:translate key="button.undo" />
</span>
</button>
<f:if condition="{allowDelete}">
<button class="btn btn-default disabled" data-action="massdelete">
<core:icon identifier="actions-edit-delete" />
<span class="text">
<f:translate key="LLL:EXT:recycler/Resources/Private/Language/locallang.xlf:button.deselectall"/>
<f:translate key="button.delete" />
</span>
</button>
</div>
<div>
<button class="btn btn-default disabled" data-action="massundo">
<core:icon identifier="actions-edit-undo" />
<span class="text">
<f:translate key="button.undo" />
</span>
</button>
<f:if condition="{allowDelete}">
<button class="btn btn-default disabled" data-action="massdelete">
<core:icon identifier="actions-edit-delete" />
<span class="text">
<f:translate key="button.delete" />
</span>
</button>
</f:if>
</div>
</f:if>
</div>
<nav>
</nav>
......
......@@ -41,10 +41,7 @@ define(['jquery',
reloadAction: 'a[data-action=reload]',
massUndo: 'button[data-action=massundo]',
massDelete: 'button[data-action=massdelete]',
selectAll: 'button[data-action=selectall]',
deselectAll: 'button[data-action=deselectall]',
toggleAll: '.t3js-toggle-all',
progressBar: '#recycler-index .progress.progress-bar-notice.alert-loading'
toggleAll: '.t3js-toggle-all'
},
elements: {}, // filled in getElements()
paging: {
......@@ -73,10 +70,7 @@ define(['jquery',
$reloadAction: $(Recycler.identifiers.reloadAction),
$massUndo: $(Recycler.identifiers.massUndo),
$massDelete: $(Recycler.identifiers.massDelete),
$selectAll: $(Recycler.identifiers.selectAll),
$deselectAll: $(Recycler.identifiers.deselectAll),
$toggleAll: $(Recycler.identifiers.toggleAll),
$progressBar: $(Recycler.identifiers.progressBar)
$toggleAll: $(Recycler.identifiers.toggleAll)
};
};
......@@ -114,7 +108,6 @@ define(['jquery',
// changing "depth"
Recycler.elements.$depthSelector.on('change', function() {
$.when(Recycler.loadAvailableTables()).done(function() {
Recycler.clearMarked();
Recycler.loadDeletedElements();
});
});
......@@ -122,7 +115,6 @@ define(['jquery',
// changing "table"
Recycler.elements.$tableSelector.on('change', function() {
Recycler.paging.currentPage = 1;
Recycler.clearMarked();
Recycler.loadDeletedElements();
});
......@@ -167,7 +159,6 @@ define(['jquery',
if (reload) {
Recycler.loadDeletedElements();
Recycler.loadMarked();
}
});
......@@ -198,31 +189,14 @@ define(['jquery',
});
// checkboxes in the table
Recycler.elements.$recyclerTable.on('change', 'tr input[type=checkbox]', Recycler.handleCheckboxSelects);
Recycler.elements.$toggleAll.on('click', Recycler.toggleAll);
Recycler.elements.$massUndo.on('click', function() {
if (!$(this).hasClass('disabled')) {
Recycler.undoRecord();
}
Recycler.elements.$toggleAll.on('click', function() {
Recycler.allToggled = !Recycler.allToggled;
$('input[type="checkbox"]').prop('checked', Recycler.allToggled).trigger('change');
});
Recycler.elements.$massDelete.on('click', function() {
if (!$(this).hasClass('disabled')) {
Recycler.deleteRecord();
}
});
Recycler.elements.$selectAll.on('click', function() {
if (!$(this).hasClass('disabled')) {
Recycler.selectAll();
}
});
Recycler.elements.$deselectAll.on('click', function() {
if (!$(this).hasClass('disabled')) {
Recycler.deselectAll();
}
Recycler.elements.$recyclerTable.on('change', 'tr input[type=checkbox]', Recycler.handleCheckboxSelects);
});
Recycler.elements.$massUndo.on('click', Recycler.undoRecord);
Recycler.elements.$massDelete.on('click', Recycler.deleteRecord);
};
/**
......@@ -252,40 +226,46 @@ define(['jquery',
table = $tr.data('table'),
uid = $tr.data('uid'),
record = table + ':' + uid;
if ($checkbox.prop('checked')) {
if (!Recycler.markedRecordsForMassAction[record]) {
Recycler.addRecord(record);
$tr.addClass('warning');
}
Recycler.markedRecordsForMassAction.push(record);
$tr.addClass('warning');
} else {
if (!!Recycler.markedRecordsForMassAction[record]) {
Recycler.subtractRecord(record);
$tr.removeClass('warning');
var index = Recycler.markedRecordsForMassAction.indexOf(record);
if (index > -1) {
Recycler.markedRecordsForMassAction.splice(index, 1);
}
$tr.removeClass('warning');
}
Recycler.selectAllRefresh();
};
if (Recycler.markedRecordsForMassAction.length > 0) {
if (Recycler.elements.$massUndo.hasClass('disabled')) {
Recycler.elements.$massUndo.removeClass('disabled');
}
if (Recycler.elements.$massDelete.hasClass('disabled')) {
Recycler.elements.$massDelete.removeClass('disabled');
}
var btnTextUndo = Recycler.createMessage(TYPO3.lang['button.undoselected'], [Recycler.markedRecordsForMassAction.length]),
btnTextDelete = Recycler.createMessage(TYPO3.lang['button.deleteselected'], [Recycler.markedRecordsForMassAction.length]);
Recycler.elements.$massUndo.find('span.text').text(btnTextUndo);
Recycler.elements.$massDelete.find('span.text').text(btnTextDelete);
} else {
Recycler.resetMassActionButtons();
}
};
/**
* Resets the mass action state
*/
Recycler.resetMassActionButtons = function() {
if (!!Recycler.markedRecordsForMassAction) {
Recycler.persistMarked(Recycler.markedRecordsForMassAction);
} else {
Recycler.markedRecordsForMassAction = {};
}
Recycler.markedRecordsForMassAction = [];
Recycler.elements.$massUndo.addClass('disabled');
Recycler.elements.$massUndo.find('span.text').text(TYPO3.lang['button.undo']);
Recycler.elements.$massDelete.addClass('disabled');
Recycler.elements.$massDelete.find('span.text').text(TYPO3.lang['button.delete']);
Recycler.elements.$selectAll.addClass('disabled');
Recycler.elements.$selectAll.find('span.text').text(TYPO3.lang['button.selectall']);
Recycler.elements.$deselectAll.addClass('disabled');
Recycler.elements.$deselectAll.find('span.text').text(TYPO3.lang['button.deselectall']);
};
/**
......@@ -306,7 +286,6 @@ define(['jquery',
NProgress.start();
Recycler.elements.$tableSelector.val('');
Recycler.paging.currentPage = 1;
Recycler.markedRecordsCounter = 0;
},
success: function(data) {
var tables = [];
......@@ -357,24 +336,13 @@ define(['jquery',
beforeSend: function() {
NProgress.start();
Recycler.resetMassActionButtons();
Recycler.selectAllDataShort = [];
Recycler.currentDataCount = 0;
/** if there are any checkboxes and corresponding buttons, hide them while new content arrives */
Recycler.showLoading();
},
success: function(data) {
var totalItems = data.totalItems;
Recycler.elements.$tableBody.html(data.rows);
Recycler.buildPaginator(totalItems);
Recycler.currentDataCount = totalItems;
Recycler.selectAllDataShort = data.allTheRows;
Recycler.buildPaginator(data.totalItems);
},
complete: function() {
NProgress.done();
Recycler.selectAllRefresh();
}
});
};
......@@ -386,12 +354,13 @@ define(['jquery',
if (TYPO3.settings.Recycler.deleteDisable) {
return;
}
var $tr = $(this).parents('tr'),
isMassDelete = $tr.parent().prop('tagName') !== 'TBODY'; // deleteRecord() was invoked by the mass delete button
var records, message;
if (isMassDelete) {
records = Recycler.returnProperMarkedArray();
records = Recycler.markedRecordsForMassAction;
message = TYPO3.lang['modal.massdelete.text'];
} else {
var uid = $tr.data('uid'),
......@@ -413,11 +382,7 @@ define(['jquery',
text: TYPO3.lang['button.delete'],
btnClass: 'btn-danger',
trigger: function() {
Recycler.callAjaxAction(
'delete',
typeof records === 'object' ? records : [records],
isMassDelete
)
Recycler.callAjaxAction('delete', typeof records === 'object' ? records : [records], isMassDelete);
}
}
]);
......@@ -432,7 +397,7 @@ define(['jquery',
var records, messageText, recoverPages;
if (isMassUndo) {
records = Recycler.returnProperMarkedArray();
records = Recycler.markedRecordsForMassAction;
messageText = TYPO3.lang['modal.massundo.text'];
recoverPages = true;
} else {
......@@ -462,7 +427,7 @@ define(['jquery',
)
);
} else {
$message = $('<div />').text(messageText);
$message = messageText;
}
Modal.confirm(TYPO3.lang['modal.undo.header'], $message, Severity.ok, [
......@@ -476,13 +441,7 @@ define(['jquery',
text: TYPO3.lang['button.undo'],
btnClass: 'btn-success',
trigger: function() {
Recycler.callAjaxAction(
'undo',
// typeof records === 'object' ? records : [records],
records,
isMassUndo,
$message.find('#undo-recursive').prop('checked') ? 1 : 0
);
Recycler.callAjaxAction('undo', typeof records === 'object' ? records : [records], isMassUndo, $message.find('#undo-recursive').prop('checked') ? 1 : 0);
}
}
]);
......@@ -497,12 +456,10 @@ define(['jquery',
*/
Recycler.callAjaxAction = function(action, records, isMassAction, recursive) {
var data = {
records: JSON.stringify(records),
records: records,
action: ''
},
reloadPageTree = false,
oldCount = Recycler.markedRecordsCounter,
error = 0;
reloadPageTree = false;
if (action === 'undo') {
data.action = 'undoRecords';
data.recursive = recursive ? 1 : 0;
......@@ -513,23 +470,18 @@ define(['jquery',
return;
}
$.ajax({
url: TYPO3.settings.ajaxUrls['recycler'],
dataType: 'json',
data: data,
method: 'POST',
beforeSend: function() {
NProgress.start();
/** if there are any checkboxes and corresponding buttons, hide them while new content arrives */
Recycler.showLoading();
},
success: function(data) {
if (data.success) {
Notification.success('', data.message);
} else {
Notification.error('', data.message);
error = 1;
}
// reload recycler data
......@@ -537,22 +489,16 @@ define(['jquery',
$.when(Recycler.loadAvailableTables()).done(function() {
Recycler.loadDeletedElements();
if (isMassAction && !error) {
Recycler.clearMarked();
} else {
if (!error) {
if (!!Recycler.markedRecordsForMassAction[records]) {
Recycler.subtractRecord(records);
oldCount--;
}
Recycler.markedRecordsCounter = oldCount;
}
if (isMassAction) {
Recycler.resetMassActionButtons();
}
if (reloadPageTree) {
Recycler.refreshPageTree();
}
// Reset toggle state
Recycler.allToggled = false;
});
},
complete: function() {
......@@ -646,235 +592,6 @@ define(['jquery',
Recycler.elements.$paginator.html($ul);
};
/**
* Select all records
*/
Recycler.selectAll = function() {
if (Recycler.currentDataCount > 0) {
Recycler.elements.$selectAll.addClass('disabled');
Recycler.markedRecordsForMassAction = {};
Recycler.markedRecordsCounter = Recycler.currentDataCount;
Recycler.markedRecordsForMassAction = $.extend(true, {}, Recycler.selectAllDataShort);
Recycler.elements.$selectAll.removeClass('disabled');
Recycler.selectAllRefresh();
}
};
/**
* Deselect all records and return everything to clean state
*/
Recycler.deselectAll = function() {
Recycler.elements.$deselectAll.addClass('disabled');
Recycler.clearMarked();
Recycler.resetMassActionButtons();
Recycler.selectAllRefresh();
Recycler.elements.$selectAll.removeClass('disabled');
};
/**
* Adjusts mass action buttons to user's action
*/
Recycler.selectAllRefresh = function() {
var totalItems, btnTextSelectAll = '',
btnDisabledArr = ['$deselectAll', '$massUndo', '$massDelete'];
Recycler.hideLoading();
Recycler.persistMarked(Recycler.markedRecordsForMassAction);
Recycler.refreshCheckboxes();
/** if any checkboxes are checked change mass action buttons state */
if (Recycler.markedRecordsCounter > 0) {
var recordsLength = Recycler.markedRecordsCounter,
btnTextDelete = Recycler.createMessage(TYPO3.lang['button.deleteselected'], [recordsLength]),
btnTextUndo = Recycler.createMessage(TYPO3.lang['button.undoselected'], [recordsLength]);
/** if there are any records unselected show the amount */
if (!!Recycler.currentDataCount && ( (Recycler.currentDataCount-Recycler.markedRecordsCounter) > 0 )) {
if (Recycler.markedRecordsCounter === 0) {
btnTextSelectAll = Recycler.createMessage(TYPO3.lang['button.selectallamount'], [Recycler.currentDataCount]);
} else {
var rest = Recycler.currentDataCount - Recycler.markedRecordsCounter;
btnTextSelectAll = Recycler.createMessage(TYPO3.lang['button.selectallamountrest'], [rest]);
}
} else {
btnTextSelectAll = Recycler.createMessage(TYPO3.lang['button.selectall'])
}
/** if total amount of records from ajax is bigger than amount of currently selected records enable selectall */
if (!!Recycler.currentDataCount && (Recycler.currentDataCount > Recycler.markedRecordsCounter)) {
if (Recycler.elements.$selectAll.hasClass('disabled')) {
Recycler.elements.$selectAll.removeClass('disabled');
}
} else {
Recycler.elements.$selectAll.addClass('disabled');
}
/** enable mass action buttons (without selectall)*/
$.each(btnDisabledArr, (function(index, value) {
if (Recycler.elements[value].hasClass('disabled')) {
Recycler.elements[value].removeClass('disabled');
}
}));
Recycler.elements.$selectAll.find('span.text').text(btnTextSelectAll);