[FEATURE] Integrate language handling in workspace module
authorOliver Hader <oliver@typo3.org>
Sat, 18 Aug 2012 20:34:02 +0000 (22:34 +0200)
committerOliver Hader <oliver.hader@typo3.org>
Mon, 20 Aug 2012 13:43:18 +0000 (15:43 +0200)
Integrate language handling in workspace module. First a new
column "language" is integrated to the toolbar section that
shows the available sys_language records of the system. This
column allows to sort by the language uid as well.

Besides that a new virtual column "record status" is itegrated.
This is needed if e.g. a translated record and the default
parent record are new on the current workspace and only the
translated record shall be published - in this case the
localization might not be shown in the frontend since it just
cannot overlay the default record which is not available.
This behaviour is called "integrity check of records".

Change-Id: I89370281f6fdcd5d34e805b2d093a020fbdd4b2a
Resolves: #36790
Releases: 6.0
Reviewed-on: http://review.typo3.org/13902
Reviewed-by: Wouter Wolters
Reviewed-by: Oliver Hader
Tested-by: Oliver Hader
22 files changed:
typo3/sysext/workspaces/Classes/Controller/AbstractController.php
typo3/sysext/workspaces/Classes/Domain/Model/CombinedRecord.php [new file with mode: 0644]
typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php [new file with mode: 0644]
typo3/sysext/workspaces/Classes/ExtDirect/AbstractHandler.php
typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php
typo3/sysext/workspaces/Classes/ExtDirect/MassActionHandler.php
typo3/sysext/workspaces/Classes/ExtDirect/Server.php
typo3/sysext/workspaces/Classes/Service/Befunc.php
typo3/sysext/workspaces/Classes/Service/GridData.php
typo3/sysext/workspaces/Classes/Service/Integrity.php [new file with mode: 0644]
typo3/sysext/workspaces/Classes/Service/Workspaces.php
typo3/sysext/workspaces/Resources/Private/Language/locallang.xlf
typo3/sysext/workspaces/Resources/Public/JavaScript/Store/mainstore.js
typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js
typo3/sysext/workspaces/Resources/Public/JavaScript/component.js
typo3/sysext/workspaces/Resources/Public/JavaScript/configuration.js
typo3/sysext/workspaces/Resources/Public/JavaScript/grid.js
typo3/sysext/workspaces/Resources/Public/JavaScript/helpers.js
typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar.js
typo3/sysext/workspaces/Resources/Public/JavaScript/workspaces.js
typo3/sysext/workspaces/Resources/Public/StyleSheet/module.css
typo3/sysext/workspaces/ext_autoload.php

index 7402489..659b6db 100644 (file)
@@ -59,8 +59,19 @@ class Tx_Workspaces_Controller_AbstractController extends Tx_Extbase_MVC_Control
                // @todo Evaluate how the intval() call can be used with Extbase validators/filters
                $this->pageId = intval(t3lib_div::_GP('id'));
 
+               $icons = array(
+                       'language' => t3lib_iconWorks::getSpriteIconClasses('flags-multiple'),
+                       'integrity' => t3lib_iconWorks::getSpriteIconClasses('status-dialog-information'),
+                       'success' => t3lib_iconWorks::getSpriteIconClasses('status-dialog-ok'),
+                       'info' => t3lib_iconWorks::getSpriteIconClasses('status-dialog-information'),
+                       'warning' => t3lib_iconWorks::getSpriteIconClasses('status-dialog-warning'),
+                       'error' => t3lib_iconWorks::getSpriteIconClasses('status-dialog-error'),
+               );
+
+               $this->pageRenderer->addInlineSetting('Workspaces', 'icons', $icons);
                $this->pageRenderer->addInlineSetting('Workspaces', 'id', $this->pageId);
                $this->pageRenderer->addInlineSetting('Workspaces', 'depth', ($this->pageId === 0 ? 999 : 1));
+               $this->pageRenderer->addInlineSetting('Workspaces', 'language', $this->getLanguageSelection());
 
                $this->pageRenderer->addCssFile(t3lib_extMgm::extRelPath('workspaces') . 'Resources/Public/StyleSheet/module.css');
 
@@ -104,5 +115,20 @@ class Tx_Workspaces_Controller_AbstractController extends Tx_Extbase_MVC_Control
 
                $response->setContent($pageHeader . $response->getContent() . $pageEnd);
        }
+
+       /**
+        * Gets the selected language.
+        *
+        * @return string
+        */
+       protected function getLanguageSelection() {
+               $language = 'all';
+
+               if (isset($GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['language'])) {
+                       $language = $GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['language'];
+               }
+
+               return $language;
+       }
 }
 ?>
