Commit 5b8092c2 authored by Benni Mack's avatar Benni Mack
Browse files

[TASK] Cleanup workspaces backend AJAX calls

The AJAX endpoints for the workspaces backend module
on the PHP side still are 1:1 like the good old ExtJS times.

In order to abstract and separate concerns in this area,
and make the AJAX-based endpoints true PSR-7 compliant
requests and responses, some cleanups are necessary.

In this first step, the AbstractHandler is removed,
as some methods can be removed or simplified in the
child classes.

Resolves: #84937
Releases: master
Change-Id: If390d4b51d19059b18f579691371669c27a337d4
Reviewed-on: https://review.typo3.org/56872


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 736f74f2
......@@ -67,7 +67,6 @@ class AjaxDispatcher
{
$tmp = new \stdClass();
$tmp->action = $call->action;
$tmp->debug = '';
$tmp->method = $call->method;
$tmp->result = $responseFromMethod;
$tmp->tid = $call->tid;
......
<?php
namespace TYPO3\CMS\Workspaces\Controller\Remote;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord;
use TYPO3\CMS\Workspaces\Service\IntegrityService;
use TYPO3\CMS\Workspaces\Service\WorkspaceService;
/**
* Class AbstractHandler
*/
abstract class AbstractHandler
{
/**
* Gets the current workspace ID.
*
* @return int The current workspace ID
*/
protected function getCurrentWorkspace()
{
return $this->getWorkspaceService()->getCurrentWorkspace();
}
/**
* Gets an error response to be shown in the grid component.
*
* @param string $errorLabel Name of the label in the locallang.xlf file
* @param int $errorCode The error code to be used
* @param bool $successFlagValue Value of the success flag to be delivered back (might be FALSE in most cases)
* @return array
*/
protected function getErrorResponse($errorLabel, $errorCode = 0, $successFlagValue = false)
{
$localLangFile = 'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf';
$response = [
'error' => [
'code' => $errorCode,
'message' => $GLOBALS['LANG']->sL($localLangFile . ':' . $errorLabel)
],
'success' => $successFlagValue
];
return $response;
}
/**
* Gets an instance of the workspaces service.
*
* @return WorkspaceService
*/
protected function getWorkspaceService()
{
return GeneralUtility::makeInstance(WorkspaceService::class);
}
/**
* Validates whether the submitted language parameter can be
* interpreted as integer value.
*
* @param stdClass $parameters
* @return int|null
*/
protected function validateLanguageParameter(\stdClass $parameters)
{
$language = null;
if (isset($parameters->language) && MathUtility::canBeInterpretedAsInteger($parameters->language)) {
$language = $parameters->language;
}
return $language;
}
/**
* Gets affected elements on publishing/swapping actions.
* Affected elements have a dependency, e.g. translation overlay
* and the default origin record - thus, the default record would be
* affected if the translation overlay shall be published.
*
* @param stdClass $parameters
* @return array
*/
protected function getAffectedElements(\stdClass $parameters)
{
$affectedElements = [];
if ($parameters->type === 'selection') {
foreach ((array)$parameters->selection as $element) {
$affectedElements[] = CombinedRecord::create($element->table, $element->liveId, $element->versionId);
}
} elseif ($parameters->type === 'all') {
$versions = $this->getWorkspaceService()->selectVersionsInWorkspace($this->getCurrentWorkspace(), 0, -99, -1, 0, 'tables_select', $this->validateLanguageParameter($parameters));
foreach ($versions as $table => $tableElements) {
foreach ($tableElements as $element) {
$affectedElement = CombinedRecord::create($table, $element['t3ver_oid'], $element['uid']);
$affectedElement->getVersionRecord()->setRow($element);
$affectedElements[] = $affectedElement;
}
}
}
return $affectedElements;
}
/**
* Creates a new instance of the integrity service for the
* given set of affected elements.
*
* @param CombinedRecord[] $affectedElements
* @return IntegrityService
* @see getAffectedElements
*/
protected function createIntegrityService(array $affectedElements)
{
$integrityService = GeneralUtility::makeInstance(IntegrityService::class);
$integrityService->setAffectedElements($affectedElements);
return $integrityService;
}
}
......@@ -18,6 +18,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Exception;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
......@@ -30,19 +31,25 @@ use TYPO3\CMS\Workspaces\Service\WorkspaceService;
/**
* Class ActionHandler
*/
class ActionHandler extends AbstractHandler
class ActionHandler
{
/**
* @var StagesService
*/
protected $stageService;
/**
* @var WorkspaceService
*/
protected $workspaceService;
/**
* Creates this object.
*/
public function __construct()
{
$this->stageService = GeneralUtility::makeInstance(StagesService::class);
$this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
}
/**
......@@ -53,7 +60,7 @@ class ActionHandler extends AbstractHandler
*/
public function generateWorkspacePreviewLink($uid)
{
return $this->getWorkspaceService()->generateWorkspacePreviewLink($uid);
return $this->workspaceService->generateWorkspacePreviewLink($uid);
}
/**
......@@ -64,7 +71,7 @@ class ActionHandler extends AbstractHandler
*/
public function generateWorkspacePreviewLinksForAllLanguages($uid)
{
return $this->getWorkspaceService()->generateWorkspacePreviewLinksForAllLanguages($uid);
return $this->workspaceService->generateWorkspacePreviewLinksForAllLanguages($uid);
}
/**
......@@ -201,14 +208,14 @@ class ActionHandler extends AbstractHandler
'hidden' => $column->hidden
];
}
$GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'] = $data;
$GLOBALS['BE_USER']->writeUC();
$this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['columns'] = $data;
$this->getBackendUser()->writeUC();
}
public function loadColumnModel()
{
if (is_array($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'])) {
return $GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['columns'];
if (is_array($this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['columns'])) {
return $this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['columns'];
}
return [];
}
......@@ -223,8 +230,8 @@ class ActionHandler extends AbstractHandler
if (MathUtility::canBeInterpretedAsInteger($language) === false && $language !== 'all') {
$language = 'all';
}
$GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['language'] = $language;
$GLOBALS['BE_USER']->writeUC();
$this->getBackendUser()->uc['moduleData']['Workspaces'][$this->getBackendUser()->workspace]['language'] = $language;
$this->getBackendUser()->writeUC();
}
/**
......@@ -342,7 +349,7 @@ class ActionHandler extends AbstractHandler
if ($stageRecord === null) {
throw new \InvalidArgumentException(
$GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'),
$this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer'),
1476044776
);
}
......@@ -872,4 +879,43 @@ class ActionHandler extends AbstractHandler
{
return $GLOBALS['BE_USER'];
}
/**
* @return LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'];
}
/**
* Gets an error response to be shown in the grid component.
*
* @param string $errorLabel Name of the label in the locallang.xlf file
* @param int $errorCode The error code to be used
* @param bool $successFlagValue Value of the success flag to be delivered back (might be FALSE in most cases)
* @return array
*/
protected function getErrorResponse($errorLabel, $errorCode = 0, $successFlagValue = false)
{
$localLangFile = 'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf';
$response = [
'error' => [
'code' => $errorCode,
'message' => $this->getLanguageService()->sL($localLangFile . ':' . $errorLabel)
],
'success' => $successFlagValue
];
return $response;
}
/**
* Gets the current workspace ID.
*
* @return int The current workspace ID
*/
protected function getCurrentWorkspace()
{
return $this->workspaceService->getCurrentWorkspace();
}
}
......@@ -14,13 +14,17 @@ namespace TYPO3\CMS\Workspaces\Controller\Remote;
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Workspaces\Service\WorkspaceService;
/**
* Class MassActionHandler
* Class encapsulates all actions which are triggered for all elements within the current workspace.
*/
class MassActionHandler extends AbstractHandler
class MassActionHandler
{
const MAX_RECORDS_TO_PROCESS = 30;
......@@ -31,28 +35,37 @@ class MassActionHandler extends AbstractHandler
*/
private $pathToLocallang = 'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf';
/**
* @var WorkspaceService
*/
protected $workspaceService;
public function __construct()
{
$this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
}
/**
* Get list of available mass workspace actions.
*
* @param \stdClass $parameter
* @return array $data
*/
public function getMassStageActions($parameter)
public function getMassStageActions()
{
$actions = [];
$currentWorkspace = $this->getCurrentWorkspace();
$massActionsEnabled = $GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.enableMassActions') !== '0';
$massActionsEnabled = $this->getBackendUser()->getTSConfigVal('options.workspaces.enableMassActions') !== '0';
// in case we're working within "All Workspaces" we can't provide Mass Actions
if ($currentWorkspace != WorkspaceService::SELECT_ALL_WORKSPACES && $massActionsEnabled) {
$publishAccess = $GLOBALS['BE_USER']->workspacePublishAccess($currentWorkspace);
if ($publishAccess && !($GLOBALS['BE_USER']->workspaceRec['publish_access'] & 1)) {
$actions[] = ['action' => 'publish', 'title' => $GLOBALS['LANG']->sL($this->pathToLocallang . ':label_doaction_publish')];
if ($GLOBALS['BE_USER']->workspaceSwapAccess()) {
$actions[] = ['action' => 'swap', 'title' => $GLOBALS['LANG']->sL($this->pathToLocallang . ':label_doaction_swap')];
$publishAccess = $this->getBackendUser()->workspacePublishAccess($currentWorkspace);
if ($publishAccess && !($this->getBackendUser()->workspaceRec['publish_access'] & 1)) {
$actions[] = ['action' => 'publish', 'title' => $this->getLanguageService()->sL($this->pathToLocallang . ':label_doaction_publish')];
if ($this->getBackendUser()->workspaceSwapAccess()) {
$actions[] = ['action' => 'swap', 'title' => $this->getLanguageService()->sL($this->pathToLocallang . ':label_doaction_swap')];
}
}
if ($currentWorkspace !== WorkspaceService::LIVE_WORKSPACE_ID) {
$actions[] = ['action' => 'discard', 'title' => $GLOBALS['LANG']->sL($this->pathToLocallang . ':label_doaction_discard')];
$actions[] = ['action' => 'discard', 'title' => $this->getLanguageService()->sL($this->pathToLocallang . ':label_doaction_discard')];
}
}
$result = [
......@@ -82,8 +95,8 @@ class MassActionHandler extends AbstractHandler
$cnt = $this->initPublishData($this->getCurrentWorkspace(), $parameters->swap, $language);
$result['total'] = $cnt;
} else {
$result['processed'] = $this->processData($this->getCurrentWorkspace());
$result['total'] = $GLOBALS['BE_USER']->getSessionData('workspaceMassAction_total');
$result['processed'] = $this->processData();
$result['total'] = $this->getBackendUser()->getSessionData('workspaceMassAction_total');
}
} catch (\Exception $e) {
$result['error'] = $e->getMessage();
......@@ -108,11 +121,10 @@ class MassActionHandler extends AbstractHandler
try {
if ($parameters->init) {
$language = $this->validateLanguageParameter($parameters);
$cnt = $this->initFlushData($this->getCurrentWorkspace(), $language);
$result['total'] = $cnt;
$result['total'] = $this->initFlushData($this->getCurrentWorkspace(), $language);
} else {
$result['processed'] = $this->processData($this->getCurrentWorkspace());
$result['total'] = $GLOBALS['BE_USER']->getSessionData('workspaceMassAction_total');
$result['processed'] = $this->processData();
$result['total'] = $this->getBackendUser()->getSessionData('workspaceMassAction_total');
}
} catch (\Exception $e) {
$result['error'] = $e->getMessage();
......@@ -131,15 +143,15 @@ class MassActionHandler extends AbstractHandler
protected function initPublishData($workspace, $swap, $language = null)
{
// workspace might be -98 a.k.a "All Workspaces but that's save here
$publishData = $this->getWorkspaceService()->getCmdArrayForPublishWS($workspace, $swap, 0, $language);
$publishData = $this->workspaceService->getCmdArrayForPublishWS($workspace, $swap, 0, $language);
$recordCount = 0;
foreach ($publishData as $table => $recs) {
$recordCount += count($recs);
}
if ($recordCount > 0) {
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction', $publishData);
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_total', $recordCount);
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_processed', 0);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction', $publishData);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction_total', $recordCount);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction_processed', 0);
}
return $recordCount;
}
......@@ -154,15 +166,15 @@ class MassActionHandler extends AbstractHandler
protected function initFlushData($workspace, $language = null)
{
// workspace might be -98 a.k.a "All Workspaces but that's save here
$flushData = $this->getWorkspaceService()->getCmdArrayForFlushWS($workspace, true, 0, $language);
$flushData = $this->workspaceService->getCmdArrayForFlushWS($workspace, true, 0, $language);
$recordCount = 0;
foreach ($flushData as $table => $recs) {
$recordCount += count($recs);
}
if ($recordCount > 0) {
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction', $flushData);
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_total', $recordCount);
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_processed', 0);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction', $flushData);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction_total', $recordCount);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction_processed', 0);
}
return $recordCount;
}
......@@ -170,13 +182,12 @@ class MassActionHandler extends AbstractHandler
/**
* Processes the data.
*
* @param int $workspace
* @return int
*/
protected function processData($workspace)
protected function processData()
{
$processData = $GLOBALS['BE_USER']->getSessionData('workspaceMassAction');
$recordsProcessed = $GLOBALS['BE_USER']->getSessionData('workspaceMassAction_processed');
$processData = $this->getBackendUser()->getSessionData('workspaceMassAction');
$recordsProcessed = $this->getBackendUser()->getSessionData('workspaceMassAction_processed');
$limitedCmd = [];
$numRecs = 0;
foreach ($processData as $table => $recs) {
......@@ -193,15 +204,14 @@ class MassActionHandler extends AbstractHandler
}
if ($numRecs == 0) {
// All done
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction', null);
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_total', 0);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction', null);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction_total', 0);
} else {
/** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */
$tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
// Execute the commands:
$tce->start([], $limitedCmd);
$tce->process_cmdmap();
$errors = $tce->errorLog;
$dataHandler->start([], $limitedCmd);
$dataHandler->process_cmdmap();
$errors = $dataHandler->errorLog;
if (!empty($errors)) {
throw new \Exception(implode(', ', $errors), 1476048278);
}
......@@ -212,9 +222,51 @@ class MassActionHandler extends AbstractHandler
unset($processData[$table][$key]);
}
}
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction', $processData);
$GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_processed', $recordsProcessed);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction', $processData);
$this->getBackendUser()->setAndSaveSessionData('workspaceMassAction_processed', $recordsProcessed);
}
return $recordsProcessed;
}
/**
* Validates whether the submitted language parameter can be
* interpreted as integer value.
*
* @param \stdClass $parameters
* @return int|null
*/
protected function validateLanguageParameter(\stdClass $parameters)
{
$language = null;
if (isset($parameters->language) && MathUtility::canBeInterpretedAsInteger($parameters->language)) {
$language = $parameters->language;
}
return $language;
}
/**
* Gets the current workspace ID.
*
* @return int The current workspace ID
*/
protected function getCurrentWorkspace()
{
return $this->workspaceService->getCurrentWorkspace();
}
/**
* @return BackendUserAuthentication
*/
protected function getBackendUser()
{
return $GLOBALS['BE_USER'];
}
/**
* @return LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'];
}
}
......@@ -28,15 +28,17 @@ use TYPO3\CMS\Core\Utility\DiffUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Workspaces\Domain\Model\CombinedRecord;
use TYPO3\CMS\Workspaces\Service\GridDataService;
use TYPO3\CMS\Workspaces\Service\HistoryService;
use TYPO3\CMS\Workspaces\Service\IntegrityService;
use TYPO3\CMS\Workspaces\Service\StagesService;
use TYPO3\CMS\Workspaces\Service\WorkspaceService;
/**
* Class RemoteServer
*/
class RemoteServer extends AbstractHandler
class RemoteServer
{
/**
* @var GridDataService
......@@ -48,11 +50,23 @@ class RemoteServer extends AbstractHandler
*/
protected $stagesService;
/**
* @var WorkspaceService
*/
protected $workspaceService;
/**
* @var DiffUtility
*/
protected $differenceHandler;
public function __construct()
{
$this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class);
$this->gridDataService = GeneralUtility::makeInstance(GridDataService::class);
$this->stagesService = GeneralUtility::makeInstance(StagesService::class);
}
/**
* Checks integrity of elements before peforming actions on them.
*
......@@ -82,23 +96,22 @@ class RemoteServer extends AbstractHandler
if (!isset($parameter->language) || !MathUtility::canBeInterpretedAsInteger($parameter->language)) {
$parameter->language = null;
}
$versions = $this->getWorkspaceService()->selectVersionsInWorkspace($this->getCurrentWorkspace(), 0, -99, $pageId, $parameter->depth, 'tables_select', $parameter->language);
$data = $this->getGridDataService()->generateGridListFromVersions($versions, $parameter, $this->getCurrentWorkspace());
$versions = $this->workspaceService->selectVersionsInWorkspace($this->getCurrentWorkspace(), 0, -99, $pageId, $parameter->depth, 'tables_select', $parameter->language);
$data = $this->gridDataService->generateGridListFromVersions($versions, $parameter, $this->getCurrentWorkspace());
return $data;
}
/**
* Get List of available workspace actions
*
* @param \stdClass $parameter
* @return array $data
*/
public function getStageActions(\stdClass $parameter)
public function getStageActions()
{
$currentWorkspace = $this->getCurrentWorkspace();
$stages = [];
if ($currentWorkspace != WorkspaceService::SELECT_ALL_WORKSPACES) {
$stages = $this->getStagesService()->getStagesForWSUser();
$stages = $this->stagesService->getStagesForWSUser();
}
$data = [
'total' => count($stages),
......@@ -124,8 +137,7 @@ class RemoteServer extends AbstractHandler
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$icon_Live = $iconFactory->getIconForRecord($parameter->table, $liveRecord, Icon::SIZE_SMALL)->render();
$icon_Workspace = $iconFactory->getIconForRecord($parameter->table, $versionRecord, Icon::SIZE_SMALL)->render();
$stagesService = $this->getStagesService();
$stagePosition = $stagesService->getPositionOfCurrentStage($parameter->stage);
$stagePosition = $this->stagesService->getPositionOfCurrentStage($parameter->stage);
$fieldsOfRecords = array_keys($liveRecord);
if ($GLOBALS['TCA'][$parameter->table]) {
if ($GLOBALS['TCA'][$parameter->table]['interface']['showRecordFieldList']) {
......@@ -247,12 +259,11 @@ class RemoteServer extends AbstractHandler
}
$commentsForRecord = $this->getCommentsForRecord($parameter->uid, $parameter->table);
/** @var $historyService HistoryService */
$historyService = GeneralUtility::makeInstance(HistoryService::class);