From 1deec2d43963c6cc136fa41c0df707959ef1acd6 Mon Sep 17 00:00:00 2001 From: Oliver Hader <oliver@typo3.org> Date: Thu, 26 Jun 2014 13:38:11 +0200 Subject: [PATCH] [FEATURE] Rework workspace notification settings The current notification settings have some drawbacks and are not easy to understand if it comes the the expected behavior in the workspace module. The settings are defined in each sys_workspace and sys_workspace_stage record and are evaluated in the workspace module if sending a particular element to be reviewed to the previous or next stage. This change extends the meaning and configuration possibilities on defining the notification settings. In general the notification modes are replaced by the definition whether the notification dialog shall be shown and if the preselection can be changed. Besides that, the preselection is cumulative and defined by owners, members, editors and responsible persons (for stages). Resolves: #35245 Releases: master Change-Id: Icb680fe85fab61a51d53e3afb94b51a4930e180c Reviewed-on: http://review.typo3.org/31160 Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de> Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de> Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: Benni Mack <benni@typo3.org> --- ...45-ReworkWorkspaceNotificationSettings.rst | 51 +++ .../WorkspacesNotificationSettingsUpdate.php | 177 +++++++++ typo3/sysext/install/ext_localconf.php | 1 + .../Classes/Domain/Model/DatabaseRecord.php | 2 +- .../Classes/Domain/Record/AbstractRecord.php | 94 +++++ .../Classes/Domain/Record/StageRecord.php | 340 ++++++++++++++++++ .../Classes/Domain/Record/WorkspaceRecord.php | 209 +++++++++++ .../Classes/ExtDirect/ActionHandler.php | 183 +++++----- .../Classes/ExtDirect/ExtDirectServer.php | 8 + .../Classes/Service/RecordService.php | 85 +++++ .../Classes/Service/StagesService.php | 288 +++++++++------ .../Configuration/TCA/sys_workspace.php | 117 +++++- .../Configuration/TCA/sys_workspace_stage.php | 33 +- .../Private/Language/locallang_db.xlf | 36 ++ .../Resources/Public/JavaScript/actions.js | 8 +- typo3/sysext/workspaces/ext_tables.sql | 7 + 16 files changed, 1416 insertions(+), 223 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-35245-ReworkWorkspaceNotificationSettings.rst create mode 100644 typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php create mode 100644 typo3/sysext/workspaces/Classes/Domain/Record/AbstractRecord.php create mode 100644 typo3/sysext/workspaces/Classes/Domain/Record/StageRecord.php create mode 100644 typo3/sysext/workspaces/Classes/Domain/Record/WorkspaceRecord.php create mode 100644 typo3/sysext/workspaces/Classes/Service/RecordService.php diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-35245-ReworkWorkspaceNotificationSettings.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-35245-ReworkWorkspaceNotificationSettings.rst new file mode 100644 index 000000000000..6d44b39c2627 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-35245-ReworkWorkspaceNotificationSettings.rst @@ -0,0 +1,51 @@ +======================================================== +Feature: #35245 - Rework workspace notification settings +======================================================== + +Description +=========== + +The current notification settings have some drawbacks and are not easy to +understand if it comes the the expected behavior in the workspace module. +The settings are defined in each sys_workspace and sys_workspace_stage +record and are evaluated in the workspace module if sending a particular +element to be reviewed to the previous or next stage. + +Currently there are the following notification settings: +* on stages + * "edit stage": takes recipients from "adminusers" field + (workspace owners) + * "ready to publish" stage: takes recipients from "members" field + (workspace members) +* on preselection of recipients + * "all (non-strict)": if users from workspace setting (field "adminusers" + or "members") are also in the specific "default_users" setting for the + stage, the checkbox is enabled by default and cannot be changed, + otherwise it's not checked + * "all (strict)": all users from workspace setting (field "adminusers" + or "members") are checked and cannot be changed + * "some (strict)": all users from workspace setting (field "adminusers" + or "members") are checked, but still can be changed +* behavior + * sending to "edit" stage: members are notified per default + * sending to "ready to publish" stage: owners are notified per default + +The changes extends the possibilities to define notification settings: +* on stages + * add settings for "publish-execute" stage (actual publishing process) +* on preselection of recipients + * remove modes + * replace settings for showing the dialog and whether modifying the + preselection is allowed at all (getting rid of the "strict" modes) + * add possibilities to defined notification recipients + * owner & members as defined in the accordant fields + * editors that have been working on a particular element + * responsible persons (on custom stages only) + +Impact +====== + +The meaning and behavior of the workspaces notification settings concerning +preselected recipients and the possibility to modify the selection on moving +an element to a particular change is different now. However, an upgrade wizard +helps to upgrade the settings to the new definitions. \ No newline at end of file diff --git a/typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php b/typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php new file mode 100644 index 000000000000..d79f781cf53a --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/WorkspacesNotificationSettingsUpdate.php @@ -0,0 +1,177 @@ +<?php +namespace TYPO3\CMS\Install\Updates; + +/* + * 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\ExtensionManagementUtility; + +/** + * Migrate the workspaces notification settings to the enhanced schema. + */ +class WorkspacesNotificationSettingsUpdate extends AbstractUpdate { + + /** + * @var string + */ + protected $title = 'Migrate the workspaces notification settings to the enhanced schema'; + + /** + * Checks if an update is needed + * + * @param string &$description The description for the update + * @return bool Whether an update is needed (TRUE) or not (FALSE) + */ + public function checkForUpdate(&$description) { + if (!ExtensionManagementUtility::isLoaded('workspaces')) { + return FALSE; + } + + if ($this->isWizardDone()) { + return FALSE; + } + + $workspacesCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'sys_workspace', + 'deleted=0' + ); + + $stagesCount = $this->getDatabaseConnection()->exec_SELECTcountRows( + 'uid', + 'sys_workspace_stage', + 'deleted=0' + ); + + if ($workspacesCount + $stagesCount > 0) { + $description = 'The workspaces notification settings have been extended' + . ' and need to be migrated to the new definitions. This update wizard' + . ' upgrades the accordant settings in the availble workspaces and stages.'; + return TRUE; + } else { + $this->markWizardAsDone(); + } + + return FALSE; + } + + /** + * Perform the database updates for workspace records + * + * @param array &$databaseQueries Queries done in this update + * @param mixed &$customMessages Custom messages + * @return bool + */ + public function performUpdate(array &$databaseQueries, &$customMessages) { + $databaseConnection = $this->getDatabaseConnection(); + + $workspaceRecords = $databaseConnection->exec_SELECTgetRows('*', 'sys_workspace', 'deleted=0'); + foreach ($workspaceRecords as $workspaceRecord) { + $update = $this->prepareWorkspaceUpdate($workspaceRecord); + if ($update !== NULL) { + $databaseConnection->exec_UPDATEquery('sys_workspace', 'uid=' . (int)$workspaceRecord['uid'], $update); + $databaseQueries[] = $databaseConnection->debug_lastBuiltQuery; + } + } + + $stageRecords = $databaseConnection->exec_SELECTgetRows('*', 'sys_workspace_stage', 'deleted=0'); + foreach ($stageRecords as $stageRecord) { + $update = $this->prepareStageUpdate($stageRecord); + if ($update !== NULL) { + $databaseConnection->exec_UPDATEquery('sys_workspace_stage', 'uid=' . (int)$stageRecord['uid'], $update); + $databaseQueries[] = $databaseConnection->debug_lastBuiltQuery; + } + } + + $this->markWizardAsDone(); + return TRUE; + } + + /** + * Prepares SQL updates for workspace records. + * + * @param array $workspaceRecord + * @return array|NULL + */ + protected function prepareWorkspaceUpdate(array $workspaceRecord) { + if (empty($workspaceRecord['uid'])) { + return NULL; + } + + $update = array(); + $update = $this->mapSettings($workspaceRecord, $update, 'edit', 'edit'); + $update = $this->mapSettings($workspaceRecord, $update, 'publish', 'publish'); + $update = $this->mapSettings($workspaceRecord, $update, 'publish', 'execute'); + return $update; + } + + /** + * Prepares SQL update for stage records. + * + * @param array $stageRecord + * @return array|null + */ + protected function prepareStageUpdate(array $stageRecord) { + if (empty($stageRecord['uid'])) { + return NULL; + } + + $update = array(); + $update = $this->mapSettings($stageRecord, $update); + return $update; + } + + /** + * Maps settings to new meaning. + * + * @param array $record + * @param array $update + * @param string $from + * @param string $to + * @return array + */ + protected function mapSettings(array $record, array $update, $from = '', $to = '') { + $fromPrefix = ($from ? $from . '_' : ''); + $toPrefix = ($to ? $to . '_' : ''); + + $settings = 0; + // Previous setting: "Allow notification settings during stage change" + if ($record[$fromPrefix . 'allow_notificaton_settings']) { + $settings += 1; + } + // Previous setting: "All are selected per default (can be changed)" + if ((int)$record[$fromPrefix . 'notification_mode'] === 0) { + $settings += 2; + } + + // Custom stages: preselect responsible persons (8) + if (isset($record['responsible_persons'])) { + $preselection = 8; + // Workspace "edit" stage: preselect members (2) + } elseif ($to === 'edit') { + $preselection = 2; + // Workspace "publish" stage: preselect owners (1) + } elseif ($to === 'publish') { + $preselection = 1; + // Workspace "execute" stage: preselect owners (1) and members (2) as default + } else { + $preselection = 1 + 2; + } + + $update[$toPrefix . 'allow_notificaton_settings'] = $settings; + $update[$toPrefix . 'notification_preselection'] = $preselection; + + return $update; + } + +} diff --git a/typo3/sysext/install/ext_localconf.php b/typo3/sysext/install/ext_localconf.php index 157a657db723..dfc33610866c 100644 --- a/typo3/sysext/install/ext_localconf.php +++ b/typo3/sysext/install/ext_localconf.php @@ -11,6 +11,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['filesReplace $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['tableCType'] = \TYPO3\CMS\Install\Updates\TableFlexFormToTtContentFieldsUpdate::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\FileListIsStartModuleUpdate::class] = \TYPO3\CMS\Install\Updates\FileListIsStartModuleUpdate::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['textmediaCType'] = \TYPO3\CMS\Install\Updates\ContentTypesToTextMediaUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update'][\TYPO3\CMS\Install\Updates\WorkspacesNotificationSettingsUpdate::class] = \TYPO3\CMS\Install\Updates\WorkspacesNotificationSettingsUpdate::class; $signalSlotDispatcher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class); $signalSlotDispatcher->connect( diff --git a/typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php b/typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php index 142b6e9e1d6d..372ebc9fac3a 100644 --- a/typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php +++ b/typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php @@ -104,7 +104,7 @@ class DatabaseRecord { * @return void */ public function setUid($uid) { - $this->uid = $uid; + $this->uid = (int)$uid; } /** diff --git a/typo3/sysext/workspaces/Classes/Domain/Record/AbstractRecord.php b/typo3/sysext/workspaces/Classes/Domain/Record/AbstractRecord.php new file mode 100644 index 000000000000..7768aae57cc7 --- /dev/null +++ b/typo3/sysext/workspaces/Classes/Domain/Record/AbstractRecord.php @@ -0,0 +1,94 @@ +<?php +namespace TYPO3\CMS\Workspaces\Domain\Record; + +/** + * 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\Workspaces\Service\StagesService; + +/** + * Combined record class + */ +abstract class AbstractRecord { + + /** + * @var array + */ + protected $record; + + static protected function fetch($tableName, $uid) { + $record = static::getDatabaseConnection()->exec_SELECTgetSingleRow('*', $tableName, 'deleted=0 AND uid=' . (int)$uid); + if (empty($record)) { + throw new \RuntimeException('Record "' . $tableName . ':' . $uid . '" not found'); + } + return $record; + } + + /** + * @return \TYPO3\CMS\Core\Database\DatabaseConnection + */ + static protected function getDatabaseConnection() { + return $GLOBALS['TYPO3_DB']; + } + + /** + * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication + */ + static protected function getBackendUser() { + return $GLOBALS['BE_USER']; + } + + /** + * @return \TYPO3\CMS\Lang\LanguageService + */ + static protected function getLanguageService() { + return $GLOBALS['LANG']; + } + + /** + * @param array $record + */ + public function __construct(array $record) { + $this->record = $record; + } + + /** + * @return string + */ + public function __toString() { + return (string)$this->getUid(); + } + + /** + * @return int + */ + public function getUid() { + return (int)$this->record['uid']; + } + + /** + * @return string + */ + public function getTitle() { + return (string)$this->record['title']; + } + + /** + * @return StagesService + */ + protected function getStagesService() { + return GeneralUtility::makeInstance(StagesService::class); + } + +} diff --git a/typo3/sysext/workspaces/Classes/Domain/Record/StageRecord.php b/typo3/sysext/workspaces/Classes/Domain/Record/StageRecord.php new file mode 100644 index 000000000000..b198cbad8e4f --- /dev/null +++ b/typo3/sysext/workspaces/Classes/Domain/Record/StageRecord.php @@ -0,0 +1,340 @@ +<?php +namespace TYPO3\CMS\Workspaces\Domain\Record; + +/** + * 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\Workspaces\Service\StagesService; + +/** + * Combined record class + */ +class StageRecord extends AbstractRecord{ + + /** + * @var WorkspaceRecord + */ + protected $workspace; + + /** + * @var bool + */ + protected $internal = FALSE; + + /** + * @var array + */ + protected $responsiblePersons; + + /** + * @var array + */ + protected $defaultRecipients; + + /** + * @var array + */ + protected $preselectedRecipients; + + /** + * @var array + */ + protected $allRecipients; + + /** + * @param int $uid + * @param array $record + * @return StageRecord + */ + static public function get($uid, array $record = NULL) { + if (empty($record)) { + $record = static::fetch('sys_workspace_stage', $uid); + } + return WorkspaceRecord::get($record['parentid'])->getStage($uid); + } + + /** + * @param WorkspaceRecord $workspace + * @param int $uid + * @param array $record + * @return StageRecord + */ + static public function build(WorkspaceRecord $workspace, $uid, array $record = NULL) { + if (empty($record)) { + $record = static::fetch('sys_workspace_stage', $uid); + } + return new StageRecord($workspace, $record); + } + + /** + * @param WorkspaceRecord $workspace + * @param array $record + */ + public function __construct(WorkspaceRecord $workspace, array $record) { + parent::__construct($record); + $this->workspace = $workspace; + } + + /** + * @return WorkspaceRecord + */ + public function getWorkspace() { + return $this->workspace; + } + + /** + * @return NULL|StageRecord + */ + public function getPrevious() { + return $this->getWorkspace()->getPreviousStage($this->getUid()); + } + + /** + * @return NULL|StageRecord + */ + public function getNext() { + return $this->getWorkspace()->getNextStage($this->getUid()); + } + + /** + * @param StageRecord $stageRecord + * @return int + */ + public function determineOrder(StageRecord $stageRecord) { + if ($this->getUid() === $stageRecord->getUid()) { + return 0; + } elseif ($this->isEditStage() || $stageRecord->isExecuteStage() || $this->isPreviousTo($stageRecord)) { + return -1; + } elseif ($this->isExecuteStage() || $stageRecord->isEditStage() || $this->isNextTo($stageRecord)) { + return 1; + } + return 0; + } + + /** + * Determines whether $this is in a previous stage compared to $stageRecord. + * + * @param StageRecord $stageRecord + * @return bool + */ + public function isPreviousTo(StageRecord $stageRecord) { + $current = $stageRecord; + while ($previous = $current->getPrevious()) { + if ($this->getUid() === $previous->getUid()) { + return TRUE; + } + $current = $previous; + } + return FALSE; + } + + /** + * Determines whether $this is in a later stage compared to $stageRecord. + * + * @param StageRecord $stageRecord + * @return bool + */ + public function isNextTo(StageRecord $stageRecord) { + $current = $stageRecord; + while ($next = $current->getNext()) { + if ($this->getUid() === $next->getUid()) { + return TRUE; + } + $current = $next; + } + return FALSE; + } + + /** + * @return string + */ + public function getDefaultComment() { + $defaultComment = ''; + if (isset($this->record['default_mailcomment'])) { + $defaultComment = $this->record['default_mailcomment']; + } + return $defaultComment; + } + + /** + * @param bool $internal + */ + public function setInternal($internal = TRUE) { + $this->internal = (bool)$internal; + } + + /** + * @return bool + */ + public function isInternal() { + return $this->internal; + } + + /** + * @return bool + */ + public function isEditStage() { + return ($this->getUid() === StagesService::STAGE_EDIT_ID); + } + + /** + * @return bool + */ + public function isPublishStage() { + return ($this->getUid() === StagesService::STAGE_PUBLISH_ID); + } + + /** + * @return bool + */ + public function isExecuteStage() { + return ($this->getUid() === StagesService::STAGE_PUBLISH_EXECUTE_ID); + } + + /** + * @return bool + */ + public function isDialogEnabled() { + return (((int)$this->record['allow_notificaton_settings'] & 1) > 0); + } + + /** + * @return bool + */ + public function isPreselectionChangeable() { + return (((int)$this->record['allow_notificaton_settings'] & 2) > 0); + } + + /** + * @return bool + */ + public function areOwnersPreselected() { + return (((int)$this->record['notification_preselection'] & 1) > 0); + } + + /** + * @return bool + */ + public function areMembersPreselected() { + return (((int)$this->record['notification_preselection'] & 2) > 0); + } + + /** + * @return bool + */ + public function areEditorsPreselected() { + return (((int)$this->record['notification_preselection'] & 4) > 0); + } + + /** + * @return bool + */ + public function areResponsiblePersonsPreselected() { + return (((int)$this->record['notification_preselection'] & 8) > 0); + } + + /** + * @return bool + */ + public function hasPreselection() { + return ( + $this->areOwnersPreselected() + || $this->areMembersPreselected() + || $this->areEditorsPreselected() + || $this->areResponsiblePersonsPreselected() + ); + } + + /** + * @return array + */ + public function getResponsiblePersons() { + if (!isset($this->responsiblePersons)) { + $this->responsiblePersons = array(); + if (!empty($this->record['responsible_persons'])) { + $this->responsiblePersons = $this->getStagesService()->resolveBackendUserIds($this->record['responsible_persons']); + } + } + return $this->responsiblePersons; + } + + /** + * @return array + */ + public function getDefaultRecipients() { + if (!isset($this->defaultRecipients)) { + $this->defaultRecipients = $this->getStagesService()->resolveBackendUserIds($this->record['notification_defaults']); + } + return $this->defaultRecipients; + } + + /** + * Gets all recipients (backend user ids). + * + * @return array + */ + public function getAllRecipients() { + if (!isset($this->allRecipients)) { + $allRecipients = $this->getDefaultRecipients(); + + if ($this->isInternal() || $this->areOwnersPreselected()) { + $allRecipients = array_merge($allRecipients, $this->getWorkspace()->getOwners()); + } + if ($this->isInternal() || $this->areMembersPreselected()) { + $allRecipients = array_merge($allRecipients, $this->getWorkspace()->getMembers()); + } + if (!$this->isInternal()) { + $allRecipients = array_merge($allRecipients, $this->getResponsiblePersons()); + } + + $this->allRecipients = array_unique($allRecipients); + } + + return $this->allRecipients; + } + + /** + * @return int[] + */ + public function getPreselectedRecipients() { + if (!isset($this->preselectedRecipients)) { + $preselectedRecipients = $this->getDefaultRecipients(); + + if ($this->areOwnersPreselected()) { + $preselectedRecipients = array_merge($preselectedRecipients, $this->getWorkspace()->getOwners()); + } + if ($this->areMembersPreselected()) { + $preselectedRecipients = array_merge($preselectedRecipients, $this->getWorkspace()->getMembers()); + } + if ($this->areResponsiblePersonsPreselected()) { + $preselectedRecipients = array_merge($preselectedRecipients, $this->getResponsiblePersons()); + } + + $this->preselectedRecipients = array_unique($preselectedRecipients); + } + + return $this->preselectedRecipients; + } + + /** + * @return bool + */ + public function isAllowed() { + return ( + $this->isEditStage() + || static::getBackendUser()->workspaceCheckStageForCurrent($this->getUid()) + || $this->isExecuteStage() && static::getBackendUser()->workspacePublishAccess($this->workspace->getUid()) + ); + } + +} diff --git a/typo3/sysext/workspaces/Classes/Domain/Record/WorkspaceRecord.php b/typo3/sysext/workspaces/Classes/Domain/Record/WorkspaceRecord.php new file mode 100644 index 000000000000..b951776cb483 --- /dev/null +++ b/typo3/sysext/workspaces/Classes/Domain/Record/WorkspaceRecord.php @@ -0,0 +1,209 @@ +<?php +namespace TYPO3\CMS\Workspaces\Domain\Record; + +/** + * 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\Workspaces\Service\StagesService; + +/** + * Combined record class + */ +class WorkspaceRecord extends AbstractRecord{ + + /** + * @var array + */ + protected $internalStages = array( + StagesService::STAGE_EDIT_ID => array( + 'name' => 'edit', + 'label' => 'LLL:EXT:lang/locallang_mod_user_ws.xlf:stage_editing' + ), + StagesService::STAGE_PUBLISH_ID => array( + 'name' => 'publish', + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:stage_ready_to_publish' + ), + StagesService::STAGE_PUBLISH_EXECUTE_ID => array( + 'name' => 'execute', + 'label' => 'LLL:EXT:lang/locallang_mod_user_ws.xlf:stage_publish' + ), + ); + + /** + * @var array + */ + protected $internalStageFieldNames = array( + 'notification_defaults', + 'notification_preselection', + 'allow_notificaton_settings' + ); + + /** + * @var array + */ + protected $owners; + + /** + * @var array + */ + protected $members; + + /** + * @var StageRecord[] + */ + protected $stages; + + /** + * @param int $uid + * @param array $record + * @return WorkspaceRecord + */ + static public function get($uid, array $record = NULL) { + if (empty($uid)) { + $record = array(); + } elseif (empty($record)) { + $record = static::fetch('sys_workspace', $uid); + } + return new static($record); + } + + /** + * @return array + */ + public function getOwners() { + if (!isset($this->owners)) { + $this->owners = $this->getStagesService()->resolveBackendUserIds($this->record['adminusers']); + } + return $this->owners; + } + + /** + * @return array + */ + public function getMembers() { + if (!isset($this->members)) { + $this->members = $this->getStagesService()->resolveBackendUserIds($this->record['members']); + } + return $this->members; + } + + /** + * @return StageRecord[] + */ + public function getStages() { + if (!isset($this->stages)) { + $this->stages = array(); + $this->addStage($this->createInternalStage(StagesService::STAGE_EDIT_ID)); + + $records = self::getDatabaseConnection()->exec_SELECTgetRows( + '*', 'sys_workspace_stage', + 'deleted=0 AND parentid=' . $this->getUid() . ' AND parenttable=' + . self::getDatabaseConnection()->fullQuoteStr('sys_workspace', 'sys_workspace_stage'), + '', 'sorting' + ); + if (!empty($records)) { + foreach ($records as $record) { + $this->addStage(StageRecord::build($this, $record['uid'], $record)); + } + } + + $this->addStage($this->createInternalStage(StagesService::STAGE_PUBLISH_ID)); + $this->addStage($this->createInternalStage(StagesService::STAGE_PUBLISH_EXECUTE_ID)); + } + + return $this->stages; + } + + /** + * @param int $stageId + * @return NULL|StageRecord + */ + public function getStage($stageId) { + $stageId = (int)$stageId; + $this->getStages(); + if (!isset($this->stages[$stageId])) { + return NULL; + } + return $this->stages[$stageId]; + } + + /** + * @param int $stageId + * @return NULL|StageRecord + */ + public function getPreviousStage($stageId) { + $stageId = (int)$stageId; + $stageIds = array_keys($this->getStages()); + $stageIndex = array_search($stageId, $stageIds); + + // catches "0" (edit stage) as well + if (empty($stageIndex)) { + return NULL; + } + + $previousStageId = $stageIds[$stageIndex - 1]; + return $this->stages[$previousStageId]; + } + + /** + * @param int $stageId + * @return NULL|StageRecord + */ + public function getNextStage($stageId) { + $stageId = (int)$stageId; + $stageIds = array_keys($this->getStages()); + $stageIndex = array_search($stageId, $stageIds); + + if ($stageIndex === FALSE || !isset($stageIds[$stageIndex + 1])) { + return NULL; + } + + $nextStageId = $stageIds[$stageIndex + 1]; + return $this->stages[$nextStageId]; + } + + /** + * @param StageRecord $stage + */ + protected function addStage(StageRecord $stage) { + $this->stages[$stage->getUid()] = $stage; + } + + /** + * @param int $stageId + * @return StageRecord + * @throws \RuntimeException + */ + protected function createInternalStage($stageId) { + $stageId = (int)$stageId; + + if (!isset($this->internalStages[$stageId])) { + throw new \RuntimeException('Invalid internal stage "' . $stageId . '"'); + } + + $record = array( + 'uid' => $stageId, + 'title' => static::getLanguageService()->sL($this->internalStages[$stageId]['label']) + ); + + $fieldNamePrefix = $this->internalStages[$stageId]['name'] . '_'; + foreach ($this->internalStageFieldNames as $fieldName) { + $record[$fieldName] = $this->record[$fieldNamePrefix . $fieldName]; + } + + $stage = StageRecord::build($this, $stageId, $record); + $stage->setInternal(TRUE); + return $stage; + } + +} diff --git a/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php b/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php index 526812c32513..bf7e6730896e 100644 --- a/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php +++ b/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php @@ -17,6 +17,8 @@ namespace TYPO3\CMS\Workspaces\ExtDirect; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Workspaces\Service\StagesService; +use TYPO3\CMS\Workspaces\Domain\Record\WorkspaceRecord; +use TYPO3\CMS\Workspaces\Domain\Record\StageRecord; /** * ExtDirect action handler @@ -224,13 +226,14 @@ class ActionHandler extends AbstractHandler { $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']); if (is_array($elementRecord)) { - $stageId = $elementRecord['t3ver_stage']; - if ($this->getStageService()->isValid($stageId)) { - $nextStage = $this->getStageService()->getNextStage($stageId); - $result = $this->getSentToStageWindow($nextStage['uid']); + $workspaceRecord = WorkspaceRecord::get($elementRecord['t3ver_wsid']); + $nextStageRecord = $workspaceRecord->getNextStage($elementRecord['t3ver_stage']); + if ($nextStageRecord !== NULL) { + $this->stageService->getRecordService()->add($table, $uid); + $result = $this->getSentToStageWindow($nextStageRecord); $result['affects'] = array( 'table' => $table, - 'nextStage' => $nextStage['uid'], + 'nextStage' => $nextStageRecord->getUid(), 't3ver_oid' => $t3ver_oid, 'uid' => $uid ); @@ -257,15 +260,18 @@ class ActionHandler extends AbstractHandler { $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']); if (is_array($elementRecord)) { - $stageId = $elementRecord['t3ver_stage']; - if ($this->getStageService()->isValid($stageId)) { - if ($stageId !== StagesService::STAGE_EDIT_ID) { - $prevStage = $this->getStageService()->getPrevStage($stageId); - $result = $this->getSentToStageWindow($prevStage['uid']); + $workspaceRecord = WorkspaceRecord::get($elementRecord['t3ver_wsid']); + $stageRecord = $workspaceRecord->getStage($elementRecord['t3ver_stage']); + + if ($stageRecord !== NULL) { + if (!$stageRecord->isEditStage()) { + $this->stageService->getRecordService()->add($table, $uid); + $previousStageRecord = $stageRecord->getPrevious(); + $result = $this->getSentToStageWindow($previousStageRecord); $result['affects'] = array( 'table' => $table, 'uid' => $uid, - 'nextStage' => $prevStage['uid'] + 'nextStage' => $previousStageRecord->getUid() ); } else { // element is already in edit stage, there is no prev stage - return an error message @@ -286,9 +292,17 @@ class ActionHandler extends AbstractHandler { * Gets the dialog window to be displayed before a record can be sent to a specific stage. * * @param int $nextStageId + * @param array|\stdClass[] $elements * @return array */ - public function sendToSpecificStageWindow($nextStageId) { + public function sendToSpecificStageWindow($nextStageId, array $elements) { + foreach ($elements as $element) { + $this->stageService->getRecordService()->add( + $element->table, + $element->uid + ); + } + $result = $this->getSentToStageWindow($nextStageId); $result['affects'] = array( 'nextStage' => $nextStageId @@ -299,20 +313,27 @@ class ActionHandler extends AbstractHandler { /** * Gets a merged variant of recipient defined by uid and custom ones. * - * @param array list of recipients - * @param string given user string of additional recipients - * @param int stage id + * @param array $uidOfRecipients list of recipients + * @param string $additionalRecipients given user string of additional recipients + * @param int $stageId stage id * @return array + * @throws \InvalidArgumentException */ public function getRecipientList(array $uidOfRecipients, $additionalRecipients, $stageId) { - $finalRecipients = array(); - if (!$this->getStageService()->isValid($stageId)) { + $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageId); + + if ($stageRecord === NULL) { throw new \InvalidArgumentException($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:error.stageId.integer')); - } else { - $stageId = (int)$stageId; } + $recipients = array(); + $finalRecipients = array(); + $backendUserIds = $stageRecord->getAllRecipients(); foreach ($uidOfRecipients as $userUid) { + // Ensure that only configured backend users are considered + if (!in_array($userUid, $backendUserIds)) { + continue; + } $beUserRecord = BackendUtility::getRecord('be_users', (int)$userUid); if (is_array($beUserRecord) && $beUserRecord['email'] !== '') { $uc = $beUserRecord['uc'] ? unserialize($beUserRecord['uc']) : array(); @@ -322,22 +343,26 @@ class ActionHandler extends AbstractHandler { ); } } - // the notification mode can be configured in the workspace stage record - $notification_mode = (int)$this->getStageService()->getNotificationMode($stageId); - if ($notification_mode === StagesService::MODE_NOTIFY_ALL || $notification_mode === StagesService::MODE_NOTIFY_ALL_STRICT) { - // get the default recipients from the stage configuration - // the default recipients needs to be added in some cases of the notification_mode - $default_recipients = $this->getStageService()->getResponsibleBeUser($stageId, TRUE); - foreach ($default_recipients as $default_recipient_uid => $default_recipient_record) { - if (!isset($recipients[$default_recipient_record['email']])) { - $uc = $default_recipient_record['uc'] ? unserialize($default_recipient_record['uc']) : array(); - $recipients[$default_recipient_record['email']] = array( - 'email' => $default_recipient_record['email'], - 'lang' => isset($uc['lang']) ? $uc['lang'] : $default_recipient_record['lang'] + + if ($stageRecord->hasPreselection() && !$stageRecord->isPreselectionChangeable()) { + $preselectedBackendUsers = $this->getStageService()->getBackendUsers( + implode(',', $this->stageService->getPreselectedRecipients($stageRecord)) + ); + + foreach ($preselectedBackendUsers as $preselectedBackendUser) { + if (empty($preselectedBackendUser['email']) || !GeneralUtility::validEmail($preselectedBackendUser['email'])) { + continue; + } + if (!isset($recipients[$preselectedBackendUser['email']])) { + $uc = (!empty($preselectedBackendUser['uc']) ? unserialize($preselectedBackendUser['uc']) : array()); + $recipients[$preselectedBackendUser['email']] = array( + 'email' => $preselectedBackendUser['email'], + 'lang' => (isset($uc['lang']) ? $uc['lang'] : $preselectedBackendUser['lang']) ); } } } + if ($additionalRecipients !== '') { $emails = GeneralUtility::trimExplode(LF, $additionalRecipients, TRUE); $additionalRecipients = array(); @@ -601,43 +626,26 @@ class ActionHandler extends AbstractHandler { /** * Gets the dialog window to be displayed before a record can be sent to a stage. * - * @param $nextStageId + * @param StageRecord|int $nextStageId * @return array */ - protected function getSentToStageWindow($nextStageId) { - $workspaceRec = BackendUtility::getRecord('sys_workspace', $this->getStageService()->getWorkspaceId()); - $showNotificationFields = FALSE; - $stageTitle = $this->getStageService()->getStageTitle($nextStageId); + protected function getSentToStageWindow($nextStage) { + if (!$nextStage instanceof StageRecord) { + $nextStage = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($nextStage); + } + $result = array( 'title' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:actionSendToStage'), 'items' => array( array( 'xtype' => 'panel', 'bodyStyle' => 'margin-bottom: 7px; border: none;', - 'html' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.itemsWillBeSentTo') . ' ' . $stageTitle + 'html' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.itemsWillBeSentTo') . ' ' . $nextStage->getTitle() ) ) ); - switch ($nextStageId) { - case StagesService::STAGE_PUBLISH_EXECUTE_ID: - case StagesService::STAGE_PUBLISH_ID: - if (!empty($workspaceRec['publish_allow_notificaton_settings'])) { - $showNotificationFields = TRUE; - } - break; - case StagesService::STAGE_EDIT_ID: - if (!empty($workspaceRec['edit_allow_notificaton_settings'])) { - $showNotificationFields = TRUE; - } - break; - default: - $allow_notificaton_settings = $this->getStageService()->getPropertyOfCurrentWorkspaceStage($nextStageId, 'allow_notificaton_settings'); - if (!empty($allow_notificaton_settings)) { - $showNotificationFields = TRUE; - } - } - if ($showNotificationFields == TRUE) { + if ($nextStage->isDialogEnabled()) { $result['items'][] = array( 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.sendMailTo'), 'xtype' => 'checkboxgroup', @@ -646,7 +654,7 @@ class ActionHandler extends AbstractHandler { 'style' => 'max-height: 200px', 'autoScroll' => TRUE, 'items' => array( - $this->getReceipientsOfStage($nextStageId) + $this->getReceipientsOfStage($nextStage->getUid()) ) ); $result['items'][] = array( @@ -661,52 +669,45 @@ class ActionHandler extends AbstractHandler { 'name' => 'comments', 'xtype' => 'textarea', 'width' => 250, - 'value' => $this->getDefaultCommentOfStage($nextStageId) + 'value' => ($nextStage->isInternal() ? '' : $nextStage->getDefaultComment()) ); + return $result; } /** * Gets all assigned recipients of a particular stage. * - * @param int $stage + * @param StageRecord|int $stageRecord * @return array */ - protected function getReceipientsOfStage($stage) { + protected function getReceipientsOfStage($stageRecord) { + if (!$stageRecord instanceof StageRecord) { + $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageRecord); + } + $result = array(); - $recipients = $this->getStageService()->getResponsibleBeUser($stage); - $default_recipients = $this->getStageService()->getResponsibleBeUser($stage, TRUE); - foreach ($recipients as $id => $user) { - if (GeneralUtility::validEmail($user['email'])) { - $checked = FALSE; - $disabled = FALSE; - $name = $user['realName'] ? $user['realName'] : $user['username']; - // the notification mode can be configured in the workspace stage record - $notification_mode = (int)$this->getStageService()->getNotificationMode($stage); - if ($notification_mode === StagesService::MODE_NOTIFY_SOMEONE) { - // all responsible users are checked per default, as in versions before - $checked = TRUE; - } elseif ($notification_mode === StagesService::MODE_NOTIFY_ALL) { - // the default users are checked only - if (!empty($default_recipients[$id])) { - $checked = TRUE; - $disabled = TRUE; - } else { - $checked = FALSE; - } - } elseif ($notification_mode === StagesService::MODE_NOTIFY_ALL_STRICT) { - // all responsible users are checked - $checked = TRUE; - $disabled = TRUE; - } - $result[] = array( - 'boxLabel' => sprintf('%s (%s)', $name, $user['email']), - 'name' => 'receipients-' . $id, - 'checked' => $checked, - 'disabled' => $disabled - ); + $allRecipients = $this->getStageService()->getResponsibleBeUser($stageRecord); + $preselectedRecipients = $this->stageService->getPreselectedRecipients($stageRecord); + $isPreselectionChangeable = $stageRecord->isPreselectionChangeable(); + + foreach ($allRecipients as $backendUserId => $backendUser) { + if (empty($backendUser['email']) || !GeneralUtility::validEmail($backendUser['email'])) { + continue; } + + $name = (!empty($backendUser['realName']) ? $backendUser['realName'] : $backendUser['username']); + $checked = in_array($backendUserId, $preselectedRecipients); + $disabled = ($checked && !$isPreselectionChangeable); + + $result[] = array( + 'boxLabel' => sprintf('%s (%s)', $name, $backendUser['email']), + 'name' => 'receipients-' . $backendUserId, + 'checked' => $checked, + 'disabled' => $disabled + ); } + return $result; } diff --git a/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php b/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php index b90617fc15c9..d50694310cab 100644 --- a/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php +++ b/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php @@ -18,6 +18,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; /** * ExtDirect server @@ -297,4 +298,11 @@ class ExtDirectServer extends AbstractHandler { return $this->stagesService; } + /** + * @return \TYPO3\CMS\Extbase\Object\ObjectManager + */ + protected function getObjectManager() { + return GeneralUtility::makeInstance(ObjectManager::class); + } + } diff --git a/typo3/sysext/workspaces/Classes/Service/RecordService.php b/typo3/sysext/workspaces/Classes/Service/RecordService.php new file mode 100644 index 000000000000..c7b391339399 --- /dev/null +++ b/typo3/sysext/workspaces/Classes/Service/RecordService.php @@ -0,0 +1,85 @@ +<?php +namespace TYPO3\CMS\Workspaces\Service; + +/** + * 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\Workspaces\Domain\Model\DatabaseRecord; + +/** + * Service for records + */ +class RecordService implements \TYPO3\CMS\Core\SingletonInterface { + + /** + * @var DatabaseRecord[] + */ + protected $records = array(); + + /** + * @param string $tableName + * @param int $id + */ + public function add($tableName, $id) { + $databaseRecord = DatabaseRecord::create($tableName, $id); + if (!isset($this->records[$databaseRecord->getIdentifier()])) { + $this->records[$databaseRecord->getIdentifier()] = $databaseRecord; + } + } + + /** + * @return array + */ + public function getIdsPerTable() { + $idsPerTable = array(); + foreach ($this->records as $databaseRecord) { + if (!isset($idsPerTable[$databaseRecord->getTable()])) { + $idsPerTable[$databaseRecord->getTable()] = array(); + } + $idsPerTable[$databaseRecord->getTable()][] = $databaseRecord->getUid(); + } + return $idsPerTable; + } + + /** + * @return array + */ + public function getCreateUserIds() { + $createUserIds = array(); + foreach ($this->getIdsPerTable() as $tableName => $ids) { + if (empty($GLOBALS['TCA'][$tableName]['ctrl']['cruser_id'])) { + continue; + } + $createUserIdFieldName = $GLOBALS['TCA'][$tableName]['ctrl']['cruser_id']; + $records = $this->getDatabaseConnection()->exec_SELECTgetRows( + $createUserIdFieldName, $tableName, + 'uid IN (' . implode(',', $ids) . ')', + $createUserIdFieldName, + '', '', + $createUserIdFieldName + ); + if (!empty($records)) { + $createUserIds = array_merge($createUserIds, array_keys($records)); + } + } + return array_unique($createUserIds); + } + + /** + * @return \TYPO3\CMS\Core\Database\DatabaseConnection + */ + protected function getDatabaseConnection() { + return $GLOBALS['TYPO3_DB']; + } + +} diff --git a/typo3/sysext/workspaces/Classes/Service/StagesService.php b/typo3/sysext/workspaces/Classes/Service/StagesService.php index d064c19139fd..439999c4071c 100644 --- a/typo3/sysext/workspaces/Classes/Service/StagesService.php +++ b/typo3/sysext/workspaces/Classes/Service/StagesService.php @@ -17,11 +17,13 @@ namespace TYPO3\CMS\Workspaces\Service; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; +use TYPO3\CMS\Workspaces\Domain\Record\WorkspaceRecord; +use TYPO3\CMS\Workspaces\Domain\Record\StageRecord; /** * Stages service */ -class StagesService { +class StagesService implements \TYPO3\CMS\Core\SingletonInterface { const TABLE_STAGE = 'sys_workspace_stage'; // if a record is in the "ready to publish" stage STAGE_PUBLISH_ID the nextStage is STAGE_PUBLISH_EXECUTE_ID, this id wont be saved at any time in db @@ -40,6 +42,11 @@ class StagesService { */ private $pathToLocallang = 'LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf'; + /** + * @var RecordService + */ + protected $recordService; + /** * Local cache to reduce number of database queries for stages, groups, etc. * @@ -173,35 +180,12 @@ class StagesService { * @return array id and title of the stages */ public function getStagesForWS() { - $stages = array(); if (isset($this->workspaceStageCache[$this->getWorkspaceId()])) { $stages = $this->workspaceStageCache[$this->getWorkspaceId()]; + } elseif ($this->getWorkspaceId() === 0) { + $stages = array(); } else { - $stages[] = array( - 'uid' => self::STAGE_EDIT_ID, - 'title' => $GLOBALS['LANG']->sL(($this->pathToLocallang . ':actionSendToStage')) . ' "' - . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_user_ws.xlf:stage_editing') . '"' - ); - $workspaceRec = BackendUtility::getRecord('sys_workspace', $this->getWorkspaceId()); - if ($workspaceRec['custom_stages'] > 0) { - // Get all stage records for this workspace - $where = 'parentid=' . $this->getWorkspaceId() . ' AND parenttable=' - . $GLOBALS['TYPO3_DB']->fullQuoteStr('sys_workspace', self::TABLE_STAGE) . ' AND deleted=0'; - $workspaceStageRecs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', self::TABLE_STAGE, $where, '', 'sorting', '', 'uid'); - foreach ($workspaceStageRecs as $stage) { - $stage['title'] = $GLOBALS['LANG']->sL(($this->pathToLocallang . ':actionSendToStage')) . ' "' . $stage['title'] . '"'; - $stages[] = $stage; - } - } - $stages[] = array( - 'uid' => self::STAGE_PUBLISH_ID, - 'title' => $GLOBALS['LANG']->sL(($this->pathToLocallang . ':actionSendToStage')) . ' "' - . $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang_mod.xlf:stage_ready_to_publish') . '"' - ); - $stages[] = array( - 'uid' => self::STAGE_PUBLISH_EXECUTE_ID, - 'title' => $GLOBALS['LANG']->sL($this->pathToLocallang . ':publish_execute_action_option') - ); + $stages = $this->prepareStagesArray($this->getWorkspaceRecord()->getStages()); $this->workspaceStageCache[$this->getWorkspaceId()] = $stages; } return $stages; @@ -213,39 +197,58 @@ class StagesService { * @return array id and title of stages */ public function getStagesForWSUser() { - $stagesForWSUserData = array(); + if ($GLOBALS['BE_USER']->isAdmin()) { + return $this->getStagesForWS(); + } + + /** @var $allowedStages StageRecord[] */ $allowedStages = array(); - $orderedAllowedStages = array(); - $workspaceStageRecs = $this->getStagesForWS(); - if (is_array($workspaceStageRecs) && !empty($workspaceStageRecs)) { - if ($GLOBALS['BE_USER']->isAdmin()) { - $orderedAllowedStages = $workspaceStageRecs; + $stageRecords = $this->getWorkspaceRecord()->getStages(); + + // Only use stages that are allowed for current backend user + foreach ($stageRecords as $stageRecord) { + if ($stageRecord->isAllowed()) { + $allowedStages[$stageRecord->getUid()] = $stageRecord; + } + } + + // Add previous and next stages (even if they are not allowed!) + foreach ($allowedStages as $allowedStage) { + $previousStage = $allowedStage->getPrevious(); + $nextStage = $allowedStage->getNext(); + if ($previousStage !== NULL && !isset($allowedStages[$previousStage->getUid()])) { + $allowedStages[$previousStage->getUid()] = $previousStage; + } + if ($nextStage !== NULL && !isset($allowedStages[$nextStage->getUid()])) { + $allowedStages[$nextStage->getUid()] = $nextStage; + } + } + + uasort($allowedStages, function(StageRecord $first, StageRecord $second) { return $first->determineOrder($second); }); + return $this->prepareStagesArray($allowedStages); + } + + /** + * Prepares simplified stages array to be used in ExtJs components. + * + * @param StageRecord[] $stageRecords + * @return array + */ + protected function prepareStagesArray(array $stageRecords) { + $stagesArray = array(); + foreach ($stageRecords as $stageRecord) { + $stage = array( + 'uid' => $stageRecord->getUid(), + 'label' => $stageRecord->getTitle(), + ); + if (!$stageRecord->isExecuteStage()) { + $stage['title'] = $GLOBALS['LANG']->sL(($this->pathToLocallang . ':actionSendToStage')) . ' "' . $stageRecord->getTitle() . '"'; } else { - foreach ($workspaceStageRecs as $workspaceStageRec) { - if ($workspaceStageRec['uid'] === self::STAGE_EDIT_ID) { - $allowedStages[self::STAGE_EDIT_ID] = $workspaceStageRec; - $stagesForWSUserData[$workspaceStageRec['uid']] = $workspaceStageRec; - } elseif ($this->isStageAllowedForUser($workspaceStageRec['uid'])) { - $stagesForWSUserData[$workspaceStageRec['uid']] = $workspaceStageRec; - } elseif ($workspaceStageRec['uid'] == self::STAGE_PUBLISH_EXECUTE_ID && $GLOBALS['BE_USER']->workspacePublishAccess($this->getWorkspaceId())) { - $allowedStages[] = $workspaceStageRec; - $stagesForWSUserData[$workspaceStageRec['uid']] = $workspaceStageRec; - } - } - foreach ($stagesForWSUserData as $allowedStage) { - $nextStage = $this->getNextStage($allowedStage['uid']); - $prevStage = $this->getPrevStage($allowedStage['uid']); - if (isset($prevStage['uid'])) { - $allowedStages[$prevStage['uid']] = $prevStage; - } - if (isset($nextStage['uid'])) { - $allowedStages[$nextStage['uid']] = $nextStage; - } - } - $orderedAllowedStages = array_values($allowedStages); + $stage['title'] = $GLOBALS['LANG']->sL($this->pathToLocallang . ':publish_execute_action_option'); } + $stagesArray[] = $stage; } - return $orderedAllowedStages; + return $stagesArray; } /** @@ -406,95 +409,129 @@ class StagesService { } /** - * Get array of all responsilbe be_users for a stage + * Gets all backend user records that are considered to be responsible + * for a particular stage or workspace. * - * @param int $stageId Stage id + * @param StageRecord|int $stageRecord Stage * @param bool $selectDefaultUserField If field notification_defaults should be selected instead of responsible users * @return array be_users with e-mail and name */ - public function getResponsibleBeUser($stageId, $selectDefaultUserField = FALSE) { - $workspaceRec = BackendUtility::getRecord('sys_workspace', $this->getWorkspaceId()); + public function getResponsibleBeUser($stageRecord, $selectDefaultUserField = FALSE) { + if (!$stageRecord instanceof StageRecord) { + $stageRecord = $this->getWorkspaceRecord()->getStage($stageRecord); + } + $recipientArray = array(); - switch ($stageId) { - case self::STAGE_PUBLISH_EXECUTE_ID: - case self::STAGE_PUBLISH_ID: - if (!$selectDefaultUserField) { - $userList = $this->getResponsibleUser($workspaceRec['adminusers'] . ',' . $workspaceRec['members']); - } else { - $notification_default_user = $workspaceRec['publish_notification_defaults']; - $userList = $this->getResponsibleUser($notification_default_user); - } - break; - case self::STAGE_EDIT_ID: - if (!$selectDefaultUserField) { - $userList = $this->getResponsibleUser($workspaceRec['adminusers'] . ',' . $workspaceRec['members']); - } else { - $notification_default_user = $workspaceRec['edit_notification_defaults']; - $userList = $this->getResponsibleUser($notification_default_user); - } - break; - default: - if (!$selectDefaultUserField) { - $responsible_persons = $this->getPropertyOfCurrentWorkspaceStage($stageId, 'responsible_persons'); - $userList = $this->getResponsibleUser($responsible_persons); - } else { - $notification_default_user = $this->getPropertyOfCurrentWorkspaceStage($stageId, 'notification_defaults'); - $userList = $this->getResponsibleUser($notification_default_user); - } - } - if (!empty($userList)) { - $userRecords = BackendUtility::getUserNames( - 'username, uid, email, realName', - 'AND uid IN (' . $userList . ')' . BackendUtility::BEenableFields('be_users') - ); + if (!$selectDefaultUserField) { + $backendUserIds = $stageRecord->getAllRecipients(); + } else { + $backendUserIds = $stageRecord->getDefaultRecipients(); } - if (!empty($userRecords) && is_array($userRecords)) { - foreach ($userRecords as $userUid => $userRecord) { - $recipientArray[$userUid] = $userRecord; - } + + $userList = implode(',', $backendUserIds); + $userRecords = $this->getBackendUsers($userList); + foreach ($userRecords as $userUid => $userRecord) { + $recipientArray[$userUid] = $userRecord; } return $recipientArray; } /** - * Get uids of all responsilbe persons for a stage + * Gets backend user ids from a mixed list of backend users + * and backend users groups. This is used for notifying persons + * responsible for a particular stage or workspace. * * @param string $stageRespValue Responsible_person value from stage record - * @return string Uid list of responsible be_users + * @return string List of backend user ids */ public function getResponsibleUser($stageRespValue) { - $stageValuesArray = GeneralUtility::trimExplode(',', $stageRespValue, TRUE); - $beuserUidArray = array(); - $begroupUidArray = array(); + return implode(',', $this->resolveBackendUserIds($stageRespValue)); + } + + /** + * Resolves backend user ids from a mixed list of backend users + * and backend user groups (e.g. "be_users_1,be_groups_3,be_users_4,...") + * + * @param string $backendUserGroupList + * @return array + */ + public function resolveBackendUserIds($backendUserGroupList) { + $elements = GeneralUtility::trimExplode(',', $backendUserGroupList, TRUE); + $backendUserIds = array(); + $backendGroupIds = array(); - foreach ($stageValuesArray as $uidvalue) { - if (strstr($uidvalue, 'be_users') !== FALSE) { + foreach ($elements as $element) { + if (strpos($element, 'be_users_') === 0) { // Current value is a uid of a be_user record - $beuserUidArray[] = str_replace('be_users_', '', $uidvalue); - } elseif (strstr($uidvalue, 'be_groups') !== FALSE) { - $begroupUidArray[] = str_replace('be_groups_', '', $uidvalue); - } elseif ((int)$uidvalue) { - $beuserUidArray[] = (int)$uidvalue; + $backendUserIds[] = str_replace('be_users_', '', $element); + } elseif (strpos($element, 'be_groups_') === 0) { + $backendGroupIds[] = str_replace('be_groups_', '', $element); + } elseif ((int)$element) { + $backendUserIds[] = (int)$element; } } - if (!empty($begroupUidArray)) { + if (!empty($backendGroupIds)) { $allBeUserArray = BackendUtility::getUserNames(); - $begroupUidList = implode(',', $begroupUidArray); + $backendGroupList = implode(',', $backendGroupIds); $this->userGroups = array(); - $begroupUidArray = $this->fetchGroups($begroupUidList); - foreach ($begroupUidArray as $groupkey => $groupData) { - foreach ($allBeUserArray as $useruid => $userdata) { - if (GeneralUtility::inList($userdata['usergroup_cached_list'], $groupData['uid'])) { - $beuserUidArray[] = $useruid; + $backendGroups = $this->fetchGroups($backendGroupList); + foreach ($backendGroups as $backendGroup) { + foreach ($allBeUserArray as $backendUserId => $backendUser) { + if (GeneralUtility::inList($backendUser['usergroup_cached_list'], $backendGroup['uid'])) { + $backendUserIds[] = $backendUserId; } } } } - array_unique($beuserUidArray); - return implode(',', $beuserUidArray); + return array_unique($backendUserIds); + } + + /** + * Gets backend user records from a given list of ids. + * + * @param string $backendUserList + * @return array + */ + public function getBackendUsers($backendUserList) { + if (empty($backendUserList)) { + return array(); + } + + $backendUserList = $this->getDatabaseConnection()->cleanIntList($backendUserList); + $backendUsers = BackendUtility::getUserNames( + 'username, uid, email, realName', + 'AND uid IN (' . $backendUserList . ')' . BackendUtility::BEenableFields('be_users') + ); + + if (empty($backendUsers)) { + $backendUsers = array(); + } + return $backendUsers; + } + + /** + * @param StageRecord $stageRecord + * @return array + */ + public function getPreselectedRecipients(StageRecord $stageRecord) { + if ($stageRecord->areEditorsPreselected()) { + return array_merge( + $stageRecord->getPreselectedRecipients(), + $this->getRecordService()->getCreateUserIds() + ); + } else { + return $stageRecord->getPreselectedRecipients(); + } + } + + /** + * @return WorkspaceRecord + */ + protected function getWorkspaceRecord() { + return WorkspaceRecord::get($this->getWorkspaceId()); } /** @@ -733,6 +770,16 @@ class StagesService { } } + /** + * @return RecordService + */ + public function getRecordService() { + if (!isset($this->recordService)) { + $this->recordService = GeneralUtility::makeInstance(RecordService::class); + } + return $this->recordService; + } + /** * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */ @@ -740,4 +787,11 @@ class StagesService { return $GLOBALS['BE_USER']; } + /** + * @return \TYPO3\CMS\Core\Database\DatabaseConnection + */ + protected function getDatabaseConnection() { + return $GLOBALS['TYPO3_DB']; + } + } diff --git a/typo3/sysext/workspaces/Configuration/TCA/sys_workspace.php b/typo3/sysext/workspaces/Configuration/TCA/sys_workspace.php index 947ca4700d28..70ff4e409756 100644 --- a/typo3/sysext/workspaces/Configuration/TCA/sys_workspace.php +++ b/typo3/sysext/workspaces/Configuration/TCA/sys_workspace.php @@ -179,6 +179,7 @@ return array( ), 'default' => 0 ), + // @deprecated not used anymore 'edit_notification_mode' => array( 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.edit_notification_mode', 'config' => array( @@ -191,8 +192,8 @@ return array( ) ), 'edit_notification_defaults' => array( - 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.edit_notification_defaults', - 'displayCond' => 'FIELD:edit_notification_mode:IN:0,1', + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace_stage.notification_defaults', + 'displayCond' => 'FIELD:edit_allow_notificaton_settings:BIT:1', 'config' => array( 'type' => 'group', 'internal_type' => 'db', @@ -210,12 +211,31 @@ return array( ) ), 'edit_allow_notificaton_settings' => array( - 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.edit_allow_notificaton_settings', + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog', 'config' => array( 'type' => 'check', - 'default' => 1 + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.showDialog', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.changeablePreselection', ''), + ), + 'default' => 3, + 'cols' => 2, + ) + ), + 'edit_notification_preselection' => array( + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection', + 'config' => array( + 'type' => 'check', + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.owners', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.members', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.editors', ''), + ), + 'default' => 2, + 'cols' => 3, ) ), + // @deprecated not used anymore 'publish_notification_mode' => array( 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.publish_notification_mode', 'config' => array( @@ -228,8 +248,8 @@ return array( ) ), 'publish_notification_defaults' => array( - 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.publish_notification_defaults', - 'displayCond' => 'FIELD:publish_notification_mode:IN:0,1', + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace_stage.notification_defaults', + 'displayCond' => 'FIELD:publish_allow_notificaton_settings:BIT:1', 'config' => array( 'type' => 'group', 'internal_type' => 'db', @@ -247,17 +267,96 @@ return array( ) ), 'publish_allow_notificaton_settings' => array( - 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.publish_allow_notificaton_settings', + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog', + 'config' => array( + 'type' => 'check', + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.showDialog', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.changeablePreselection', ''), + ), + 'default' => 3, + 'cols' => 2, + ) + ), + 'publish_notification_preselection' => array( + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection', + 'config' => array( + 'type' => 'check', + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.owners', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.members', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.editors', ''), + ), + 'default' => 1, + 'cols' => 3, + ) + ), + 'execute_notification_defaults' => array( + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace_stage.notification_defaults', + 'displayCond' => 'FIELD:execute_allow_notificaton_settings:BIT:1', + 'config' => array( + 'type' => 'group', + 'internal_type' => 'db', + 'allowed' => 'be_users,be_groups', + 'prepend_tname' => 1, + 'size' => '3', + 'maxitems' => '100', + 'autoSizeMax' => 20, + 'show_thumbs' => '1', + 'wizards' => array( + 'suggest' => array( + 'type' => 'suggest' + ) + ) + ) + ), + 'execute_allow_notificaton_settings' => array( + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog', 'config' => array( 'type' => 'check', - 'default' => 1 + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.showDialog', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.changeablePreselection', ''), + ), + 'default' => 3, + 'cols' => 2, + ) + ), + 'execute_notification_preselection' => array( + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection', + 'config' => array( + 'type' => 'check', + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.owners', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.members', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.editors', ''), + ), + 'default' => 3, + 'cols' => 3, ) ) ), + 'palettes' => array( + 'stage.edit' => array( + 'canNotCollapse' => TRUE, + 'showitem' => 'edit_allow_notificaton_settings, edit_notification_preselection,', + ), + 'stage.publish' => array( + 'canNotCollapse' => TRUE, + 'showitem' => 'publish_allow_notificaton_settings, publish_notification_preselection,', + ), + 'stage.execute' => array( + 'canNotCollapse' => TRUE, + 'showitem' => 'execute_allow_notificaton_settings, execute_notification_preselection,', + ) + ), 'types' => array( '0' => array('showitem' => 'title,description, --div--;LLL:EXT:lang/locallang_tca.xlf:sys_filemounts.tabs.users,adminusers,members, - --div--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:tabs.notification_settings,stagechg_notification,edit_notification_mode,edit_notification_defaults,edit_allow_notificaton_settings,publish_notification_mode,publish_notification_defaults,publish_allow_notificaton_settings, + --div--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:tabs.notification_settings, stagechg_notification, + --palette--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xml:sys_workspace.palette.stage.edit;stage.edit, edit_notification_defaults, + --palette--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xml:sys_workspace.palette.stage.publish;stage.publish, publish_notification_defaults, + --palette--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xml:sys_workspace.palette.stage.execute;stage.execute, execute_notification_defaults, --div--;LLL:EXT:lang/locallang_tca.xlf:sys_filemounts.tabs.mountpoints,db_mountpoints,file_mountpoints, --div--;LLL:EXT:lang/locallang_tca.xlf:sys_filemounts.tabs.publishing,publish_time,unpublish_time, --div--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_filemounts.tabs.staging,custom_stages, diff --git a/typo3/sysext/workspaces/Configuration/TCA/sys_workspace_stage.php b/typo3/sysext/workspaces/Configuration/TCA/sys_workspace_stage.php index fd4813c0a268..ebc9c768c205 100644 --- a/typo3/sysext/workspaces/Configuration/TCA/sys_workspace_stage.php +++ b/typo3/sysext/workspaces/Configuration/TCA/sys_workspace_stage.php @@ -78,7 +78,7 @@ return array( ), 'notification_defaults' => array( 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace_stage.notification_defaults', - 'displayCond' => 'FIELD:notification_mode:IN:0,1', + 'displayCond' => 'FIELD:allow_notificaton_settings:BIT:1', 'config' => array( 'type' => 'group', 'internal_type' => 'db', @@ -96,16 +96,41 @@ return array( ) ), 'allow_notificaton_settings' => array( - 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace_stage.allow_notificaton_settings', + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog', 'config' => array( 'type' => 'check', - 'default' => 1 + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.showDialog', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.settingsDialog.changeablePreselection', ''), + ), + 'default' => 3, + 'cols' => 2, + ) + ), + 'notification_preselection' => array( + 'label' => 'LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection', + 'config' => array( + 'type' => 'check', + 'items' => array( + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.owners', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.members', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.editors', ''), + array('LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:sys_workspace.preselection.responsiblePersons', ''), + ), + 'default' => 8, + 'cols' => 4, ) ) ), + 'palettes' => array( + 'stage' => array( + 'canNotCollapse' => TRUE, + 'showitem' => 'allow_notificaton_settings, notification_preselection,', + ) + ), 'types' => array( '0' => array('showitem' => ' --div--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:tabs.general,title,responsible_persons, - --div--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:tabs.notification_settings,notification_mode,notification_defaults,allow_notificaton_settings,default_mailcomment') + --div--;LLL:EXT:workspaces/Resources/Private/Language/locallang_db.xlf:tabs.notification_settings,--palette--;;stage, notification_defaults, default_mailcomment') ) ); diff --git a/typo3/sysext/workspaces/Resources/Private/Language/locallang_db.xlf b/typo3/sysext/workspaces/Resources/Private/Language/locallang_db.xlf index cead9329d9f7..0f6dcb266111 100644 --- a/typo3/sysext/workspaces/Resources/Private/Language/locallang_db.xlf +++ b/typo3/sysext/workspaces/Resources/Private/Language/locallang_db.xlf @@ -57,6 +57,9 @@ <trans-unit id="tabs.general"> <source>General</source> </trans-unit> + <trans-unit id="sys_workspace.execute_notification_defaults"> + <source>Publishing execute stage: default notification mail recipients</source> + </trans-unit> <trans-unit id="sys_workspace_stage.notification_mode"> <source>Recipient suggestion checkboxes</source> </trans-unit> @@ -66,6 +69,39 @@ <trans-unit id="sys_workspace_stage.allow_notificaton_settings"> <source>Allow notification settings during stage change</source> </trans-unit> + <trans-unit id="sys_workspace.palette.stage.edit"> + <source>Stage "editing":</source> + </trans-unit> + <trans-unit id="sys_workspace.palette.stage.publish"> + <source>Stage "ready to publish":</source> + </trans-unit> + <trans-unit id="sys_workspace.palette.stage.execute"> + <source>Stage "publishing execute":</source> + </trans-unit> + <trans-unit id="sys_workspace.settingsDialog"> + <source>Settings dialog</source> + </trans-unit> + <trans-unit id="sys_workspace.settingsDialog.showDialog"> + <source>show dialog</source> + </trans-unit> + <trans-unit id="sys_workspace.settingsDialog.changeablePreselection"> + <source>changeable preselection</source> + </trans-unit> + <trans-unit id="sys_workspace.preselection"> + <source>Preselection</source> + </trans-unit> + <trans-unit id="sys_workspace.preselection.owners"> + <source>owners</source> + </trans-unit> + <trans-unit id="sys_workspace.preselection.members"> + <source>members</source> + </trans-unit> + <trans-unit id="sys_workspace.preselection.editors"> + <source>editors</source> + </trans-unit> + <trans-unit id="sys_workspace.preselection.responsiblePersons"> + <source>responsible persons</source> + </trans-unit> </body> </file> </xliff> diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js index 25eccdba405e..13e2ecdbd935 100644 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js @@ -172,7 +172,13 @@ TYPO3.Workspaces.Actions = { }); }, sendToSpecificStageWindow: function(selection, nextStage) { - TYPO3.Workspaces.ExtDirectActions.sendToSpecificStageWindow(nextStage, function(response) { + var elements = []; + + Ext.each(selection, function(row) { + elements.push({table: row.json.table, uid: row.json.uid}) + }); + + TYPO3.Workspaces.ExtDirectActions.sendToSpecificStageWindow(nextStage, elements, function(response) { TYPO3.Workspaces.Actions.currentSendToMode = 'specific'; TYPO3.Workspaces.Actions.sendToStageWindow(response, selection); }); diff --git a/typo3/sysext/workspaces/ext_tables.sql b/typo3/sysext/workspaces/ext_tables.sql index c97bf9066831..ebc980bb8223 100644 --- a/typo3/sysext/workspaces/ext_tables.sql +++ b/typo3/sysext/workspaces/ext_tables.sql @@ -24,10 +24,16 @@ CREATE TABLE sys_workspace ( stagechg_notification tinyint(3) DEFAULT '0' NOT NULL, edit_notification_mode tinyint(3) DEFAULT '0' NOT NULL, edit_notification_defaults varchar(255) DEFAULT '' NOT NULL, + edit_notification_preselection tinyint(3) DEFAULT '3' NOT NULL, edit_allow_notificaton_settings tinyint(3) DEFAULT '0' NOT NULL, publish_notification_mode tinyint(3) DEFAULT '0' NOT NULL, publish_notification_defaults varchar(255) DEFAULT '' NOT NULL, + publish_notification_preselection tinyint(3) DEFAULT '3' NOT NULL, publish_allow_notificaton_settings tinyint(3) DEFAULT '0' NOT NULL, + execute_notification_mode tinyint(3) DEFAULT '0' NOT NULL, + execute_notification_defaults varchar(255) DEFAULT '' NOT NULL, + execute_notification_preselection tinyint(3) DEFAULT '3' NOT NULL, + execute_allow_notificaton_settings tinyint(3) DEFAULT '0' NOT NULL, PRIMARY KEY (uid), KEY parent (pid) @@ -51,6 +57,7 @@ CREATE TABLE sys_workspace_stage ( notification_mode tinyint(3) DEFAULT '0' NOT NULL, notification_defaults varchar(255) DEFAULT '' NOT NULL, allow_notificaton_settings tinyint(3) DEFAULT '0' NOT NULL, + notification_preselection tinyint(3) DEFAULT '8' NOT NULL, PRIMARY KEY (uid), KEY parent (pid) -- GitLab