\ No newline at end of file
diff --git a/typo3/sysext/workspaces/Classes/Domain/Model/CombinedRecord.php b/typo3/sysext/workspaces/Classes/Domain/Model/CombinedRecord.php
new file mode 100644 (file)
index 0000000..33b70b5
--- /dev/null
@@ -0,0 +1,173 @@
+<?php
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2012 Oliver Hader <oliver.hader@typo3.org>
+ * All rights reserved
+ *
+ * This script is part of the TYPO3 project. The TYPO3 project is
+ * free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The GNU General Public License can be found at
+ * http://www.gnu.org/copyleft/gpl.html.
+ * A copy is found in the textfile GPL.txt and important notices to the license
+ * from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ * This script is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ * @package Workspaces
+ * @subpackage Domain
+ */
+class Tx_Workspaces_Domain_Model_CombinedRecord {
+       /**
+        * @var string
+        */
+       protected $table;
+
+       /**
+        * @var Tx_Workspaces_Domain_Model_DatabaseRecord
+        */
+       protected $versionRecord;
+
+       /**
+        * @var Tx_Workspaces_Domain_Model_DatabaseRecord
+        */
+       protected $liveRecord;
+
+       /**
+        * Creates combined record object just by live-id and version-id of database record rows.
+        *
+        * @param string $table Name of the database table
+        * @param integer $liveId Id of the database live-record row
+        * @param integer $versionId Id of the datbase version-record row
+        * @return Tx_Workspaces_Domain_Model_CombinedRecord
+        */
+       public static function create($table, $liveId, $versionId) {
+               $liveRecord = Tx_Workspaces_Domain_Model_DatabaseRecord::create($table, $liveId);
+               $versionRecord = Tx_Workspaces_Domain_Model_DatabaseRecord::create($table, $versionId);
+
+               return t3lib_div::makeInstance(
+                       'Tx_Workspaces_Domain_Model_CombinedRecord',
+                       $table, $liveRecord, $versionRecord
+               );
+       }
+
+       /**
+        * Creates combined record object by relevant database live-record and version-record rows.
+        *
+        * @param string $table Name of the database table
+        * @param array $liveRow The relevant datbase live-record row
+        * @param array $versionRow The relevant database version-record row
+        * @return Tx_Workspaces_Domain_Model_CombinedRecord
+        */
+       public static function createFromArrays($table, array $liveRow, array $versionRow) {
+               $liveRecord = Tx_Workspaces_Domain_Model_DatabaseRecord::createFromArray($table, $liveRow);
+               $versionRecord = Tx_Workspaces_Domain_Model_DatabaseRecord::createFromArray($table, $versionRow);
+
+               return t3lib_div::makeInstance(
+                       'Tx_Workspaces_Domain_Model_CombinedRecord',
+                       $table, $liveRecord, $versionRecord
+               );
+       }
+
+       /**
+        * Creates this object.
+        *
+        * @param string $table
+        * @param Tx_Workspaces_Domain_Model_DatabaseRecord $liveRecord
+        * @param Tx_Workspaces_Domain_Model_DatabaseRecord $versionRecord
+        */
+       public function __construct($table, Tx_Workspaces_Domain_Model_DatabaseRecord $liveRecord, Tx_Workspaces_Domain_Model_DatabaseRecord $versionRecord) {
+               $this->setTable($table);
+               $this->setLiveRecord($liveRecord);
+               $this->setVersionRecord($versionRecord);
+       }
+
+       /**
+        * Gets the name of the database table.
+        *
+        * @return string
+        */
+       public function getTable() {
+               return $this->table;
+       }
+
+       /**
+        * Sets the name of the database table.
+        *
+        * @param string $table
+        * @return void
+        */
+       public function setTable($table) {
+               $this->table = $table;
+       }
+
+       /**
+        * Gets the live-record object.
+        *
+        * @return Tx_Workspaces_Domain_Model_DatabaseRecord
+        */
+       public function getLiveRecord() {
+               return $this->liveRecord;
+       }
+
+       /**
+        * Sets the live-record object.
+        *
+        * @param Tx_Workspaces_Domain_Model_DatabaseRecord $liveRecord
+        * @return void
+        */
+       public function setLiveRecord(Tx_Workspaces_Domain_Model_DatabaseRecord $liveRecord) {
+               $this->liveRecord = $liveRecord;
+       }
+
+       /**
+        * Gets the version-record object.
+        *
+        * @return Tx_Workspaces_Domain_Model_DatabaseRecord
+        */
+       public function getVersionRecord() {
+               return $this->versionRecord;
+       }
+
+       /**
+        * Sets the version-record object.
+        *
+        * @param Tx_Workspaces_Domain_Model_DatabaseRecord $versionRecord
+        * @return void
+        */
+       public function setVersionRecord(Tx_Workspaces_Domain_Model_DatabaseRecord $versionRecord) {
+               $this->versionRecord = $versionRecord;
+       }
+
+       /**
+        * Gets the id of the live-record.
+        *
+        * @return integer
+        */
+       public function getLiveId() {
+               return $this->getLiveRecord()->getUid();
+       }
+
+       /**
+        * Gets the id of version-record.
+        *
+        * @return integer
+        */
+       public function getVersiondId() {
+               return $this->getVersionRecord()->getUid();
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php b/typo3/sysext/workspaces/Classes/Domain/Model/DatabaseRecord.php
new file mode 100644 (file)
index 0000000..d627695
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2012 Oliver Hader <oliver.hader@typo3.org>
+ * All rights reserved
+ *
+ * This script is part of the TYPO3 project. The TYPO3 project is
+ * free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The GNU General Public License can be found at
+ * http://www.gnu.org/copyleft/gpl.html.
+ * A copy is found in the textfile GPL.txt and important notices to the license
+ * from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ * This script is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ * @package Workspaces
+ * @subpackage Domain
+ */
+class Tx_Workspaces_Domain_Model_DatabaseRecord {
+       /**
+        * @var string
+        */
+       protected $table;
+
+       /**
+        * @var integer
+        */
+       protected $uid;
+
+       /**
+        * @var array
+        */
+       protected $row;
+
+       /**
+        * Creates database record object just by id of database record.
+        *
+        * @param string $table Name of the database table
+        * @param integer $uid Id of the datbase record row
+        * @return Tx_Workspaces_Domain_Model_DatabaseRecord
+        */
+       public static function create($table, $uid) {
+               return t3lib_div::makeInstance(
+                       'Tx_Workspaces_Domain_Model_DatabaseRecord',
+                       $table, $uid
+               );
+       }
+
+       /**
+        * Creates datbase record object by relevant database record row.
+        *
+        * @param string $table Name of the database table
+        * @param array $row The relevant database record row
+        * @return Tx_Workspaces_Domain_Model_DatabaseRecord
+        */
+       public static function createFromArray($table, array $row) {
+               return t3lib_div::makeInstance(
+                       'Tx_Workspaces_Domain_Model_DatabaseRecord',
+                       $table, $row['uid'], $row
+               );
+       }
+
+       /**
+        * @param string $table Name of the database table
+        * @param integer $uid Id of the datbase record row
+        * @param array|NULL $row The relevant database record row
+        */
+       public function __construct($table, $uid, array $row = NULL) {
+               $this->setTable($table);
+               $this->setUid($uid);
+
+               if ($row !== NULL) {
+                       $this->setRow($row);
+               }
+       }
+
+       /**
+        * Gets the name of the database table.
+        *
+        * @return string
+        */
+       public function getTable() {
+               return $this->table;
+       }
+
+       /**
+        * Sets the name of the database table.
+        *
+        * @param string $table
+        * @return void
+        */
+       public function setTable($table) {
+               $this->table = $table;
+       }
+
+       /**
+        * Gets the id of the database record row.
+        *
+        * @return integer
+        */
+       public function getUid() {
+               return $this->uid;
+       }
+
+       /**
+        * Sets the id of the database record row.
+        *
+        * @param integer $uid
+        * @return void
+        */
+       public function setUid($uid) {
+               $this->uid = $uid;
+       }
+
+       /**
+        * Gets the database record row.
+        *
+        * @return array
+        */
+       public function getRow() {
+               $this->loadRow();
+               return $this->row;
+       }
+
+       /**
+        * Sets the database record row.
+        *
+        * @param array $row
+        * @return void
+        */
+       public function setRow(array $row) {
+               $this->row = $row;
+       }
+
+       /**
+        * Gets the record identifier (table:id).
+        *
+        * @return string
+        */
+       public function getIdentifier() {
+               return implode(
+                       ':',
+                       array($this->getTable(), $this->getUid())
+               );
+       }
+
+       /**
+        * Loads the database record row (if not available yet).
+        *
+        * @return void
+        */
+       protected function loadRow() {
+               if ($this->row === NULL) {
+                       $this->row = t3lib_BEfunc::getRecord(
+                               $this->getTable(),
+                               $this->getUid()
+                       );
+               }
+       }
+}
+?>
\ No newline at end of file
index cdb0e2f..ed65647 100644 (file)
@@ -72,5 +72,81 @@ abstract class Tx_Workspaces_ExtDirect_AbstractHandler {
        protected function getWorkspaceService() {
                return t3lib_div::makeInstance('Tx_Workspaces_Service_Workspaces');
        }
+
+       /**
+        * Validates whether the submitted language parameter can be
+        * interpreted as integer value.
+        *
+        * @param stdClass $parameters
+        * @return integer|NULL
+        */
+       protected function validateLanguageParameter(stdClass $parameters) {
+               $language = NULL;
+
+               if (isset($parameters->language) && t3lib_utility_Math::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 = array();
+
+               if ($parameters->type === 'selection') {
+                       foreach ((array) $parameters->selection as $element) {
+                               $affectedElements[] = Tx_Workspaces_Domain_Model_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 = Tx_Workspaces_Domain_Model_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 Tx_Workspaces_Domain_Model_CombinedRecord[] $affectedElements
+        * @return Tx_Workspaces_Service_Integrity
+        * @see getAffectedElements
+        */
+       protected function createIntegrityService(array $affectedElements) {
+               /** @var $integrityService Tx_Workspaces_Service_Integrity */
+               $integrityService = t3lib_div::makeInstance('Tx_Workspaces_Service_Integrity');
+               $integrityService->setAffectedElements($affectedElements);
+               return $integrityService;
+       }
 }
 ?>
\ No newline at end of file
index ef68258..c81c39c 100644 (file)
@@ -131,9 +131,24 @@ class Tx_Workspaces_ExtDirect_ActionHandler extends Tx_Workspaces_ExtDirect_Abst
        }
 
        /**
+        * Saves the selected language.
+        *
+        * @param integer|string $language
+        * @return void
+        */
+       public function saveLanguageSelection($language) {
+               if (t3lib_utility_Math::canBeInterpretedAsInteger($language) === FALSE && $language !== 'all') {
+                       $language = 'all';
+               }
+
+               $GLOBALS['BE_USER']->uc['moduleData']['Workspaces'][$GLOBALS['BE_USER']->workspace]['language'] = $language;
+               $GLOBALS['BE_USER']->writeUC();
+       }
+
+       /**
         * Gets the dialog window to be displayed before a record can be sent to the next stage.
         *
-        *      @param integer $uid
+        * @param integer $uid
         * @param string $table
         * @param integer $t3ver_oid
         * @return array
index 61bee36..274d9f3 100644 (file)
@@ -94,7 +94,8 @@ class Tx_Workspaces_ExtDirect_MassActionHandler extends Tx_Workspaces_ExtDirect_
 
                try {
                        if ($parameters->init) {
-                               $cnt = $this->initPublishData($this->getCurrentWorkspace(), $parameters->swap);
+                               $language = $this->validateLanguageParameter($parameters);
+                               $cnt = $this->initPublishData($this->getCurrentWorkspace(), $parameters->swap, $language);
                                $result['total'] = $cnt;
                        } else {
                                $result['processed'] = $this->processData($this->getCurrentWorkspace());
@@ -122,7 +123,8 @@ class Tx_Workspaces_ExtDirect_MassActionHandler extends Tx_Workspaces_ExtDirect_
 
                try {
                        if ($parameters->init) {
-                               $cnt = $this->initFlushData($this->getCurrentWorkspace());
+                               $language = $this->validateLanguageParameter($parameters);
+                               $cnt = $this->initFlushData($this->getCurrentWorkspace(), $language);
                                $result['total'] = $cnt;
                        } else {
                                $result['processed'] = $this->processData($this->getCurrentWorkspace());
@@ -139,12 +141,12 @@ class Tx_Workspaces_ExtDirect_MassActionHandler extends Tx_Workspaces_ExtDirect_
         *
         * @param integer $workspace
         * @param boolean $swap
+        * @param integer $language
         * @return integer
         */
-       protected function initPublishData($workspace, $swap) {
-               $workspaceService = t3lib_div::makeInstance('Tx_Workspaces_Service_Workspaces');
+       protected function initPublishData($workspace, $swap, $language = NULL) {
                        // workspace might be -98 a.k.a "All Workspaces but that's save here
-               $publishData = $workspaceService->getCmdArrayForPublishWS($workspace, $swap);
+               $publishData = $this->getWorkspaceService()->getCmdArrayForPublishWS($workspace, $swap, 0, $language);
                $recordCount = 0;
                foreach ($publishData as $table => $recs) {
                        $recordCount += count($recs);
@@ -161,12 +163,12 @@ class Tx_Workspaces_ExtDirect_MassActionHandler extends Tx_Workspaces_ExtDirect_
         * Initializes the command map to be used for flushing.
         *
         * @param integer $workspace
+        * @param integer $language
         * @return integer
         */
-       protected function initFlushData($workspace) {
-               $workspaceService = t3lib_div::makeInstance('Tx_Workspaces_Service_Workspaces');
+       protected function initFlushData($workspace, $language = NULL) {
                        // workspace might be -98 a.k.a "All Workspaces but that's save here
-               $flushData = $workspaceService->getCmdArrayForFlushWS($workspace);
+               $flushData = $this->getWorkspaceService()->getCmdArrayForFlushWS($workspace, TRUE, 0, $language);
                $recordCount = 0;
                foreach ($flushData as $table => $recs) {
                        $recordCount += count($recs);
@@ -209,9 +211,10 @@ class Tx_Workspaces_ExtDirect_MassActionHandler extends Tx_Workspaces_ExtDirect_
                        $GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction', NULL);
                        $GLOBALS['BE_USER']->setAndSaveSessionData('workspaceMassAction_total', 0);
                } else {
-                               // Execute the commands:
+                       /** @var $tce t3lib_TCEmain */
                        $tce = t3lib_div::makeInstance('t3lib_TCEmain');
                        $tce->stripslashes_values = 0;
+                               // Execute the commands:
                        $tce->start(array(), $limitedCmd);
                        $tce->process_cmdmap();
 
index 886f1ed..c085ee0 100644 (file)
  */
 class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHandler {
        /**
+        * @var Tx_Workspaces_Service_GridData
+        */
+       protected $gridDataService;
+
+       /**
+        * @var Tx_Workspaces_Service_Stages
+        */
+       protected $stagesService;
+
+       /**
+        * Checks integrity of elements before peforming actions on them.
+        *
+        * @param stdClass $parameters
+        * @return array
+        */
+       public function checkIntegrity(stdClass $parameters) {
+               $integrity = $this->createIntegrityService(
+                       $this->getAffectedElements($parameters)
+               );
+
+               $integrity->check();
+
+               $response = array(
+                       'result' => $integrity->getStatusRepresentation(),
+               );
+
+               return $response;
+       }
+
+       /**
         * Get List of workspace changes
         *
         * @param object $parameter
@@ -43,11 +73,21 @@ class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHan
                        // To avoid too much work we use -1 to indicate that every page is relevant
                $pageId = $parameter->id > 0 ? $parameter->id : -1;
 
-               $wslibObj = t3lib_div::makeInstance('Tx_Workspaces_Service_Workspaces');
-               $versions = $wslibObj->selectVersionsInWorkspace($this->getCurrentWorkspace(), 0, -99, $pageId, $parameter->depth);
+               if (!isset($parameter->language) || !t3lib_utility_Math::canBeInterpretedAsInteger($parameter->language)) {
+                       $parameter->language = NULL;
+               }
+
+               $versions = $this->getWorkspaceService()->selectVersionsInWorkspace(
+                       $this->getCurrentWorkspace(),
+                       0,
+                       -99,
+                       $pageId,
+                       $parameter->depth,
+                       'tables_select',
+                       $parameter->language
+               );
 
-               $workspacesService = t3lib_div::makeInstance('Tx_Workspaces_Service_GridData');
-               $data = $workspacesService->generateGridListFromVersions($versions, $parameter, $this->getCurrentWorkspace());
+               $data = $this->getGridDataService()->generateGridListFromVersions($versions, $parameter, $this->getCurrentWorkspace());
                return $data;
        }
 
@@ -61,8 +101,7 @@ class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHan
                $currentWorkspace = $this->getCurrentWorkspace();
                $stages = array();
                if ($currentWorkspace != Tx_Workspaces_Service_Workspaces::SELECT_ALL_WORKSPACES) {
-                       $stagesService = t3lib_div::makeInstance('Tx_Workspaces_Service_Stages');
-                       $stages = $stagesService->getStagesForWSUser();
+                       $stages = $this->getStagesService()->getStagesForWSUser();
                }
 
                $data = array(
@@ -84,15 +123,15 @@ class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHan
 
                /** @var $t3lib_diff t3lib_diff */
                $t3lib_diff = t3lib_div::makeInstance('t3lib_diff');
-               /** @var $stagesService Tx_Workspaces_Service_Stages */
-               $stagesService = t3lib_div::makeInstance('Tx_Workspaces_Service_Stages');
+
+               /** @var $parseObj t3lib_parsehtml_proc */
                $parseObj = t3lib_div::makeInstance('t3lib_parsehtml_proc');
 
                $liveRecord = t3lib_BEfunc::getRecord($parameter->table, $parameter->t3ver_oid);
                $versionRecord = t3lib_BEfunc::getRecord($parameter->table, $parameter->uid);
                $icon_Live = t3lib_iconWorks::mapRecordTypeToSpriteIconClass($parameter->table, $liveRecord);
                $icon_Workspace = t3lib_iconWorks::mapRecordTypeToSpriteIconClass($parameter->table, $versionRecord);
-               $stagePosition = $stagesService->getPositionOfCurrentStage($parameter->stage);
+               $stagePosition = $this->getStagesService()->getPositionOfCurrentStage($parameter->stage);
 
                $fieldsOfRecords = array_keys($liveRecord);
 
@@ -182,11 +221,10 @@ class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHan
         * Gets an array with all sys_log entries and their comments for the given record uid and table
         *
         * @param integer $uid uid of changed element to search for in log
-        * @param string $table table name
+        * @param string $table Name of the record's table
         * @return array
         */
        public function getCommentsForRecord($uid, $table) {
-               $stagesService = t3lib_div::makeInstance('Tx_Workspaces_Service_Stages');
                $sysLogReturnArray = array();
 
                $sysLogRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
@@ -204,7 +242,7 @@ class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHan
                        $data = unserialize($sysLogRow['log_data']);
                        $beUserRecord = t3lib_BEfunc::getRecord('be_users', $sysLogRow['userid']);
 
-                       $sysLogEntry['stage_title'] = $stagesService->getStageTitle($data['stage']);
+                       $sysLogEntry['stage_title'] = $this->getStagesService()->getStageTitle($data['stage']);
                        $sysLogEntry['user_uid'] = $sysLogRow['userid'];
                        $sysLogEntry['user_username'] = is_array($beUserRecord) ? $beUserRecord['username'] : '';
                        $sysLogEntry['tstamp'] = t3lib_BEfunc::datetime($sysLogRow['tstamp']);
@@ -215,5 +253,63 @@ class Tx_Workspaces_ExtDirect_Server extends Tx_Workspaces_ExtDirect_AbstractHan
 
                return $sysLogReturnArray;
        }
+
+       /**
+        * Gets all available system languages.
+        *
+        * @return array
+        */
+       public function getSystemLanguages() {
+               $systemLanguages = array(
+                       array(
+                               'uid' => 'all',
+                               'title' => Tx_Extbase_Utility_Localization::translate('language.allLanguages', 'workspaces'),
+                               'cls' => t3lib_iconWorks::getSpriteIconClasses('empty-empty'),
+                       )
+               );
+
+               foreach ($this->getGridDataService()->getSystemLanguages() as $id => $systemLanguage) {
+                       if ($id < 0) {
+                               continue;
+                       }
+
+                       $systemLanguages[] = array(
+                               'uid' => $id,
+                               'title' => htmlspecialchars($systemLanguage['title']),
+                               'cls' => t3lib_iconWorks::getSpriteIconClasses($systemLanguage['flagIcon']),
+                       );
+               }
+
+               $result = array(
+                       'total' => count($systemLanguages),
+                       'data' => $systemLanguages,
+               );
+
+               return $result;
+       }
+
+       /**
+        * Gets the Grid Data Service.
+        *
+        * @return Tx_Workspaces_Service_GridData
+        */
+       protected function getGridDataService() {
+               if (!isset($this->gridDataService)) {
+                       $this->gridDataService = t3lib_div::makeInstance('Tx_Workspaces_Service_GridData');
+               }
+               return $this->gridDataService;
+       }
+
+       /**
+        * Gets the Stages Service.
+        *
+        * @return Tx_Workspaces_Service_Stages
+        */
+       protected function getStagesService() {
+               if (!isset($this->stagesService)) {
+                       $this->stagesService = t3lib_div::makeInstance('Tx_Workspaces_Service_Stages');
+               }
+               return $this->stagesService;
+       }
 }
 ?>
\ No newline at end of file
index 89e6e66..40769fd 100644 (file)
  * @package Workspaces
  * @subpackage Service
  */
-class Tx_Workspaces_Service_Befunc {
+class Tx_Workspaces_Service_Befunc implements t3lib_Singleton {
+       /**
+        * Gets a singleton instance of this object.
+        *
+        * @return Tx_Workspaces_Service_Befunc
+        */
+       public static function getInstance() {
+               return t3lib_div::makeInstance('Tx_Workspaces_Service_Befunc');
+       }
 
        /**
         * Hooks into the t3lib_beFunc::viewOnClick and redirects to the workspace preview
index a63b48b..e88b8eb 100644 (file)
@@ -72,6 +72,16 @@ class Tx_Workspaces_Service_GridData {
        protected $workspacesCache = NULL;
 
        /**
+        * @var array
+        */
+       protected $systemLanguages;
+
+       /**
+        * @var Tx_Workspaces_Service_Integrity
+        */
+       protected $integrityService;
+
+       /**
         * Generates grid list array from given versions.
         *
         * @param array $versions All records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid and t3ver_oid fields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid"
@@ -130,10 +140,15 @@ class Tx_Workspaces_Service_GridData {
                                $isRecordTypeAllowedToModify = $GLOBALS['BE_USER']->check('tables_modify', $table);
 
                                foreach ($records as $record) {
-
                                        $origRecord = t3lib_BEFunc::getRecord($table, $record['t3ver_oid']);
                                        $versionRecord = t3lib_BEFunc::getRecord($table, $record['uid']);
 
+                                       $combinedRecord = Tx_Workspaces_Domain_Model_CombinedRecord::createFromArrays(
+                                               $table, $origRecord, $versionRecord
+                                       );
+
+                                       $this->getIntegrityService()->checkElement($combinedRecord);
+
                                        if (isset($GLOBALS['TCA'][$table]['columns']['hidden'])) {
                                                $recordState = $this->workspaceState($versionRecord['t3ver_state'], $origRecord['hidden'], $versionRecord['hidden']);
                                        } else {
@@ -165,6 +180,13 @@ class Tx_Workspaces_Service_GridData {
                                        $versionArray['icon_Live'] = t3lib_iconWorks::mapRecordTypeToSpriteIconClass($table, $origRecord);
                                        $versionArray['icon_Workspace'] = t3lib_iconWorks::mapRecordTypeToSpriteIconClass($table, $versionRecord);
 
+                                       $languageValue = $this->getLanguageValue($table, $versionRecord);
+                                       $versionArray['languageValue'] = $languageValue;
+                                       $versionArray['language'] = array(
+                                               'cls' => t3lib_iconWorks::getSpriteIconClasses($this->getSystemLanguageValue($languageValue, 'flagIcon')),
+                                               'title' => htmlspecialchars($this->getSystemLanguageValue($languageValue, 'title')),
+                                       );
+
                                        $versionArray['allowedAction_nextStage'] = $isRecordTypeAllowedToModify && $stagesObj->isNextStageAllowedForUser($versionRecord['t3ver_stage']);
                                        $versionArray['allowedAction_prevStage'] = $isRecordTypeAllowedToModify && $stagesObj->isPrevStageAllowedForUser($versionRecord['t3ver_stage']);
 
@@ -197,6 +219,16 @@ class Tx_Workspaces_Service_GridData {
                                $this->dataArray, $versions
                        );
 
+                               // Enrich elements after everything has been processed:
+                       foreach ($this->dataArray as &$element) {
+                               $identifier = $element['table'] . ':' . $element['t3ver_oid'];
+
+                               $element['integrity'] = array(
+                                       'status' => $this->getIntegrityService()->getStatusRepresentation($identifier),
+                                       'messages' => htmlspecialchars($this->getIntegrityService()->getIssueMessages($identifier, TRUE)),
+                               );
+                       }
+
                        $this->setDataArrayIntoCache($versions, $filterTxt);
                }
 
@@ -317,6 +349,7 @@ class Tx_Workspaces_Service_GridData {
                                case 't3ver_oid';
                                case 'liveid';
                                case 'livepid':
+                               case 'languageValue';
                                        usort($this->dataArray, array($this, 'intSort'));
                                        break;
                                case 'label_Workspace';
@@ -465,6 +498,76 @@ class Tx_Workspaces_Service_GridData {
        }
 
        /**
+        * Gets the used language value (sys_language.uid) of
+        * a given database record.
+        *
+        * @param string $table Name of the table
+        * @param array $record Database record
+        * @return integer
+        */
+       protected function getLanguageValue($table, array $record) {
+               $languageValue = 0;
+
+               if (t3lib_BEfunc::isTableLocalizable($table)) {
+                       $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
+
+                       if (!empty($record[$languageField])) {
+                               $languageValue = $record[$languageField];
+                       }
+               }
+
+               return $languageValue;
+       }
+
+       /**
+        * Gets a named value of the available sys_language elements.
+        *
+        * @param integer $id sys_language uid
+        * @param string $key Name of the value to be fetched (e.g. title)
+        * @return string|NULL
+        * @see getSystemLanguages
+        */
+       protected function getSystemLanguageValue($id, $key) {
+               $value = NULL;
+
+               $systemLanguages = $this->getSystemLanguages();
+               if (!empty($systemLanguages[$id][$key])) {
+                       $value = $systemLanguages[$id][$key];
+               }
+
+               return $value;
+       }
+
+       /**
+        * Gets all available system languages.
+        *
+        * @return array
+        * @see t3lib_transl8tools::getSystemLanguages
+        */
+       public function getSystemLanguages() {
+               if (!isset($this->systemLanguages)) {
+                       /** @var $translateTools t3lib_transl8tools */
+                       $translateTools = t3lib_div::makeInstance('t3lib_transl8tools');
+                       $this->systemLanguages = $translateTools->getSystemLanguages();
+               }
+
+               return $this->systemLanguages;
+       }
+
+       /**
+        * Gets an instance of the integrity service.
+        *
+        * @return Tx_Workspaces_Service_Integrity
+        */
+       protected function getIntegrityService() {
+               if (!isset($this->integrityService)) {
+                       $this->integrityService = t3lib_div::makeInstance('Tx_Workspaces_Service_Integrity');
+               }
+
+               return $this->integrityService;
+       }
+
+       /**
         * Emits a signal to be handled by any registered slots.
         *
         * @param string $signalName Name of the signal
diff --git a/typo3/sysext/workspaces/Classes/Service/Integrity.php b/typo3/sysext/workspaces/Classes/Service/Integrity.php
new file mode 100644 (file)
index 0000000..9256858
--- /dev/null
@@ -0,0 +1,281 @@
+<?php
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2012 Oliver Hader <oliver.hader@typo3.org>
+ * All rights reserved
+ *
+ * This script is part of the TYPO3 project. The TYPO3 project is
+ * free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The GNU General Public License can be found at
+ * http://www.gnu.org/copyleft/gpl.html.
+ * A copy is found in the textfile GPL.txt and important notices to the license
+ * from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ * This script is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * @author Oliver Hader <oliver.hader@typo3.org>
+ * @package Workspaces
+ * @subpackage Service
+ */
+class Tx_Workspaces_Service_Integrity {
+       /**
+        * Succes status - everything is fine
+        *
+        * @var integer
+        */
+       const STATUS_Succes = 100;
+
+       /**
+        * Info status - nothing is wrong, but a notice is shown
+        *
+        * @var integer
+        */
+       const STATUS_Info = 101;
+
+       /**
+        * Warning status - user interaction might be required
+        *
+        * @var integer
+        */
+       const STATUS_Warning = 102;
+
+       /**
+        * Error status - user interaction is required
+        *
+        * @var integer
+        */
+       const STATUS_Error = 103;
+
+       /**
+        * @var array
+        */
+       protected $statusRepresentation = array(
+               self::STATUS_Succes => 'success',
+               self::STATUS_Info => 'info',
+               self::STATUS_Warning => 'warning',
+               self::STATUS_Error => 'error',
+       );
+
+       /**
+        * @var Tx_Workspaces_Domain_Model_CombinedRecord[]
+        */
+       protected $affectedElements;
+
+       /**
+        * Array storing all issues that have been checked and
+        * found during runtime in this object. The array keys
+        * are identifiers of table and the version-id.
+        *
+        * 'tx_table:123' => array(
+        *     array(
+        *         'status' => 'warning',
+        *         'message' => 'Element cannot be...',
+        *     )
+        * )
+        *
+        * @var array
+        */
+       protected $issues = array();
+
+       /**
+        * Sets the affected elements.
+        *
+        * @param Tx_Workspaces_Domain_Model_CombinedRecord[] $affectedElements
+        * @return void
+        */
+       public function setAffectedElements(array $affectedElements) {
+               $this->affectedElements = $affectedElements;
+       }
+
+       /**
+        * Checks integrity of affected records.
+        *
+        * @return void
+        */
+       public function check() {
+               foreach ($this->affectedElements as $affectedElement) {
+                       $this->checkElement($affectedElement);
+               }
+       }
+
+       /**
+        * Checks a single element.
+        *
+        * @param Tx_Workspaces_Domain_Model_CombinedRecord $element
+        * @return void
+        */
+       public function checkElement(Tx_Workspaces_Domain_Model_CombinedRecord $element) {
+               $this->checkLocalization($element);
+       }
+
+       /**
+        * Checks workspace localization integrity of a single elements.
+        * If current record is a localization and its localization parent
+        * is new in this workspace (has only a placeholder record in live),
+        * then boths (localization and localization parent) should be published.
+        *
+        * @param Tx_Workspaces_Domain_Model_CombinedRecord $element
+        * @return void
+        */
+       protected function checkLocalization(Tx_Workspaces_Domain_Model_CombinedRecord $element) {
+               $table = $element->getTable();
+
+               if (t3lib_BEfunc::isTableLocalizable($table)) {
+                       $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
+                       $languageParentField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
+
+                       $versionRow = $element->getVersionRecord()->getRow();
+
+                       // If element is a localization:
+                       if ($versionRow[$languageField] > 0) {
+                               // Get localization parent from live workspace:
+                               $languageParentRecord = t3lib_BEfunc::getRecord(
+                                       $table, $versionRow[$languageParentField], 'uid,t3ver_state'
+                               );
+
+                               // If localization parent is a "new placeholder" record:
+                               if ($languageParentRecord['t3ver_state'] == 1) {
+                                       $title = t3lib_BEfunc::getRecordTitle($table, $versionRow);
+
+                                       // Add warning for current versionized record:
+                                       $this->addIssue(
+                                               $element->getLiveRecord()->getIdentifier(),
+                                               self::STATUS_Warning,
+                                               sprintf(
+                                                       Tx_Extbase_Utility_Localization::translate('integrity.dependsOnDefaultLanguageRecord', 'workspaces'),
+                                                       $title
+                                               )
+                                       );
+
+                                       // Add info for related localization parent record:
+                                       $this->addIssue(
+                                               $table . ':' . $languageParentRecord['uid'],
+                                               self::STATUS_Info,
+                                               sprintf(
+                                                       Tx_Extbase_Utility_Localization::translate('integrity.isDefaultLanguageRecord', 'workspaces'),
+                                                       $title
+                                               )
+                                       );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Gets the status of the most important severity.
+        * (low << success, info, warning, error >> high)
+        *
+        * @param string $identifier Record identifier (table:id) for look-ups
+        * @return string
+        */
+       public function getStatus($identifier = NULL) {
+               $status = self::STATUS_Succes;
+
+               if ($identifier === NULL) {
+                       foreach ($this->issues as $idenfieriferIssues) {
+                               foreach ($idenfieriferIssues as $issue) {
+                                       if ($status < $issue['status']) {
+                                               $status = $issue['status'];
+                                       }
+                               }
+                       }
+               } else {
+                       foreach ($this->getIssues($identifier) as $issue) {
+                               if ($status < $issue['status']) {
+                                       $status = $issue['status'];
+                               }
+                       }
+               }
+
+               return $status;
+       }
+
+       /**
+        * Gets the (human readable) represetation of the status with the most
+        * important severity (wraps $this->getStatus() and translates the result).
+        *
+        * @param string $identifier Record identifier (table:id) for look-ups
+        * @return string One out of success, info, warning, error
+        */
+       public function getStatusRepresentation($identifier = NULL) {
+               return $this->statusRepresentation[$this->getStatus($identifier)];
+       }
+
+       /**
+        * Gets issues, all or specific for one identifier.
+        *
+        * @param string $identifier Record identifier (table:id) for look-ups
+        * @return array
+        */
+       public function getIssues($identifier = NULL) {
+               if ($identifier === NULL) {
+                       return $this->issues;
+               } elseif (isset($this->issues[$identifier])) {
+                       return $this->issues[$identifier];
+               }
+
+               return array();
+       }
+
+       /**
+        * Gets the message of all issues.
+        *
+        * @param string $identifier Record identifier (table:id) for look-ups
+        * @param boolean $asString Return results as string instead of array
+        * @return array|string
+        */
+       public function getIssueMessages($identifier = NULL, $asString = FALSE) {
+               $messages = array();
+
+               if ($identifier === NULL) {
+                       foreach ($this->issues as $idenfieriferIssues) {
+                               foreach ($idenfieriferIssues as $issue) {
+                                       $messages[] = $issue['message'];
+                               }
+                       }
+               } else {
+                       foreach ($this->getIssues($identifier) as $issue) {
+                               $messages[] = $issue['message'];
+                       }
+               }
+
+               if ($asString) {
+                       $messages = implode('<br/>' , $messages);
+               }
+
+               return $messages;
+       }
+
+       /**
+        * Adds an issue.
+        *
+        * @param string $identifier Record identifier (table:id)
+        * @param integer $status Status code (see constants)
+        * @param string $message Message/description of the issue
+        * @return void
+        */
+       protected function addIssue($identifier, $status, $message) {
+               if (!isset($this->issues[$identifier])) {
+                       $this->issues[$identifier] = array();
+               }
+
+               $this->issues[$identifier][] = array(
+                       'status' => $status,
+                       'message' => $message,
+               );
+       }
+}
+?>
\ No newline at end of file
index 972ac59..5faaf28 100644 (file)
@@ -117,9 +117,10 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
         * @param       integer         Real workspace ID, cannot be ONLINE (zero).
         * @param       boolean         If set, then the currently online versions are swapped into the workspace in exchange for the offline versions. Otherwise the workspace is emptied.
         * @param       integer         $pageId: ...
+        * @param       integer         $language Select specific language only
         * @return      array           Command array for tcemain
         */
-       public function getCmdArrayForPublishWS($wsid, $doSwap, $pageId = 0) {
+       public function getCmdArrayForPublishWS($wsid, $doSwap, $pageId = 0, $language = NULL) {
 
                $wsid = intval($wsid);
                $cmd = array();
@@ -136,7 +137,7 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
                        }
 
                                // Select all versions to swap:
-                       $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, ($pageId ? $pageId : -1), 0, 'tables_modify');
+                       $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, ($pageId ? $pageId : -1), 0, 'tables_modify', $language);
 
                                // Traverse the selection to build CMD array:
                        foreach ($versions as $table => $records) {
@@ -156,9 +157,10 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
         * @param       integer         Real workspace ID, cannot be ONLINE (zero).
         * @param       boolean         Run Flush (TRUE) or ClearWSID (FALSE) command
         * @param       integer         $pageId: ...
+        * @param       integer         $language Select specific language only
         * @return      array           Command array for tcemain
         */
-       public function getCmdArrayForFlushWS($wsid, $flush = TRUE, $pageId = 0) {
+       public function getCmdArrayForFlushWS($wsid, $flush = TRUE, $pageId = 0, $language = NULL) {
 
                $wsid = intval($wsid);
                $cmd = array();
@@ -168,7 +170,7 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
                        $stage = -99;
 
                                // Select all versions to swap:
-                       $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, ($pageId ? $pageId : -1), 0, 'tables_modify');
+                       $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, ($pageId ? $pageId : -1), 0, 'tables_modify', $language);
 
                                // Traverse the selection to build CMD array:
                        foreach ($versions as $table => $records) {
@@ -193,9 +195,10 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
         * @param       integer         Page id: Live page for which to find versions in workspace!
         * @param       integer         Recursion Level - select versions recursive - parameter is only relevant if $pageId != -1
         * @param       string          How to collect records for "listing" or "modify" these tables. Support the permissions of each type of record (@see t3lib_userAuthGroup::check).
+        * @parem       integer         $language Select specific language only
         * @return      array           Array of all records uids etc. First key is table name, second key incremental integer. Records are associative arrays with uid and t3ver_oidfields. The pid of the online record is found as "livepid" the pid of the offline record is found in "wspid"
         */
-       public function selectVersionsInWorkspace($wsid, $filter = 0, $stage = -99, $pageId = -1, $recursionLevel = 0, $selectionType = 'tables_select') {
+       public function selectVersionsInWorkspace($wsid, $filter = 0, $stage = -99, $pageId = -1, $recursionLevel = 0, $selectionType = 'tables_select', $language = NULL) {
 
                $wsid = intval($wsid);
                $filter = intval($filter);
@@ -231,7 +234,7 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
 
                        if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
 
-                               $recs = $this->selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage);
+                               $recs = $this->selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language);
                                if (intval($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) === 2) {
                                        $moveRecs = $this->getMoveToPlaceHolderFromPages($table, $pageList, $wsid, $filter, $stage);
                                        $recs = array_merge($recs, $moveRecs);
@@ -253,24 +256,42 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
         * @param integer $wsid
         * @param integer $filter
         * @param integer $stage
+        * @param integer $language
         * @return array
         */
-       protected function selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage) {
+       protected function selectAllVersionsFromPages($table, $pageList, $wsid, $filter, $stage, $language = NULL) {
+               $isTableLocalizable = t3lib_BEfunc::isTableLocalizable($table);
+               $languageParentField = '';
+
+                       // If table is not localizable, but localized reocrds shall
+                       // be collected, an empty result array needs to be returned:
+               if ($isTableLocalizable === FALSE && $language > 0) {
+                       return array();
+               } elseif ($isTableLocalizable) {
+                       $languageParentField = 'A.' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ', ';
+               }
 
-               $fields = 'A.uid, A.t3ver_oid, A.t3ver_stage, B.pid AS wspid, B.pid AS livepid';
-               if (t3lib_BEfunc::isTableLocalizable($table)) {
+               $fields = 'A.uid, A.t3ver_oid, A.t3ver_stage, ' . $languageParentField . 'B.pid AS wspid, B.pid AS livepid';
+
+               if ($isTableLocalizable) {
                        $fields .= ', A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
                }
+
                $from = $table . ' A,' . $table . ' B';
 
                        // Table A is the offline version and pid=-1 defines offline
                $where = 'A.pid=-1 AND A.t3ver_state!=4';
+
                if ($pageList) {
                        $pidField = ($table==='pages' ? 'uid' : 'pid');
                        $pidConstraint = strstr($pageList, ',') ? ' IN (' . $pageList . ')' : '=' . $pageList;
                        $where .= ' AND B.' . $pidField . $pidConstraint;
                }
 
+               if ($isTableLocalizable && t3lib_utility_Math::canBeInterpretedAsInteger($language)) {
+                       $where .= ' AND A.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . $language;
+               }
+
                /**
                 * For "real" workspace numbers, select by that.
                 * If = -98, select all that are NOT online (zero).
@@ -382,8 +403,10 @@ class Tx_Workspaces_Service_Workspaces implements t3lib_Singleton {
                 * mount points are not covered yet
                 **/
                $perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
+
+               /** @var $searchObj t3lib_fullsearch */
                $searchObj = t3lib_div::makeInstance('t3lib_fullsearch');
-               $pageList = FALSE;
+
                if ($pageId > 0) {
                        $pageList = $searchObj->getTreeList($pageId, $recursionLevel, 0, $perms_clause);
                } else {
index c3a20db..95b5f6e 100644 (file)
                        <trans-unit id="comments" xml:space="preserve">
                                <source><![CDATA[User comments for <b>step {0} of stage</b> "{1}"]]></source>
                        </trans-unit>
+                       <trans-unit id="language.allLanguages" xml:space="preserve">
+                               <source>all languages</source>
+                       </trans-unit>
+                       <trans-unit id="language.selectLanguage" xml:space="preserve">
+                               <source>all languages</source>
+                       </trans-unit>
+                       <trans-unit id="integrity.dependsOnDefaultLanguageRecord" xml:space="preserve">
+                               <source><![CDATA[Record "%s" depends on default language record]]></source>
+                       </trans-unit>
+                       <trans-unit id="integrity.isDefaultLanguageRecord" xml:space="preserve">
+                               <source><![CDATA[Default language record for record "%s"]]></source>
+                       </trans-unit>
+                       <trans-unit id="integrity.hasIssuesDescription" xml:space="preserve">
+                               <source>The integrity check discovered several issues, see status icons infront of each record for further details.</source>
+                       </trans-unit>
+                       <trans-unit id="integrity.hasIssuesQuestion" xml:space="preserve">
+                               <source>Do you want to continue?</source>
+                       </trans-unit>
+                       <trans-unit id="status.info" xml:space="preserve">
+                               <source>Information</source>
+                       </trans-unit>
+                       <trans-unit id="status.warning" xml:space="preserve">
+                               <source>Warning</source>
+                       </trans-unit>
+                       <trans-unit id="status.error" xml:space="preserve">
+                               <source>Error</source>
+                       </trans-unit>
                </body>
        </file>
 </xliff>
index d425a5b..fa421ec 100644 (file)
@@ -7,6 +7,9 @@ TYPO3.Workspaces.Configuration.StoreFieldArray = [
        {name : 'livepid', type : 'int'},
        {name : 'stage', type: 'int'},
        {name : 'change',type : 'int'},
+       {name : 'languageValue'},
+       {name : 'language'},
+       {name : 'integrity'},
        {name : 'label_Live'},
        {name : 'label_Workspace'},
        {name : 'label_Stage'},
@@ -48,6 +51,7 @@ TYPO3.Workspaces.MainStore = new Ext.data.GroupingStore({
        baseParams: {
                depth : 990,
                id: TYPO3.settings.Workspaces.id,
+               language: TYPO3.settings.Workspaces.language,
                query: '',
                start: 0,
                limit: 30
index 8242892..08e37f0 100644 (file)
@@ -31,8 +31,40 @@ TYPO3.Workspaces.Actions = {
 
        runningMassAction: null,
        currentSendToMode: 'next',
-       triggerMassAction: function(action) {
 
+       checkIntegrity: function(parameters, callbackFunction, callbackArguments) {
+               TYPO3.Workspaces.ExtDirect.checkIntegrity(
+                               parameters,
+                               function (response) {
+                                       switch (response.result) {
+                                               case 'error':
+                                                       top.TYPO3.Dialog.ErrorDialog({
+                                                               minWidth: 400,
+                                                               title: 'Error',
+                                                               msg: '<div class="scope">' + TYPO3.l10n.localize('integrity.hasIssuesDescription') + '</div>'
+                                                       });
+                                                       break;
+                                               case 'warning':
+                                                       top.TYPO3.Dialog.QuestionDialog({
+                                                               minWidth: 400,
+                                                               title: 'Warning',
+                                                               msg: '<div class="scope">' + TYPO3.l10n.localize('integrity.hasIssuesDescription') + '</div>' +
+                                                                       '<div class="question">' + TYPO3.l10n.localize('integrity.hasIssuesQuestion') + '</div>',
+                                                               fn: function(result) {
+                                                                       if (result == 'yes') {
+                                                                               callbackFunction.call(this, callbackArguments)
+                                                                       }
+                                                               }
+                                                       });
+                                                       break;
+                                               default:
+                                                       callbackFunction.call(this, callbackArguments);
+                                       }
+                               }
+               )
+       },
+
+       triggerMassAction: function(action, language) {
                switch (action) {
                        case 'publish':
                        case 'swap':
@@ -47,6 +79,7 @@ TYPO3.Workspaces.Actions = {
                        init: true,
                        total:0,
                        processed:0,
+                       language: language,
                        swap: (action == 'swap')
                });
        },
index d757b85..bff48ed 100644 (file)
@@ -48,7 +48,9 @@ TYPO3.Workspaces.RowDetail.rowDataStore = new Ext.data.DirectStore({
                {name : 'live_record'},
                {name : 'comments'},
                {name : 'icon_Live'},
-               {name : 'icon_Workspace'}
+               {name : 'icon_Workspace'},
+               {name : 'languageValue'},
+               {name : 'integrity'}
        ]
 });
 
index dda77bc..961d497 100644 (file)
@@ -40,6 +40,10 @@ TYPO3.Workspaces.Configuration.GridFilters = new Ext.ux.grid.GridFilters({
                        dataIndex : 'workspace_Title'
                },
                {
+                       type : 'numeric',
+                       dataIndex : 'languageValue'
+               },
+               {
                        type : 'string',
                        dataIndex : 'label_Live'
                },
@@ -54,6 +58,21 @@ TYPO3.Workspaces.Configuration.GridFilters = new Ext.ux.grid.GridFilters({
        ]
 });
 
+TYPO3.Workspaces.Configuration.Integrity = new Ext.grid.Column({
+       width: 24,
+       hideable: true,
+       sortable: false,
+       header: '<span class="' + TYPO3.settings.Workspaces.icons.integrity + '">&nbsp;</span>',
+       renderer: function(value, meta, record) {
+               if (record.json.integrity.status !== 'success') {
+                       var cls = TYPO3.settings.Workspaces.icons[record.json.integrity.status] + ' t3-visible';
+                       var title = TYPO3.l10n.localize('status.' + record.json.integrity.status);
+                       var message = record.json.integrity.messages;
+
+                       return '<span class="' + cls + '" ext:qtitle="' + title + '" ext:qtip="' + message + '">&nbsp;</span>';
+               }
+       }
+});
 TYPO3.Workspaces.Configuration.WsPath = {
        id: 'path_Workspace',
        dataIndex : 'path_Workspace',
@@ -104,6 +123,19 @@ TYPO3.Workspaces.Configuration.WsTitleWithIcon = {
        filter : {type: 'string'}
 };
 
+TYPO3.Workspaces.Configuration.Language = {
+       id: 'language',
+       dataIndex: 'languageValue',
+       width: 30,
+       hideable: true,
+       sortable: true,
+       header: '<span class="' + TYPO3.settings.Workspaces.icons.language + '">&nbsp;</span>',
+       filter: { type: 'string '},
+       renderer: function(value, metaData, record) {
+               return '<span class="' + record.json.language.cls + '" title="' + record.json.language.title + '">&nbsp;</span>';
+       }
+};
+
 TYPO3.Workspaces.Configuration.TitleWithIcon = {
        id: 'label_Live',
        dataIndex : 'label_Live',
@@ -317,6 +349,15 @@ TYPO3.Workspaces.Configuration.SwapButton = {
                        ,tooltip: TYPO3.l10n.localize('tooltip.swap')
                        ,handler: function(grid, rowIndex, colIndex) {
                                var record = TYPO3.Workspaces.MainStore.getAt(rowIndex);
+                               var parameters = {
+                                       type: 'selection',
+                                       selection: [{
+                                               table: record.json.table,
+                                               versionId: record.json.uid,
+                                               liveId: record.json.t3ver_oid
+                                       }]
+                               };
+
                                var configuration = {
                                        title: TYPO3.l10n.localize('window.swap.title'),
                                        msg: TYPO3.l10n.localize('window.swap.message'),
@@ -327,7 +368,9 @@ TYPO3.Workspaces.Configuration.SwapButton = {
                                        }
                                };
 
-                               top.TYPO3.Dialog.QuestionDialog(configuration);
+                               TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() {
+                                       top.TYPO3.Dialog.QuestionDialog(configuration);
+                               });
                        },
                        getClass: function(v, meta, rec) {
                                if(!rec.json.allowedAction_swap) {
index 6699b02..4933880 100644 (file)
@@ -96,10 +96,12 @@ TYPO3.Workspaces.WorkspaceGrid = new Ext.grid.GridPanel({
                        this.colModel = new Ext.grid.ColumnModel({
                                columns: [
                                        TYPO3.Workspaces.RowExpander,
+                                       TYPO3.Workspaces.Configuration.Integrity,
                                        {id: 'uid', dataIndex : 'uid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.uid'), hidden: true, filterable : true },
                                        {id: 't3ver_oid', dataIndex : 't3ver_oid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.oid'), hidden: true, filterable : true },
                                        {id: 'workspace_Title', dataIndex : 'workspace_Title', width: 120, sortable: true, header : TYPO3.l10n.localize('column.workspaceName'), hidden: true, filter : {type : 'string'}},
                                        TYPO3.Workspaces.Configuration.WsPath,
+                                       TYPO3.Workspaces.Configuration.Language,
                                        TYPO3.Workspaces.Configuration.LivePath,
                                        TYPO3.Workspaces.Configuration.WsTitleWithIcon,
                                        TYPO3.Workspaces.Configuration.TitleWithIcon,
@@ -120,10 +122,12 @@ TYPO3.Workspaces.WorkspaceGrid = new Ext.grid.GridPanel({
                                columns: [
                                        TYPO3.Workspaces.SelectionModel,
                                        TYPO3.Workspaces.RowExpander,
+                                       TYPO3.Workspaces.Configuration.Integrity,
                                        {id: 'uid', dataIndex : 'uid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.uid'), hidden: true, filterable : true },
                                        {id: 't3ver_oid', dataIndex : 't3ver_oid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.oid'), hidden: true, filterable : true },
                                        {id: 'workspace_Title', dataIndex : 'workspace_Title', width: 120, sortable: true, header : TYPO3.l10n.localize('column.workspaceName'), hidden: true, filter : {type : 'string'}},
                                        TYPO3.Workspaces.Configuration.WsPath,
+                                       TYPO3.Workspaces.Configuration.Language,
                                        TYPO3.Workspaces.Configuration.LivePath,
                                        TYPO3.Workspaces.Configuration.WsTitleWithIcon,
                                        TYPO3.Workspaces.Configuration.SwapButton,
index e161db7..5128a28 100644 (file)
@@ -90,5 +90,20 @@ TYPO3.Workspaces.Helpers = {
                });
 
                return elements;
+       },
+
+       getElementsArrayOfSelectionForIntegrityCheck: function(selection) {
+               var elements = [];
+
+               Ext.each(selection, function(item) {
+                       var element = {
+                               table: item.data.table,
+                               liveId: item.data.t3ver_oid,
+                               versionId: item.data.uid
+                       }
+                       elements.push(element);
+               });
+
+               return elements;
        }
 };
\ No newline at end of file
index b2fc017..f1ff51f 100644 (file)
@@ -74,7 +74,21 @@ TYPO3.Workspaces.Toolbar.selectStateActionCombo = new Ext.form.ComboBox({
        listeners: {
                'select' : function () {
                        var selection = TYPO3.Workspaces.WorkspaceGrid.getSelectionModel().getSelections();
-                       TYPO3.Workspaces.Actions.sendToSpecificStageWindow(selection, this.getValue());
+                       var nextStage = this.getValue();
+
+                       // Use integrity check since "publish execute" stage is effective
+                       if (nextStage == -20) {
+                               var parameters = {
+                                       type: 'selection',
+                                       selection: TYPO3.Workspaces.Helpers.getElementsArrayOfSelectionForIntegrityCheck(selection)
+                               };
+
+                               TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() {
+                                       TYPO3.Workspaces.Actions.sendToSpecificStageWindow(selection, nextStage);
+                               });
+                       } else {
+                               TYPO3.Workspaces.Actions.sendToSpecificStageWindow(selection, nextStage);
+                       }
                }
        }
 });
@@ -116,19 +130,25 @@ TYPO3.Workspaces.Toolbar.selectStateMassActionCombo = new Ext.form.ComboBox({
                'select' : function (combo, record) {
                        var label = '';
                        var affectWholeWorkspaceWarning = TYPO3.l10n.localize('tooltip.affectWholeWorkspace');
+                       var language = TYPO3.Workspaces.MainStore.baseParams.language;
+                       var checkIntegrity = false;
+
                        switch (record.data.action) {
                                case 'publish':
                                        label = TYPO3.l10n.localize('tooltip.publishAll');
+                                       checkIntegrity = true;
                                        break;
                                case 'swap':
                                        label = TYPO3.l10n.localize('tooltip.swapAll');
+                                       checkIntegrity = true;
                                        break;
                                case 'discard':
                                        label = TYPO3.l10n.localize('tooltip.discardAll');
                                        break;
                        }
                        top.TYPO3.Windows.close('executeMassActionWindow');
-                       var dialog = top.TYPO3.Windows.showWindow({
+
+                       var configuration = {
                                id: 'executeMassActionWindow',
                                title: TYPO3.l10n.localize('window.massAction.title'),
                                items: [
@@ -156,7 +176,10 @@ TYPO3.Workspaces.Toolbar.selectStateMassActionCombo = new Ext.form.ComboBox({
                                                text: TYPO3.l10n.localize('ok'),
                                                disabled:false,
                                                handler: function(event) {
-                                                       TYPO3.Workspaces.Actions.triggerMassAction(event.data.action);
+                                                       TYPO3.Workspaces.Actions.triggerMassAction(
+                                                               event.data.action,
+                                                               language
+                                                       );
                                                }
                                        },
                                        {
@@ -170,7 +193,19 @@ TYPO3.Workspaces.Toolbar.selectStateMassActionCombo = new Ext.form.ComboBox({
                                                }
                                        }
                                ]
-                       });
+                       };
+
+                       if (checkIntegrity && language != 'all') {
+                               var parameters = {
+                                       type: 'all',
+                                       language: language
+                               };
+                               TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() {
+                                       top.TYPO3.Windows.showWindow(configuration);
+                               });
+                       } else {
+                               top.TYPO3.Windows.showWindow(configuration);
+                       }
                }
        }
 });
@@ -226,8 +261,48 @@ TYPO3.Workspaces.Toolbar.depthFilter = new Ext.form.ComboBox({
        }
 });
 
+TYPO3.Workspaces.Toolbar.LanguageSelector = new Ext.form.ComboBox({
+       width: 150,
+       listWidth: 350,
+       lazyRender: true,
+       valueField: 'uid',
+       displayField: 'title',
+       mode: 'local',
+       emptyText: TYPO3.l10n.localize('language.selectLanguage'),
+       selectOnFocus: true,
+       triggerAction: 'all',
+       editable: false,
+       forceSelection: true,
+       tpl: '<tpl for="."><div class="x-combo-list-item"><span class="{cls}">&nbsp;</span> {title}</div></tpl>',
+       store: new Ext.data.DirectStore({
+               storeId: 'languages',
+               root: 'data',
+               totalProperty: 'total',
+               idProperty: 'uid',
+               fields: [
+                       {name : 'uid'},
+                       {name : 'title'},
+                       {name : 'cls'}
+               ],
+               listeners: {
+                       load: function() {
+                               TYPO3.Workspaces.Toolbar.LanguageSelector.setValue(TYPO3.settings.Workspaces.language);
+                       }
+               }
+       }),
+       listeners: {
+               select: function (comboBox, record, index) {
+                       TYPO3.Workspaces.ExtDirectActions.saveLanguageSelection(this.getValue());
+                       TYPO3.Workspaces.MainStore.setBaseParam('language', this.getValue());
+                       TYPO3.Workspaces.MainStore.load();
+               }
+       }
+});
+
 TYPO3.Workspaces.Toolbar.FullTopToolbar = [
        TYPO3.Workspaces.Toolbar.depthFilter,
+       '-',
+       TYPO3.Workspaces.Toolbar.LanguageSelector,
        {xtype: 'tbfill'},
        TYPO3.Workspaces.Toolbar.search
 ];
index 5c04e71..fe8a17e 100644 (file)
@@ -35,6 +35,7 @@ TYPO3.Workspaces.App = {
                TYPO3.Workspaces.MainStore.load();
                TYPO3.Workspaces.Toolbar.selectActionStore.load();
                TYPO3.Workspaces.Toolbar.selectMassActionStore.load();
+               TYPO3.Workspaces.Toolbar.LanguageSelector.getStore().load();
        }
 };
 
@@ -62,6 +63,10 @@ Ext.onReady(function() {
        TYPO3.Workspaces.Toolbar.selectActionStore.proxy = new Ext.data.DirectProxy({
                directFn : TYPO3.Workspaces.ExtDirect.getStageActions
        });
+       // late binding of ExtDirect
+       TYPO3.Workspaces.Toolbar.LanguageSelector.getStore().proxy = new Ext.data.DirectProxy({
+               directFn : TYPO3.Workspaces.ExtDirect.getSystemLanguages
+       });
 
        TYPO3.Workspaces.RowDetail.rowDataStore.proxy = new Ext.data.DirectProxy({
                directFn: TYPO3.Workspaces.ExtDirect.getRowDetails
index 3077d31..1797ae9 100644 (file)
@@ -73,7 +73,8 @@ div.x-grid3-row img.x-action-col-icon {
        display:none;
 }
 
-div.x-grid3-row-over img.x-action-col-icon {
+div.x-grid3-row-over img.x-action-col-icon,
+div.x-grid3-row img.x-action-col-icon.t3-visible {
        display:inline-block;
 }
 
index 1d61b8e..898856f 100644 (file)
@@ -7,6 +7,8 @@ return array(
        'tx_workspaces_controller_abstractcontroller' => $extensionPath . 'Classes/Controller/AbstractController.php',
        'tx_workspaces_controller_previewcontroller' => $extensionPath . 'Classes/Controller/PreviewController.php',
        'tx_workspaces_controller_reviewcontroller' => $extensionPath . 'Classes/Controller/ReviewController.php',
+       'tx_workspaces_domain_model_combinedrecord' => $extensionPath . 'Classes/Domain/Model/CombinedRecord.php',
+       'tx_workspaces_domain_model_databaserecord' => $extensionPath . 'Classes/Domain/Model/DatabaseRecord.php',
        'tx_workspaces_extdirect_abstracthandler' => $extensionPath . 'Classes/ExtDirect/AbstractHandler.php',
        'tx_workspaces_extdirect_actionhandler' => $extensionPath . 'Classes/ExtDirect/ActionHandler.php',
        'tx_workspaces_extdirect_massactionhandler' => $extensionPath . 'Classes/ExtDirect/MassActionHandler.php',
@@ -18,6 +20,7 @@ return array(
        'tx_workspaces_service_cleanuppreviewlinktask' => $extensionPath . 'Classes/Service/CleanupPreviewLinkTask.php',
        'tx_workspaces_service_befunc' => $extensionPath . 'Classes/Service/Befunc.php',
        'tx_workspaces_service_griddata' => $extensionPath . 'Classes/Service/GridData.php',
+       'tx_workspaces_service_integrity' => $extensionPath . 'Classes/Service/Integrity.php',
        'tx_workspaces_service_stages' => $extensionPath . 'Classes/Service/Stages.php',
        'tx_workspaces_service_tcemain' => $extensionPath . 'Classes/Service/Tcemain.php',
        'tx_workspaces_service_workspaces' => $extensionPath . 'Classes/Service/Workspaces.php',