[TASK] Merge submodule version into core
authorThomas Maroschik <tmaroschik@dfau.de>
Mon, 27 May 2013 17:05:45 +0000 (19:05 +0200)
committerThomas Maroschik <tmaroschik@dfau.de>
Mon, 27 May 2013 17:05:45 +0000 (19:05 +0200)
38 files changed:
.gitmodules
typo3/sysext/version [deleted submodule]
typo3/sysext/version/ChangeLog [new file with mode: 0644]
typo3/sysext/version/Classes/ClickMenu/VersionClickMenu.php [new file with mode: 0644]
typo3/sysext/version/Classes/Controller/VersionModuleController.php [new file with mode: 0644]
typo3/sysext/version/Classes/DataHandler/CommandMap.php [new file with mode: 0644]
typo3/sysext/version/Classes/Dependency/DependencyEntityFactory.php [new file with mode: 0644]
typo3/sysext/version/Classes/Dependency/DependencyResolver.php [new file with mode: 0644]
typo3/sysext/version/Classes/Dependency/ElementEntity.php [new file with mode: 0644]
typo3/sysext/version/Classes/Dependency/EventCallback.php [new file with mode: 0644]
typo3/sysext/version/Classes/Dependency/ReferenceEntity.php [new file with mode: 0644]
typo3/sysext/version/Classes/Hook/DataHandlerHook.php [new file with mode: 0644]
typo3/sysext/version/Classes/Hook/IconUtilityHook.php [new file with mode: 0644]
typo3/sysext/version/Classes/Hook/PreviewHook.php [new file with mode: 0644]
typo3/sysext/version/Classes/Preview.php [new file with mode: 0644]
typo3/sysext/version/Classes/Task/AutoPublishTask.php [new file with mode: 0644]
typo3/sysext/version/Classes/Utility/WorkspacesUtility.php [new file with mode: 0644]
typo3/sysext/version/Classes/View/VersionView.php [new file with mode: 0644]
typo3/sysext/version/Migrations/Code/ClassAliasMap.php [new file with mode: 0644]
typo3/sysext/version/Resources/Private/Language/locallang_emails.xlf [new file with mode: 0644]
typo3/sysext/version/class.tx_version_cm1.php [new file with mode: 0755]
typo3/sysext/version/class.tx_version_gui.php [new file with mode: 0644]
typo3/sysext/version/class.tx_version_iconworks.php [new file with mode: 0644]
typo3/sysext/version/class.tx_version_tcemain.php [new file with mode: 0644]
typo3/sysext/version/class.tx_version_tcemain_commandmap.php [new file with mode: 0644]
typo3/sysext/version/cm1/clear.gif [new file with mode: 0755]
typo3/sysext/version/cm1/cm_icon.gif [new file with mode: 0755]
typo3/sysext/version/cm1/conf.php [new file with mode: 0755]
typo3/sysext/version/cm1/index.php [new file with mode: 0755]
typo3/sysext/version/ext_autoload.php [new file with mode: 0644]
typo3/sysext/version/ext_emconf.php [new file with mode: 0644]
typo3/sysext/version/ext_icon.gif [new file with mode: 0755]
typo3/sysext/version/ext_localconf.php [new file with mode: 0644]
typo3/sysext/version/ext_tables.php [new file with mode: 0644]
typo3/sysext/version/ext_tables.sql [new file with mode: 0644]
typo3/sysext/version/locallang.xlf [new file with mode: 0644]
typo3/sysext/version/tasks/class.tx_version_tasks_autopublish.php [new file with mode: 0644]
typo3/sysext/version/ws/class.wslib.php [new file with mode: 0755]

index 685b147..e69de29 100644 (file)
@@ -1,3 +0,0 @@
-[submodule "typo3/sysext/version"]
-       path = typo3/sysext/version
-       url = git://git.typo3.org/TYPO3v4/CoreProjects/workspaces/version.git
diff --git a/typo3/sysext/version b/typo3/sysext/version
deleted file mode 160000 (submodule)
index 71f57e4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 71f57e4453ffdfabb57664a953a5aa5b04754c5b
diff --git a/typo3/sysext/version/ChangeLog b/typo3/sysext/version/ChangeLog
new file mode 100644 (file)
index 0000000..bfb4bb4
--- /dev/null
@@ -0,0 +1,87 @@
+2011-02-20  Tolleiv Nietsch  <typo3@tolleiv.de>
+
+       * Fixed bug #13096: class tx_version_tcemain_CommandMap contains calls to protected methods from tx_version_tcemain (Thanks to Andreas Kiessling)
+       * Fixed bug #13094: Cannot push IRRE records to next stage (Thanks to Andreas Kiessling)
+       * Fixed bug #13097: tx_version_tcemain->processCmdmap - &$commandIsProcessed is not set to TRUE (Thanks to Andreas Kiessling)
+
+2011-02-17  Oliver Hader  <oliver.hader@typo3.org>
+
+       * Fixed bug #12615: Dependencies in sysext 'version' and 'workspaces'
+
+2011-01-26  Tolleiv Nietsch <typo3@tolleiv.de>
+
+       * Reapplied #17284: Formprotection persistToken method is called too often, causing unnecessary DB-load - included in 4.5.0 (Thanks to Helmut Hummel / Ernesto Baschny)
+       * Raised version to 4.5.0
+
+2011-01-25  Tolleiv Nietsch <typo3@tolleiv.de>
+
+       * Raised version to 4.5.0rc2
+
+2011-01-24  Susanne Moog <typo3@susanne-moog.de>
+
+       * Fixed bug #12505: Fatal Error: Call to undefined method t3lib_TCEmain::rawCopyPageContent()
+
+2011-01-21  Tolleiv Nietsch <typo3@tolleiv.de>
+
+       * Raised version to 4.5.0rc1
+
+2011-01-20  Tolleiv Nietsch <typo3@tolleiv.de>
+
+       * Fixed bug #12386: "Publish only content in publish stage" doesn't work anymore
+
+2011-01-20  Susanne Moog <typo3@susanne-moog.de>
+
+       * Fixed bug #12397: CSRF protection for clickmenu and old workspace module (Thanks to Helmut Hummel)
+
+2011-01-12  Oliver Hader  <oliver.hader@typo3.org>
+
+       * Follow-up to bug #11832: Label of the anchor is empty
+       * Raised version to 4.5.0beta4
+
+2011-01-09  Tolleiv Nietsch  <typo3@tolleiv.de>
+
+       * Fixed bug #11832: Versioning - t3lib_extMgm::createListViewLink() function no longer available (Thanks to Mark Johnston)
+       * Fixed bug #16786: Generating preview link not working since Update to 4.4.5
+
+2011-01-06  Tolleiv Nietsch  <typo3@tolleiv.de>
+
+       * Fixed bug #11926: Language file Resources/Private/Language/emails.xml can not be translated (Thanks to Christopher).
+
+2011-01-01  Oliver Hader  <oliver.hader@typo3.org>
+
+       * Fixed bug #11833: Sorting order of IRRE records is wrong after publishing
+
+2010-12-28  Oliver Hader  <oliver.hader@typo3.org>
+
+       * Cleanup: Fixed PHPdoc comments, type hints and XCLASS blocks
+       * Cleanup: Renamed command map class file to start with proper extension prefix
+
+2010-12-22  Tolleiv Nietsch  <typo3@tolleiv.de>
+
+       * Fixed bug:#11606 Remove "Send to review / pub" from contextmenu
+
+2010-12-21  Sonja Scholz  <ss@cabag.ch>
+
+       * Fixed bug #11633: Versioning module should not be available when workspaces is loaded
+
+2010-12-05  Steffen Gebert  <steffen@steffen-gebert.de>
+
+       * Fixed bug #11290: PHP notices from XCLASS inclusions
+
+2010-12-01  Oliver Hader  <oliver@typo3.org>
+
+       * Cleanup: Fixed ext_emconf.php
+       * Raised version to 4.5.0beta2a
+
+2010-12-01  Tolleiv Nietsch  <typo3@tolleiv.de>
+
+       * Fixed bug: #11209: BACK_PATH is not handled right in class.tx_version_gui.php
+
+2010-12-01  Oliver Hader  <oliver@typo3.org>
+
+       * Cleanup: Fixed ext_emconf.php
+       * Raised version to 4.5.0beta2
+
+2010-11-28  Sonja Scholz  <ss@cabag.ch>
+
+       * Fixed bug #5923: Notify per email if groups are selected instead of single users
diff --git a/typo3/sysext/version/Classes/ClickMenu/VersionClickMenu.php b/typo3/sysext/version/Classes/ClickMenu/VersionClickMenu.php
new file mode 100644 (file)
index 0000000..7f19c64
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+namespace TYPO3\CMS\Version\ClickMenu;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2004-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
+ *  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.
+ *
+ *  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!
+ ***************************************************************/
+/**
+ * "Versioning" item added to click menu of elements.
+ *
+ * @author     Kasper Skårhøj <kasperYYYY@typo3.com>
+ */
+class VersionClickMenu {
+
+       /**
+        * Main function, adding the item to input menuItems array
+        *
+        * @param       object          References to parent clickmenu objects.
+        * @param       array           Array of existing menu items accumulated. New element added to this.
+        * @param       string          Table name of the element
+        * @param       integer         Record UID of the element
+        * @return      array           Modified menuItems array
+        * @todo Define visibility
+        */
+       public function main(&$backRef, $menuItems, $table, $uid) {
+               $localItems = array();
+               if (!$backRef->cmLevel && $uid > 0 && $GLOBALS['BE_USER']->check('modules', 'web_txversionM1')) {
+                       // Returns directly, because the clicked item was not from the pages table
+                       if (in_array('versioning', $backRef->disabledItems) || !$GLOBALS['TCA'][$table] || !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
+                               return $menuItems;
+                       }
+                       // Adds the regular item
+                       $LL = $this->includeLL();
+                       // "Versioning" element added:
+                       $url = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('version') . 'cm1/index.php?table=' . rawurlencode($table) . '&uid=' . $uid;
+                       $localItems[] = $backRef->linkItem($GLOBALS['LANG']->getLLL('title', $LL), $backRef->excludeIcon('<img src="' . $backRef->backPath . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('version') . 'cm1/cm_icon.gif" width="15" height="12" border="0" align="top" alt="" />'), $backRef->urlRefForCM($url), 1);
+                       // Find position of "delete" element:
+                       $c = 0;
+                       foreach ($menuItems as $k => $value) {
+                               $c++;
+                               if (!strcmp($k, 'delete')) {
+                                       break;
+                               }
+                       }
+                       // .. subtract two (delete item + divider line)
+                       $c -= 2;
+                       // ... and insert the items just before the delete element.
+                       array_splice($menuItems, $c, 0, $localItems);
+               }
+               return $menuItems;
+       }
+
+       /**
+        * Includes the [extDir]/locallang.php and returns the $LOCAL_LANG array found in that file.
+        *
+        * @return      array           Local lang array
+        * @todo Define visibility
+        */
+       public function includeLL() {
+               return $GLOBALS['LANG']->includeLLFile('EXT:version/locallang.xml', FALSE);
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Controller/VersionModuleController.php b/typo3/sysext/version/Classes/Controller/VersionModuleController.php
new file mode 100644 (file)
index 0000000..4410336
--- /dev/null
@@ -0,0 +1,518 @@
+<?php
+namespace TYPO3\CMS\Version\Controller;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2004-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
+ *  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.
+ *
+ *  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!
+ ***************************************************************/
+
+use \TYPO3\CMS\Core\Utility\GeneralUtility;
+use \TYPO3\CMS\Backend\Utility\BackendUtility;
+
+/**
+ * Versioning module, including workspace management
+ *
+ * @author     Kasper Skårhøj <kasperYYYY@typo3.com>
+ */
+class VersionModuleController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
+
+       // Default variables for backend modules
+       /**
+        * @todo Define visibility
+        */
+       public $MCONF = array();
+
+       // Module configuration
+       /**
+        * @todo Define visibility
+        */
+       public $MOD_MENU = array();
+
+       // Module menu items
+       /**
+        * @todo Define visibility
+        */
+       public $MOD_SETTINGS = array();
+
+       // Module session settings
+       /**
+        * document template object
+        *
+        * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
+        * @todo Define visibility
+        */
+       public $doc;
+
+       /**
+        * @todo Define visibility
+        */
+       public $content;
+
+       // Accumulated content
+       // Internal:
+       /**
+        * @todo Define visibility
+        */
+       public $showWorkspaceCol = 0;
+
+       /**
+        * @todo Define visibility
+        */
+       public $formatWorkspace_cache = array();
+
+       /**
+        * @todo Define visibility
+        */
+       public $formatCount_cache = array();
+
+       /**
+        * @todo Define visibility
+        */
+       public $targets = array();
+
+       // Accumulation of online targets.
+       /**
+        * @todo Define visibility
+        */
+       public $pageModule = '';
+
+       // Name of page module
+       /**
+        * @todo Define visibility
+        */
+       public $publishAccess = FALSE;
+
+       /**
+        * @todo Define visibility
+        */
+       public $be_user_Array = array();
+
+       /**
+        * @todo Define visibility
+        */
+       public $stageIndex = array();
+
+       /**
+        * @todo Define visibility
+        */
+       public $recIndex = array();
+
+       // Determines whether to show the dummy draft workspace
+       /*********************************
+        *
+        * Standard module initialization
+        *
+        *********************************/
+       /**
+        * Initialize menu configuration
+        *
+        * @return      void
+        * @todo Define visibility
+        */
+       public function menuConfig() {
+               // CLEANSE SETTINGS
+               $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name'], 'ses');
+       }
+
+       /**
+        * Main function of the module. Write the content to $this->content
+        *
+        * @return      void
+        * @todo Define visibility
+        */
+       public function main() {
+               // Template markers
+               $markers = array(
+                       'CSH' => '',
+                       'FUNC_MENU' => '',
+                       'WS_MENU' => '',
+                       'CONTENT' => ''
+               );
+               // Setting module configuration:
+               $this->MCONF = $GLOBALS['MCONF'];
+               $this->REQUEST_URI = str_replace('&sendToReview=1', '', GeneralUtility::getIndpEnv('REQUEST_URI'));
+               // Draw the header.
+               $this->doc = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Template\\DocumentTemplate');
+               $this->doc->backPath = $GLOBALS['BACK_PATH'];
+               $this->doc->setModuleTemplate('templates/version.html');
+               // Add styles
+               $this->doc->inDocStylesArray[$GLOBALS['MCONF']['name']] = '
+.version-diff-1 { background-color: green; }
+.version-diff-2 { background-color: red; }
+';
+               // Setting up the context sensitive menu:
+               $this->doc->getContextMenuCode();
+               // Getting input data:
+               $this->id = intval(GeneralUtility::_GP('id'));
+
+               // Record uid. Goes with table name to indicate specific record
+               $this->uid = intval(GeneralUtility::_GP('uid'));
+               // // Record table. Goes with uid to indicate specific record
+               $this->table = GeneralUtility::_GP('table');
+
+               $this->details = GeneralUtility::_GP('details');
+               // Page id. If set, indicates activation from Web>Versioning module
+               $this->diffOnly = GeneralUtility::_GP('diffOnly');
+               // Flag. If set, shows only the offline version and with diff-view
+               // Force this setting:
+               $this->MOD_SETTINGS['expandSubElements'] = TRUE;
+               $this->MOD_SETTINGS['diff'] = $this->details || $this->MOD_SETTINGS['diff'] ? 1 : 0;
+               // Reading the record:
+               $record = BackendUtility::getRecord($this->table, $this->uid);
+               if ($record['pid'] == -1) {
+                       $record = BackendUtility::getRecord($this->table, $record['t3ver_oid']);
+               }
+               $this->recordFound = is_array($record);
+               $pidValue = $this->table === 'pages' ? $this->uid : $record['pid'];
+               // Checking access etc.
+               if ($this->recordFound && $GLOBALS['TCA'][$this->table]['ctrl']['versioningWS'] && !$this->id) {
+                       $this->doc->form = '<form action="" method="post">';
+                       $this->uid = $record['uid'];
+                       // Might have changed if new live record was found!
+                       // Access check!
+                       // The page will show only if there is a valid page and if this page may be viewed by the user
+                       $this->pageinfo = BackendUtility::readPageAccess($pidValue, $this->perms_clause);
+                       $access = is_array($this->pageinfo) ? 1 : 0;
+                       if ($pidValue && $access || $GLOBALS['BE_USER']->user['admin'] && !$pidValue) {
+                               // JavaScript
+                               $this->doc->JScode .= '
+                                       <script language="javascript" type="text/javascript">
+                                               script_ended = 0;
+                                               function jumpToUrl(URL) {
+                                                       window.location.href = URL;
+                                               }
+
+                                               function hlSubelements(origId, verId, over, diffLayer)  {       //
+                                                       if (over) {
+                                                               document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
+                                                               document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver-hl\';
+                                                               if (diffLayer) {
+                                                                       document.getElementById(\'diff_\'+verId).style.visibility = \'visible\';
+                                                               }
+                                                       } else {
+                                                               document.getElementById(\'orig_\'+origId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
+                                                               document.getElementById(\'ver_\'+verId).attributes.getNamedItem("class").nodeValue = \'typo3-ver\';
+                                                               if (diffLayer) {
+                                                                       document.getElementById(\'diff_\'+verId).style.visibility = \'hidden\';
+                                                               }
+                                                       }
+                                               }
+                                       </script>
+                               ';
+                               // If another page module was specified, replace the default Page module with the new one
+                               $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule'));
+                               $this->pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
+                               // Setting publish access permission for workspace:
+                               $this->publishAccess = $GLOBALS['BE_USER']->workspacePublishAccess($GLOBALS['BE_USER']->workspace);
+                               $this->versioningMgm();
+                       }
+                       $this->content .= $this->doc->spacer(10);
+                       // Setting up the buttons and markers for docheader
+                       $docHeaderButtons = $this->getButtons();
+                       $markers['CSH'] = $docHeaderButtons['csh'];
+                       $markers['FUNC_MENU'] = BackendUtility::getFuncMenu($this->id, 'SET[function]', $this->MOD_SETTINGS['function'], $this->MOD_MENU['function']);
+                       $markers['CONTENT'] = $this->content;
+               } else {
+                       // If no access or id value, create empty document
+                       $this->content = $this->doc->section($GLOBALS['LANG']->getLL('clickAPage_header'), $GLOBALS['LANG']->getLL('clickAPage_content'), 0, 1);
+                       // Setting up the buttons and markers for docheader
+                       $docHeaderButtons = $this->getButtons();
+                       $markers['CONTENT'] = $this->content;
+               }
+               // Build the <body> for the module
+               $this->content = $this->doc->startPage($GLOBALS['LANG']->getLL('title'));
+               $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
+               $this->content .= $this->doc->endPage();
+               $this->content = $this->doc->insertStylesAndJS($this->content);
+       }
+
+       /**
+        * Outputs accumulated module content to browser.
+        *
+        * @return      void
+        * @todo Define visibility
+        */
+       public function printContent() {
+               echo $this->content;
+       }
+
+       /**
+        * Create the panel of buttons for submitting the form or otherwise perform operations.
+        *
+        * @return      array   all available buttons as an assoc. array
+        */
+       protected function getButtons() {
+               $buttons = array(
+                       'csh' => '',
+                       'view' => '',
+                       'record_list' => '',
+                       'shortcut' => ''
+               );
+               // CSH
+               if ($this->recordFound && $GLOBALS['TCA'][$this->table]['ctrl']['versioningWS']) {
+                       // View page
+                       $buttons['view'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($this->pageinfo['uid'], $GLOBALS['BACK_PATH'], BackendUtility::BEgetRootLine($this->pageinfo['uid']))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.showPage', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-document-view') . '</a>';
+                       // Shortcut
+                       if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
+                               $buttons['shortcut'] = $this->doc->makeShortcutIcon('id, edit_record, pointer, new_unique_uid, search_field, search_levels, showLimit', implode(',', array_keys($this->MOD_MENU)), $this->MCONF['name']);
+                       }
+                       // If access to Web>List for user, then link to that module.
+                       $buttons['record_list'] = BackendUtility::getListViewLink(array(
+                               'id' => $this->pageinfo['uid'],
+                               'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
+                       ), '', $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.showList'));
+               }
+               return $buttons;
+       }
+
+       /******************************
+        *
+        * Versioning management
+        *
+        ******************************/
+       /**
+        * Management of versions for record
+        *
+        * @return      void
+        * @todo Define visibility
+        */
+       public function versioningMgm() {
+               // Diffing:
+               $diff_1 = GeneralUtility::_POST('diff_1');
+               $diff_2 = GeneralUtility::_POST('diff_2');
+               if (GeneralUtility::_POST('do_diff')) {
+                       $content = '';
+                       $content .= '<h3>' . $GLOBALS['LANG']->getLL('diffing') . ':</h3>';
+                       if ($diff_1 && $diff_2) {
+                               $diff_1_record = BackendUtility::getRecord($this->table, $diff_1);
+                               $diff_2_record = BackendUtility::getRecord($this->table, $diff_2);
+                               if (is_array($diff_1_record) && is_array($diff_2_record)) {
+                                       $t3lib_diff_Obj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\DiffUtility');
+                                       $tRows = array();
+                                       $tRows[] = '
+                                                                       <tr class="bgColor5 tableheader">
+                                                                               <td>' . $GLOBALS['LANG']->getLL('fieldname') . '</td>
+                                                                               <td width="98%">' . $GLOBALS['LANG']->getLL('coloredDiffView') . ':</td>
+                                                                       </tr>
+                                                               ';
+                                       foreach ($diff_1_record as $fN => $fV) {
+                                               if ($GLOBALS['TCA'][$this->table]['columns'][$fN] && $GLOBALS['TCA'][$this->table]['columns'][$fN]['config']['type'] !== 'passthrough' && !GeneralUtility::inList('t3ver_label', $fN)) {
+                                                       if (strcmp($diff_1_record[$fN], $diff_2_record[$fN])) {
+                                                               $diffres = $t3lib_diff_Obj->makeDiffDisplay(BackendUtility::getProcessedValue($this->table, $fN, $diff_2_record[$fN], 0, 1), BackendUtility::getProcessedValue($this->table, $fN, $diff_1_record[$fN], 0, 1));
+                                                               $tRows[] = '
+                                                                       <tr class="bgColor4">
+                                                                               <td>' . $fN . '</td>
+                                                                               <td width="98%">' . $diffres . '</td>
+                                                                       </tr>
+                                                               ';
+                                                       }
+                                               }
+                                       }
+                                       if (count($tRows) > 1) {
+                                               $content .= '<table border="0" cellpadding="1" cellspacing="1" width="100%">' . implode('', $tRows) . '</table><br /><br />';
+                                       } else {
+                                               $content .= $GLOBALS['LANG']->getLL('recordsMatchesCompletely');
+                                       }
+                               } else {
+                                       $content .= $GLOBALS['LANG']->getLL('errorRecordsNotFound');
+                               }
+                       } else {
+                               $content .= $GLOBALS['LANG']->getLL('errorDiffSources');
+                       }
+               }
+               // Element:
+               $record = BackendUtility::getRecord($this->table, $this->uid);
+               $recordIcon = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($this->table, $record);
+               $recTitle = BackendUtility::getRecordTitle($this->table, $record, TRUE);
+               // Display versions:
+               $content .= '
+                       ' . $recordIcon . $recTitle . '
+                       <form name="theform" action="' . str_replace('&sendToReview=1', '', $this->REQUEST_URI) . '" method="post">
+                       <table border="0" cellspacing="1" cellpadding="1">';
+               $content .= '
+                               <tr class="bgColor5 tableheader">
+                                       <td>&nbsp;</td>
+                                       <td>&nbsp;</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_title') . '">' . $GLOBALS['LANG']->getLL('tblHeader_title') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_uid') . '">' . $GLOBALS['LANG']->getLL('tblHeader_uid') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_oid') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_oid') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_id') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_id') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_wsid') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_wsid') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_state') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_state') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_stage') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_stage') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_count') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_count') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_pid') . '">' . $GLOBALS['LANG']->getLL('tblHeader_pid') . '</td>
+                                       <td title="' . $GLOBALS['LANG']->getLL('tblHeaderDesc_t3ver_label') . '">' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_label') . '</td>
+                                       <td colspan="2"><input type="submit" name="do_diff" value="' . $GLOBALS['LANG']->getLL('diff') . '" /></td>
+                               </tr>';
+               $versions = BackendUtility::selectVersionsOfRecord($this->table, $this->uid, '*', $GLOBALS['BE_USER']->workspace);
+               foreach ($versions as $row) {
+                       $adminLinks = $this->adminLinks($this->table, $row);
+                       $content .= '
+                               <tr class="' . ($row['uid'] != $this->uid ? 'bgColor4' : 'bgColor2 tableheader') . '">
+                                       <td>' . ($row['uid'] != $this->uid ?
+                                               '<a href="' . $this->doc->issueCommand(('&cmd[' . $this->table . '][' . $this->uid . '][version][swapWith]=' . $row['uid'] . '&cmd[' . $this->table . '][' . $this->uid . '][version][action]=swap')) . '" title="' . $GLOBALS['LANG']->getLL('swapWithCurrent', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-version-swap-version') . '</a>' :
+                                                       \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('status-status-current', array('title' => $GLOBALS['LANG']->getLL('currentOnlineVersion', TRUE)))) . '</td>
+                                       <td nowrap="nowrap">' . $adminLinks . '</td>
+                                       <td nowrap="nowrap">' . BackendUtility::getRecordTitle($this->table, $row, TRUE) . '</td>
+                                       <td>' . $row['uid'] . '</td>
+                                       <td>' . $row['t3ver_oid'] . '</td>
+                                       <td>' . $row['t3ver_id'] . '</td>
+                                       <td>' . $row['t3ver_wsid'] . '</td>
+                                       <td>' . $row['t3ver_state'] . '</td>
+                                       <td>' . $row['t3ver_stage'] . '</td>
+                                       <td>' . $row['t3ver_count'] . '</td>
+                                       <td>' . $row['pid'] . '</td>
+                                       <td nowrap="nowrap"><a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick(('&edit[' . $this->table . '][' . $row['uid'] . ']=edit&columnsOnly=t3ver_label'), $this->doc->backPath)) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:cm.edit', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-document-open') . '</a>' . htmlspecialchars($row['t3ver_label']) . '</td>
+                                       <td class="version-diff-1"><input type="radio" name="diff_1" value="' . $row['uid'] . '"' . ($diff_1 == $row['uid'] ? ' checked="checked"' : '') . '/></td>
+                                       <td class="version-diff-2"><input type="radio" name="diff_2" value="' . $row['uid'] . '"' . ($diff_2 == $row['uid'] ? ' checked="checked"' : '') . '/></td>
+                               </tr>';
+                       // Show sub-content if the table is pages AND it is not the online branch (because that will mostly render the WHOLE tree below - not smart;)
+                       if ($this->table == 'pages' && $row['uid'] != $this->uid) {
+                               $sub = $this->pageSubContent($row['uid']);
+                               if ($sub) {
+                                       $content .= '
+                                               <tr>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td colspan="10">' . $sub . '</td>
+                                                       <td colspan="2"></td>
+                                               </tr>';
+                               }
+                       }
+               }
+               $content .= '</table></form>';
+               $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('title'), $content, 0, 1);
+               // Create new:
+               $content = '
+
+                       <form action="' . $this->doc->backPath . 'tce_db.php" method="post">
+                       ' . $GLOBALS['LANG']->getLL('tblHeader_t3ver_label') . ': <input type="text" name="cmd[' . $this->table . '][' . $this->uid . '][version][label]" /><br />
+                       <br /><input type="hidden" name="cmd[' . $this->table . '][' . $this->uid . '][version][action]" value="new" />
+                       <input type="hidden" name="prErr" value="1" />
+                       <input type="hidden" name="redirect" value="' . htmlspecialchars($this->REQUEST_URI) . '" />
+                       <input type="submit" name="_" value="' . $GLOBALS['LANG']->getLL('createNewVersion') . '" />
+                       ' . \TYPO3\CMS\Backend\Form\FormEngine::getHiddenTokenField('tceAction') . '
+                       </form>
+
+               ';
+               $this->content .= $this->doc->spacer(15);
+               $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('createNewVersion'), $content, 0, 1);
+       }
+
+       /**
+        * Recursively look for children for page version with $pid
+        *
+        * @param       integer         UID of page record for which to look up sub-elements following that version
+        * @param       integer         Counter, do not set (limits to 100 levels)
+        * @return      string          Table with content if any
+        * @todo Define visibility
+        */
+       public function pageSubContent($pid, $c = 0) {
+               $tableNames = GeneralUtility::removeArrayEntryByValue(array_keys($GLOBALS['TCA']), 'pages');
+               $tableNames[] = 'pages';
+               foreach ($tableNames as $tN) {
+                       // Basically list ALL tables - not only those being copied might be found!
+                       $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $tN, 'pid=' . intval($pid) . BackendUtility::deleteClause($tN), '', $GLOBALS['TCA'][$tN]['ctrl']['sortby'] ? $GLOBALS['TCA'][$tN]['ctrl']['sortby'] : '');
+                       if ($GLOBALS['TYPO3_DB']->sql_num_rows($mres)) {
+                               $content .= '
+                                       <tr>
+                                               <td colspan="4" class="' . ($GLOBALS['TCA'][$tN]['ctrl']['versioning_followPages'] ? 'bgColor6' : ($tN == 'pages' ? 'bgColor5' : 'bgColor-10')) . '"' . (!$GLOBALS['TCA'][$tN]['ctrl']['versioning_followPages'] && $tN !== 'pages' ? ' style="color: #666666; font-style:italic;"' : '') . '>' . $tN . '</td>
+                                       </tr>';
+                               while ($subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
+                                       $ownVer = $this->lookForOwnVersions($tN, $subrow['uid']);
+                                       $content .= '
+                                               <tr>
+                                                       <td>' . $this->adminLinks($tN, $subrow) . '</td>
+                                                       <td>' . $subrow['uid'] . '</td>
+                                                       ' . ($ownVer > 1 ? '<td style="font-weight: bold; background-color: yellow;"><a href="index.php?table=' . rawurlencode($tN) . '&uid=' . $subrow['uid'] . '">' . ($ownVer - 1) . '</a></td>' : '<td></td>') . '
+                                                       <td width="98%">' . BackendUtility::getRecordTitle($tN, $subrow, TRUE) . '</td>
+                                               </tr>';
+                                       if ($tN == 'pages' && $c < 100) {
+                                               $sub = $this->pageSubContent($subrow['uid'], $c + 1);
+                                               if ($sub) {
+                                                       $content .= '
+                                                               <tr>
+                                                                       <td></td>
+                                                                       <td></td>
+                                                                       <td></td>
+                                                                       <td width="98%">' . $sub . '</td>
+                                                               </tr>';
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return $content ? '<table border="1" cellpadding="1" cellspacing="0" width="100%">' . $content . '</table>' : '';
+       }
+
+       /**
+        * Look for number of versions of a record
+        *
+        * @param       string          Table name
+        * @param       integer         Record uid
+        * @return      integer         Number of versions for record, FALSE if none.
+        * @todo Define visibility
+        */
+       public function lookForOwnVersions($table, $uid) {
+               $versions = BackendUtility::selectVersionsOfRecord($table, $uid, 'uid');
+               if (is_array($versions)) {
+                       return count($versions);
+               }
+               return FALSE;
+       }
+
+       /**
+        * Administrative links for a table / record
+        *
+        * @param       string          Table name
+        * @param       array           Record for which administrative links are generated.
+        * @return      string          HTML link tags.
+        * @todo Define visibility
+        */
+       public function adminLinks($table, $row) {
+               // Edit link:
+               $adminLink = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick(('&edit[' . $table . '][' . $row['uid'] . ']=edit'), $this->doc->backPath)) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:cm.edit', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-document-open') . '</a>';
+               // Delete link:
+               $adminLink .= '<a href="' . htmlspecialchars($this->doc->issueCommand(('&cmd[' . $table . '][' . $row['uid'] . '][delete]=1'))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.delete', TRUE) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-edit-delete') . '</a>';
+               if ($table == 'pages') {
+                       // If another page module was specified, replace the default Page module with the new one
+                       $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule'));
+                       $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
+                       // Perform some acccess checks:
+                       $a_wl = $GLOBALS['BE_USER']->check('modules', 'web_list');
+                       $a_wp = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('cms') && $GLOBALS['BE_USER']->check('modules', $pageModule);
+                       $adminLink .= '<a href="#" onclick="top.loadEditId(' . $row['uid'] . ');top.goToModule(\'' . $pageModule . '\'); return false;">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-page-open') . '</a>';
+                       $adminLink .= '<a href="#" onclick="top.loadEditId(' . $row['uid'] . ');top.goToModule(\'web_list\'); return false;">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-system-list-open') . '</a>';
+                       // "View page" icon is added:
+                       $adminLink .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($row['uid'], $this->doc->backPath, BackendUtility::BEgetRootLine($row['uid']))) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-document-view') . '</a>';
+               } else {
+                       if ($row['pid'] == -1) {
+                               $getVars = '&ADMCMD_vPrev[' . rawurlencode(($table . ':' . $row['t3ver_oid'])) . ']=' . $row['uid'];
+                               // "View page" icon is added:
+                               $adminLink .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($row['_REAL_PID'], $this->doc->backPath, BackendUtility::BEgetRootLine($row['_REAL_PID']), '', '', $getVars)) . '">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-document-view') . '</a>';
+                       }
+               }
+               return $adminLink;
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/DataHandler/CommandMap.php b/typo3/sysext/version/Classes/DataHandler/CommandMap.php
new file mode 100644 (file)
index 0000000..9af4b5a
--- /dev/null
@@ -0,0 +1,921 @@
+<?php
+namespace TYPO3\CMS\Version\DataHandler;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010-2013 Oliver Hader <oliver@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!
+ ***************************************************************/
+
+/**
+ * Handles the \TYPO3\CMS\Core\DataHandling\DataHandler command map and is
+ * only used in combination with \TYPO3\CMS\Core\DataHandling\DataHandler
+ */
+class CommandMap {
+
+       const SCOPE_WorkspacesSwap = 'SCOPE_WorkspacesSwap';
+       const SCOPE_WorkspacesSetStage = 'SCOPE_WorkspacesSetStage';
+       const SCOPE_WorkspacesClear = 'SCOPE_WorkspacesClear';
+       const KEY_ScopeErrorMessage = 'KEY_ScopeErrorMessage';
+       const KEY_ScopeErrorCode = 'KEY_ScopeErrorCode';
+       const KEY_GetElementPropertiesCallback = 'KEY_GetElementPropertiesCallback';
+       const KEY_GetCommonPropertiesCallback = 'KEY_GetCommonPropertiesCallback';
+       const KEY_ElementConstructCallback = 'KEY_EventConstructCallback';
+       const KEY_ElementCreateChildReferenceCallback = 'KEY_ElementCreateChildReferenceCallback';
+       const KEY_ElementCreateParentReferenceCallback = 'KEY_ElementCreateParentReferenceCallback';
+       const KEY_PurgeWithErrorMessageGetIdCallback = 'KEY_PurgeWithErrorMessageGetIdCallback';
+       const KEY_UpdateGetIdCallback = 'KEY_UpdateGetIdCallback';
+       const KEY_TransformDependentElementsToUseLiveId = 'KEY_TransformDependentElementsToUseLiveId';
+       /**
+        * @var \TYPO3\CMS\Version\Hook\DataHandlerHook
+        */
+       protected $parent;
+
+       /**
+        * @var \TYPO3\CMS\Core\DataHandling\DataHandler
+        */
+       protected $tceMain;
+
+       /**
+        * @var array
+        */
+       protected $commandMap = array();
+
+       /**
+        * @var string
+        */
+       protected $workspacesSwapMode;
+
+       /**
+        * @var string
+        */
+       protected $workspacesChangeStageMode;
+
+       /**
+        * @var boolean
+        */
+       protected $workspacesConsiderReferences;
+
+       /**
+        * @var array
+        */
+       protected $scopes;
+
+       /**
+        * Creates this object.
+        *
+        * @param \TYPO3\CMS\Version\Hook\DataHandlerHook $parent
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain
+        * @param array $commandMap
+        */
+       public function __construct(\TYPO3\CMS\Version\Hook\DataHandlerHook $parent, \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain, array $commandMap) {
+               $this->setParent($parent);
+               $this->setTceMain($tceMain);
+               $this->set($commandMap);
+               $this->setWorkspacesSwapMode($this->getTceMain()->BE_USER->getTSConfigVal('options.workspaces.swapMode'));
+               $this->setWorkspacesChangeStageMode($this->getTceMain()->BE_USER->getTSConfigVal('options.workspaces.changeStageMode'));
+               $this->setWorkspacesConsiderReferences($this->getTceMain()->BE_USER->getTSConfigVal('options.workspaces.considerReferences'));
+               $this->constructScopes();
+       }
+
+       /**
+        * Gets the command map.
+        *
+        * @return array
+        */
+       public function get() {
+               return $this->commandMap;
+       }
+
+       /**
+        * Sets the command map.
+        *
+        * @param array $commandMap
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function set(array $commandMap) {
+               $this->commandMap = $commandMap;
+               return $this;
+       }
+
+       /**
+        * Gets the parent object.
+        *
+        * @return \TYPO3\CMS\Version\Hook\DataHandlerHook
+        */
+       public function getParent() {
+               return $this->parent;
+       }
+
+       /**
+        * Sets the parent object.
+        *
+        * @param \TYPO3\CMS\Version\Hook\DataHandlerHook $parent
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function setParent(\TYPO3\CMS\Version\Hook\DataHandlerHook $parent) {
+               $this->parent = $parent;
+               return $this;
+       }
+
+       /**
+        * Gets the parent object.
+        *
+        * @return \TYPO3\CMS\Core\DataHandling\DataHandler
+        */
+       public function getTceMain() {
+               return $this->tceMain;
+       }
+
+       /**
+        * Sets the parent object.
+        *
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function setTceMain(\TYPO3\CMS\Core\DataHandling\DataHandler $tceMain) {
+               $this->tceMain = $tceMain;
+               return $this;
+       }
+
+       /**
+        * Sets the workspaces swap mode
+        * (see options.workspaces.swapMode).
+        *
+        * @param string $workspacesSwapMode
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function setWorkspacesSwapMode($workspacesSwapMode) {
+               $this->workspacesSwapMode = (string) $workspacesSwapMode;
+               return $this;
+       }
+
+       /**
+        * Sets the workspaces change stage mode
+        * see options.workspaces.changeStageMode)
+        *
+        * @param string $workspacesChangeStageMode
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function setWorkspacesChangeStageMode($workspacesChangeStageMode) {
+               $this->workspacesChangeStageMode = (string) $workspacesChangeStageMode;
+               return $this;
+       }
+
+       /**
+        * Sets the workspace behaviour to automatically consider references
+        * (see options.workspaces.considerReferences)
+        *
+        * @param boolean $workspacesConsiderReferences
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function setWorkspacesConsiderReferences($workspacesConsiderReferences) {
+               $this->workspacesConsiderReferences = (bool) $workspacesConsiderReferences;
+               return $this;
+       }
+
+       /**
+        * Processes the command map.
+        *
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function process() {
+               $this->resolveWorkspacesSwapDependencies();
+               $this->resolveWorkspacesSetStageDependencies();
+               $this->resolveWorkspacesClearDependencies();
+               return $this;
+       }
+
+       /**
+        * Invokes all items for swapping/publishing with a callback method.
+        *
+        * @param string $callbackMethod
+        * @param array $arguments Optional leading arguments for the callback method
+        * @return void
+        */
+       protected function invokeWorkspacesSwapItems($callbackMethod, array $arguments = array()) {
+               // Traverses the cmd[] array and fetches the accordant actions:
+               foreach ($this->commandMap as $table => $liveIdCollection) {
+                       foreach ($liveIdCollection as $liveId => $commandCollection) {
+                               foreach ($commandCollection as $command => $properties) {
+                                       if ($command === 'version' && isset($properties['action']) && $properties['action'] === 'swap') {
+                                               if (isset($properties['swapWith']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($properties['swapWith'])) {
+                                                       call_user_func_array(array($this, $callbackMethod), array_merge($arguments, array($table, $liveId, $properties)));
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Resolves workspaces related dependencies for swapping/publishing of the command map.
+        * Workspaces records that have children or (relative) parents which are versionized
+        * but not published with this request, are removed from the command map. Otherwise
+        * this would produce hanging record sets and lost references.
+        *
+        * @return void
+        */
+       protected function resolveWorkspacesSwapDependencies() {
+               $scope = self::SCOPE_WorkspacesSwap;
+               $dependency = $this->getDependencyUtility($scope);
+               if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('any,pages', $this->workspacesSwapMode)) {
+                       $this->invokeWorkspacesSwapItems('applyWorkspacesSwapBehaviour');
+               }
+               $this->invokeWorkspacesSwapItems('addWorkspacesSwapElements', array($dependency));
+               $this->applyWorkspacesDependencies($dependency, $scope);
+       }
+
+       /**
+        * Applies workspaces behaviour for swapping/publishing and takes care of the swapMode.
+        *
+        * @param string $table
+        * @param integer $liveId
+        * @param array $properties
+        * @return void
+        */
+       protected function applyWorkspacesSwapBehaviour($table, $liveId, array $properties) {
+               $extendedCommandMap = array();
+               $elementList = array();
+               // Fetch accordant elements if the swapMode is 'any' or 'pages':
+               if ($this->workspacesSwapMode === 'any' || $this->workspacesSwapMode === 'pages' && $table === 'pages') {
+                       $elementList = $this->getParent()->findPageElementsForVersionSwap($table, $liveId, $properties['swapWith']);
+               }
+               foreach ($elementList as $elementTable => $elementIdArray) {
+                       foreach ($elementIdArray as $elementIds) {
+                               $extendedCommandMap[$elementTable][$elementIds[0]]['version'] = array_merge($properties, array('swapWith' => $elementIds[1]));
+                       }
+               }
+               if (count($elementList) > 0) {
+                       $this->remove($table, $liveId, 'version');
+                       $this->mergeToBottom($extendedCommandMap);
+               }
+       }
+
+       /**
+        * Adds workspaces elements for swapping/publishing.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
+        * @param string $table
+        * @param integer $liveId
+        * @param array $properties
+        * @return void
+        */
+       protected function addWorkspacesSwapElements(\TYPO3\CMS\Version\Dependency\DependencyResolver $dependency, $table, $liveId, array $properties) {
+               $elementList = array();
+               // Fetch accordant elements if the swapMode is 'any' or 'pages':
+               if ($this->workspacesSwapMode === 'any' || $this->workspacesSwapMode === 'pages' && $table === 'pages') {
+                       $elementList = $this->getParent()->findPageElementsForVersionSwap($table, $liveId, $properties['swapWith']);
+               }
+               foreach ($elementList as $elementTable => $elementIdArray) {
+                       foreach ($elementIdArray as $elementIds) {
+                               $dependency->addElement($elementTable, $elementIds[1], array('liveId' => $elementIds[0], 'properties' => array_merge($properties, array('swapWith' => $elementIds[1]))));
+                       }
+               }
+               if (count($elementList) === 0) {
+                       $dependency->addElement($table, $properties['swapWith'], array('liveId' => $liveId, 'properties' => $properties));
+               }
+       }
+
+       /**
+        * Invokes all items for staging with a callback method.
+        *
+        * @param string $callbackMethod
+        * @param array $arguments Optional leading arguments for the callback method
+        * @return void
+        */
+       protected function invokeWorkspacesSetStageItems($callbackMethod, array $arguments = array()) {
+               // Traverses the cmd[] array and fetches the accordant actions:
+               foreach ($this->commandMap as $table => $liveIdCollection) {
+                       foreach ($liveIdCollection as $liveIdList => $commandCollection) {
+                               foreach ($commandCollection as $command => $properties) {
+                                       if ($command === 'version' && isset($properties['action']) && $properties['action'] === 'setStage') {
+                                               if (isset($properties['stageId']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($properties['stageId'])) {
+                                                       call_user_func_array(array($this, $callbackMethod), array_merge($arguments, array($table, $liveIdList, $properties)));
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Resolves workspaces related dependencies for staging of the command map.
+        * Workspaces records that have children or (relative) parents which are versionized
+        * but not staged with this request, are removed from the command map.
+        *
+        * @return void
+        */
+       protected function resolveWorkspacesSetStageDependencies() {
+               $scope = self::SCOPE_WorkspacesSetStage;
+               $dependency = $this->getDependencyUtility($scope);
+               if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('any,pages', $this->workspacesChangeStageMode)) {
+                       $this->invokeWorkspacesSetStageItems('applyWorkspacesSetStageBehaviour');
+               }
+               $this->invokeWorkspacesSetStageItems('explodeSetStage');
+               $this->invokeWorkspacesSetStageItems('addWorkspacesSetStageElements', array($dependency));
+               $this->applyWorkspacesDependencies($dependency, $scope);
+       }
+
+       /**
+        * Applies workspaces behaviour for staging and takes care of the changeStageMode.
+        *
+        * @param string $table
+        * @param string $liveIdList
+        * @param array $properties
+        * @return void
+        */
+       protected function applyWorkspacesSetStageBehaviour($table, $liveIdList, array $properties) {
+               $extendedCommandMap = array();
+               $liveIds = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $liveIdList, TRUE);
+               $elementList = array($table => $liveIds);
+               if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('any,pages', $this->workspacesChangeStageMode)) {
+                       if (count($liveIds) === 1) {
+                               $workspaceRecord = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $liveIds[0], 't3ver_wsid');
+                               $workspaceId = $workspaceRecord['t3ver_wsid'];
+                       } else {
+                               $workspaceId = $this->getTceMain()->BE_USER->workspace;
+                       }
+                       if ($table === 'pages') {
+                               // Find all elements from the same ws to change stage
+                               $this->getParent()->findRealPageIds($liveIds);
+                               $this->getParent()->findPageElementsForVersionStageChange($liveIds, $workspaceId, $elementList);
+                       } elseif ($this->workspacesChangeStageMode === 'any') {
+                               // Find page to change stage:
+                               $pageIdList = array();
+                               $this->getParent()->findPageIdsForVersionStateChange($table, $liveIds, $workspaceId, $pageIdList, $elementList);
+                               // Find other elements from the same ws to change stage:
+                               $this->getParent()->findPageElementsForVersionStageChange($pageIdList, $workspaceId, $elementList);
+                       }
+               }
+               foreach ($elementList as $elementTable => $elementIds) {
+                       foreach ($elementIds as $elementId) {
+                               $extendedCommandMap[$elementTable][$elementId]['version'] = $properties;
+                       }
+               }
+               $this->remove($table, $liveIdList, 'version');
+               $this->mergeToBottom($extendedCommandMap);
+       }
+
+       /**
+        * Adds workspaces elements for staging.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
+        * @param string $table
+        * @param string $liveIdList
+        * @param array $properties
+        * @return void
+        */
+       protected function addWorkspacesSetStageElements(\TYPO3\CMS\Version\Dependency\DependencyResolver $dependency, $table, $liveIdList, array $properties) {
+               $dependency->addElement($table, $liveIdList, array('properties' => $properties));
+       }
+
+       /**
+        * Resolves workspaces related dependencies for clearing/flushing of the command map.
+        * Workspaces records that have children or (relative) parents which are versionized
+        * but not cleared/flushed with this request, are removed from the command map.
+        *
+        * @return void
+        */
+       protected function resolveWorkspacesClearDependencies() {
+               $scope = self::SCOPE_WorkspacesClear;
+               $dependency = $this->getDependencyUtility($scope);
+               // Traverses the cmd[] array and fetches the accordant actions:
+               foreach ($this->commandMap as $table => $liveIdCollection) {
+                       foreach ($liveIdCollection as $liveId => $commandCollection) {
+                               foreach ($commandCollection as $command => $properties) {
+                                       if ($command === 'version' && isset($properties['action']) && ($properties['action'] === 'clearWSID' || $properties['action'] === 'flush')) {
+                                               $dependency->addElement($table, $liveId, array('properties' => $properties));
+                                       }
+                               }
+                       }
+               }
+               $this->applyWorkspacesDependencies($dependency, $scope);
+       }
+
+       /**
+        * Explodes id-lists in the command map for staging actions.
+        *
+        * @throws RuntimeException
+        * @param string $table
+        * @param string $liveIdList
+        * @param array $properties
+        * @return void
+        */
+       protected function explodeSetStage($table, $liveIdList, array $properties) {
+               $extractedCommandMap = array();
+               $liveIds = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $liveIdList, TRUE);
+               if (count($liveIds) > 1) {
+                       foreach ($liveIds as $liveId) {
+                               if (isset($this->commandMap[$table][$liveId]['version'])) {
+                                       throw new \RuntimeException('Command map for [' . $table . '][' . $liveId . '][version] was already set.', 1289391048);
+                               }
+                               $extractedCommandMap[$table][$liveId]['version'] = $properties;
+                       }
+                       $this->remove($table, $liveIdList, 'version');
+                       $this->mergeToBottom($extractedCommandMap);
+               }
+       }
+
+       /**
+        * Applies the workspaces dependencies and removes incomplete structures or automatically
+        * completes them, depending on the options.workspaces.considerReferences setting
+        *
+        * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
+        * @param string $scope
+        * @return void
+        */
+       protected function applyWorkspacesDependencies(\TYPO3\CMS\Version\Dependency\DependencyResolver $dependency, $scope) {
+               $transformDependentElementsToUseLiveId = $this->getScopeData($scope, self::KEY_TransformDependentElementsToUseLiveId);
+               $elementsToBeVersionized = $dependency->getElements();
+               // Use the uid of the live record instead of the workspace record:
+               if ($transformDependentElementsToUseLiveId) {
+                       $elementsToBeVersionized = $this->transformDependentElementsToUseLiveId($elementsToBeVersionized);
+               }
+               $outerMostParents = $dependency->getOuterMostParents();
+               /** @var $outerMostParent \TYPO3\CMS\Version\Dependency\ElementEntity */
+               foreach ($outerMostParents as $outerMostParent) {
+                       $dependentElements = $dependency->getNestedElements($outerMostParent);
+                       if ($transformDependentElementsToUseLiveId) {
+                               $dependentElements = $this->transformDependentElementsToUseLiveId($dependentElements);
+                       }
+                       // Gets the difference (intersection) between elements that were submitted by the user
+                       // and the evaluation of all dependent records that should be used for this action instead:
+                       $intersectingElements = array_intersect_key($dependentElements, $elementsToBeVersionized);
+                       if (count($intersectingElements) > 0) {
+                               // If at least one element intersects but not all, throw away all elements of the depdendent structure:
+                               if (count($intersectingElements) !== count($dependentElements) && $this->workspacesConsiderReferences === FALSE) {
+                                       $this->purgeWithErrorMessage($intersectingElements, $scope);
+                               } else {
+                                       $this->update(current($intersectingElements), $dependentElements, $scope);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Purges incomplete structures from the command map and triggers an error message.
+        *
+        * @param array $elements
+        * @param string $scope
+        * @return void
+        */
+       protected function purgeWithErrorMessage(array $elements, $scope) {
+               /** @var $element \TYPO3\CMS\Version\Dependency\ElementEntity */
+               foreach ($elements as $element) {
+                       $table = $element->getTable();
+                       $id = $this->processCallback($this->getScopeData($scope, self::KEY_PurgeWithErrorMessageGetIdCallback), array($element));
+                       $this->remove($table, $id, 'version');
+                       $this->getTceMain()->log($table, $id, 5, 0, 1, $this->getScopeData($scope, self::KEY_ScopeErrorMessage), $this->getScopeData($scope, self::KEY_ScopeErrorCode), array(
+                               \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($table, \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $id)),
+                               $table,
+                               $id
+                       ));
+               }
+       }
+
+       /**
+        * Updates the command map accordant to valid structures and takes care of the correct order.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $intersectingElement
+        * @param array $elements
+        * @param string $scope
+        * @return void
+        */
+       protected function update(\TYPO3\CMS\Version\Dependency\ElementEntity $intersectingElement, array $elements, $scope) {
+               $orderedCommandMap = array();
+               $commonProperties = array();
+               if ($this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback)) {
+                       $commonProperties = $this->processCallback($this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback), array($intersectingElement));
+               }
+               /** @var $element \TYPO3\CMS\Version\Dependency\ElementEntity */
+               foreach ($elements as $element) {
+                       $table = $element->getTable();
+                       $id = $this->processCallback($this->getScopeData($scope, self::KEY_UpdateGetIdCallback), array($element));
+                       $this->remove($table, $id, 'version');
+                       $orderedCommandMap[$table][$id]['version'] = $commonProperties;
+                       if ($this->getScopeData($scope, self::KEY_GetElementPropertiesCallback)) {
+                               $orderedCommandMap[$table][$id]['version'] = array_merge($commonProperties, $this->processCallback($this->getScopeData($scope, self::KEY_GetElementPropertiesCallback), array($element)));
+                       }
+               }
+               // Ensure that ordered command map is on top of the command map:
+               $this->mergeToTop($orderedCommandMap);
+       }
+
+       /**
+        * Merges command map elements to the top of the current command map..
+        *
+        * @param array $commandMap
+        * @return void
+        */
+       protected function mergeToTop(array $commandMap) {
+               $this->commandMap = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($commandMap, $this->commandMap);
+       }
+
+       /**
+        * Merges command map elements to the bottom of the current command map.
+        *
+        * @param array $commandMap
+        * @return void
+        */
+       protected function mergeToBottom(array $commandMap) {
+               $this->commandMap = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($this->commandMap, $commandMap);
+       }
+
+       /**
+        * Removes an element from the command map.
+        *
+        * @param string $table
+        * @param string $id
+        * @param string $command (optional)
+        * @return void
+        */
+       protected function remove($table, $id, $command = NULL) {
+               if (is_string($command)) {
+                       unset($this->commandMap[$table][$id][$command]);
+               } else {
+                       unset($this->commandMap[$table][$id]);
+               }
+       }
+
+       /**
+        * Callback to get the liveId of an dependent element.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return integer
+        */
+       protected function getElementLiveIdCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               return $element->getDataValue('liveId');
+       }
+
+       /**
+        * Callback to get the real id of an dependent element.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return integer
+        */
+       protected function getElementIdCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               return $element->getId();
+       }
+
+       /**
+        * Callback to get the specific properties of a dependent element for swapping/publishing.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return array
+        */
+       protected function getElementSwapPropertiesCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               return array(
+                       'swapWith' => $element->getId()
+               );
+       }
+
+       /**
+        * Callback to get common properties of dependent elements for clearing.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return array
+        */
+       protected function getCommonClearPropertiesCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               $commonSwapProperties = array();
+               $elementProperties = $element->getDataValue('properties');
+               if (isset($elementProperties['action'])) {
+                       $commonSwapProperties['action'] = $elementProperties['action'];
+               }
+               return $commonSwapProperties;
+       }
+
+       /**
+        * Callback to get common properties of dependent elements for swapping/publishing.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return array
+        */
+       protected function getCommonSwapPropertiesCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               $commonSwapProperties = array();
+               $elementProperties = $element->getDataValue('properties');
+               if (isset($elementProperties['action'])) {
+                       $commonSwapProperties['action'] = $elementProperties['action'];
+               }
+               if (isset($elementProperties['swapIntoWS'])) {
+                       $commonSwapProperties['swapIntoWS'] = $elementProperties['swapIntoWS'];
+               }
+               if (isset($elementProperties['comment'])) {
+                       $commonSwapProperties['comment'] = $elementProperties['comment'];
+               }
+               if (isset($elementProperties['notificationAlternativeRecipients'])) {
+                       $commonSwapProperties['notificationAlternativeRecipients'] = $elementProperties['notificationAlternativeRecipients'];
+               }
+
+               return $commonSwapProperties;
+       }
+
+       /**
+        * Callback to get the specific properties of a dependent element for staging.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return array
+        */
+       protected function getElementSetStagePropertiesCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               return $this->getCommonSetStagePropertiesCallback($element);
+       }
+
+       /**
+        * Callback to get common properties of dependent elements for staging.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return array
+        */
+       protected function getCommonSetStagePropertiesCallback(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               $commonSetStageProperties = array();
+               $elementProperties = $element->getDataValue('properties');
+               if (isset($elementProperties['stageId'])) {
+                       $commonSetStageProperties['stageId'] = $elementProperties['stageId'];
+               }
+               if (isset($elementProperties['comment'])) {
+                       $commonSetStageProperties['comment'] = $elementProperties['comment'];
+               }
+               if (isset($elementProperties['action'])) {
+                       $commonSetStageProperties['action'] = $elementProperties['action'];
+               }
+               if (isset($elementProperties['notificationAlternativeRecipients'])) {
+                       $commonSetStageProperties['notificationAlternativeRecipients'] = $elementProperties['notificationAlternativeRecipients'];
+               }
+               return $commonSetStageProperties;
+       }
+
+       /**
+        * Gets an instance of the depency resolver utility.
+        *
+        * @param string $scope Scope identifier
+        * @return \TYPO3\CMS\Version\Dependency\DependencyResolver
+        */
+       protected function getDependencyUtility($scope) {
+               /** @var $dependency \TYPO3\CMS\Version\Dependency\DependencyResolver */
+               $dependency = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Dependency\\DependencyResolver');
+               $dependency->setOuterMostParentsRequireReferences(TRUE);
+               if ($this->getScopeData($scope, self::KEY_ElementConstructCallback)) {
+                       $dependency->setEventCallback(\TYPO3\CMS\Version\Dependency\ElementEntity::EVENT_Construct, $this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementConstructCallback)));
+               }
+               if ($this->getScopeData($scope, self::KEY_ElementCreateChildReferenceCallback)) {
+                       $dependency->setEventCallback(\TYPO3\CMS\Version\Dependency\ElementEntity::EVENT_CreateChildReference, $this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementCreateChildReferenceCallback)));
+               }
+               if ($this->getScopeData($scope, self::KEY_ElementCreateParentReferenceCallback)) {
+                       $dependency->setEventCallback(\TYPO3\CMS\Version\Dependency\ElementEntity::EVENT_CreateParentReference, $this->getDependencyCallback($this->getScopeData($scope, self::KEY_ElementCreateParentReferenceCallback)));
+               }
+               return $dependency;
+       }
+
+       /**
+        * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
+        *
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
+        * @param string $eventName
+        * @return string Skip response (if required)
+        */
+       public function createNewDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, \TYPO3\CMS\Version\Dependency\ElementEntity $caller, $eventName) {
+               /** @var $reference \TYPO3\CMS\Version\Dependency\ReferenceEntity */
+               $reference = $callerArguments['reference'];
+               $fieldCOnfiguration = \TYPO3\CMS\Backend\Utility\BackendUtility::getTcaFieldConfiguration($caller->getTable(), $reference->getField());
+               if (!$fieldCOnfiguration || !\TYPO3\CMS\Core\Utility\GeneralUtility::inList('field,list', $this->getTceMain()->getInlineFieldType($fieldCOnfiguration))) {
+                       return \TYPO3\CMS\Version\Dependency\ElementEntity::RESPONSE_Skip;
+               }
+       }
+
+       /**
+        * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
+        *
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
+        * @param string $eventName
+        * @return string Skip response (if required)
+        */
+       public function createNewDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, \TYPO3\CMS\Version\Dependency\ElementEntity $caller, $eventName) {
+               /** @var $reference \TYPO3\CMS\Version\Dependency\ReferenceEntity */
+               $reference = $callerArguments['reference'];
+               $fieldCOnfiguration = \TYPO3\CMS\Backend\Utility\BackendUtility::getTcaFieldConfiguration($reference->getElement()->getTable(), $reference->getField());
+               if (!$fieldCOnfiguration || !\TYPO3\CMS\Core\Utility\GeneralUtility::inList('field,list', $this->getTceMain()->getInlineFieldType($fieldCOnfiguration))) {
+                       return \TYPO3\CMS\Version\Dependency\ElementEntity::RESPONSE_Skip;
+               }
+       }
+
+       /**
+        * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
+        * Only elements that are a delete placeholder are considered.
+        *
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
+        * @param string $eventName
+        * @return string Skip response (if required)
+        */
+       public function createClearDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, \TYPO3\CMS\Version\Dependency\ElementEntity $caller, $eventName) {
+               $response = $this->createNewDependentElementChildReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
+               if (empty($response)) {
+                       /** @var $reference \TYPO3\CMS\Version\Dependency\ReferenceEntity */
+                       $reference = $callerArguments['reference'];
+                       $record = $reference->getElement()->getRecord();
+                       if ($record['t3ver_state'] != 2) {
+                               $response = \TYPO3\CMS\Version\Dependency\ElementEntity::RESPONSE_Skip;
+                       }
+               }
+               return $response;
+       }
+
+       /**
+        * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
+        * Only elements that are a delete placeholder are considered.
+        *
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
+        * @param string $eventName
+        * @return string Skip response (if required)
+        */
+       public function createClearDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, \TYPO3\CMS\Version\Dependency\ElementEntity $caller, $eventName) {
+               $response = $this->createNewDependentElementParentReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
+               if (empty($response)) {
+                       /** @var $reference \TYPO3\CMS\Version\Dependency\ReferenceEntity */
+                       $reference = $callerArguments['reference'];
+                       $record = $reference->getElement()->getRecord();
+                       if ($record['t3ver_state'] != 2) {
+                               $response = \TYPO3\CMS\Version\Dependency\ElementEntity::RESPONSE_Skip;
+                       }
+               }
+               return $response;
+       }
+
+       /**
+        * Callback to add additional data to new elements created in the dependency resolver utility.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $caller
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @return void
+        */
+       public function createNewDependentElementCallback(array $callerArguments, array $targetArgument, \TYPO3\CMS\Version\Dependency\ElementEntity $caller) {
+               if ($caller->hasDataValue('liveId') === FALSE) {
+                       $liveId = \TYPO3\CMS\Backend\Utility\BackendUtility::getLiveVersionIdOfRecord($caller->getTable(), $caller->getId());
+                       if (is_null($liveId) === FALSE) {
+                               $caller->setDataValue('liveId', $liveId);
+                       }
+               }
+       }
+
+       /**
+        * Transforms dependent elements to use the liveId as array key.
+        *
+        * @param $elements array<\TYPO3\CMS\Version\Dependency\ElementEntity>
+        * @return array
+        */
+       protected function transformDependentElementsToUseLiveId(array $elements) {
+               $transformedElements = array();
+               /** @var $element \TYPO3\CMS\Version\Dependency\ElementEntity */
+               foreach ($elements as $element) {
+                       $elementName = \TYPO3\CMS\Version\Dependency\ElementEntity::getIdentifier($element->getTable(), $element->getDataValue('liveId'));
+                       $transformedElements[$elementName] = $element;
+               }
+               return $transformedElements;
+       }
+
+       /**
+        * Constructs the scope settings.
+        * Currently the scopes for swapping/publishing and staging are available.
+        *
+        * @return void
+        */
+       protected function constructScopes() {
+               $this->scopes = array(
+                       // settings for publishing and swapping:
+                       self::SCOPE_WorkspacesSwap => array(
+                               // error message and error code
+                               self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be swapped or published independently, because it is related to other new or modified records.',
+                               self::KEY_ScopeErrorCode => 1288283630,
+                               // callback functons used to modify the commandMap
+                               // + element properties are specific for each element
+                               // + common properties are the same for all elements
+                               self::KEY_GetElementPropertiesCallback => 'getElementSwapPropertiesCallback',
+                               self::KEY_GetCommonPropertiesCallback => 'getCommonSwapPropertiesCallback',
+                               // callback function used, when a new element to be checked is added
+                               self::KEY_ElementConstructCallback => 'createNewDependentElementCallback',
+                               // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
+                               self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
+                               self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
+                               // callback function used to get the correct record uid to be used in the error message
+                               self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementLiveIdCallback',
+                               // callback function used to fetch the correct record uid on modifying the commandMap
+                               self::KEY_UpdateGetIdCallback => 'getElementLiveIdCallback',
+                               // setting whether to use the uid of the live record instead of the workspace record
+                               self::KEY_TransformDependentElementsToUseLiveId => TRUE
+                       ),
+                       // settings for modifying the stage:
+                       self::SCOPE_WorkspacesSetStage => array(
+                               // error message and error code
+                               self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be sent to another stage independently, because it is related to other new or modified records.',
+                               self::KEY_ScopeErrorCode => 1289342524,
+                               // callback functons used to modify the commandMap
+                               // + element properties are specific for each element
+                               // + common properties are the same for all elements
+                               self::KEY_GetElementPropertiesCallback => 'getElementSetStagePropertiesCallback',
+                               self::KEY_GetCommonPropertiesCallback => 'getCommonSetStagePropertiesCallback',
+                               // callback function used, when a new element to be checked is added
+                               self::KEY_ElementConstructCallback => NULL,
+                               // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
+                               self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
+                               self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
+                               // callback function used to get the correct record uid to be used in the error message
+                               self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
+                               // callback function used to fetch the correct record uid on modifying the commandMap
+                               self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
+                               // setting whether to use the uid of the live record instead of the workspace record
+                               self::KEY_TransformDependentElementsToUseLiveId => FALSE
+                       ),
+                       // settings for clearing and flushing:
+                       self::SCOPE_WorkspacesClear => array(
+                               // error message and error code
+                               self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be flushed independently, because it is related to other new or modified records.',
+                               self::KEY_ScopeErrorCode => 1300467990,
+                               // callback functons used to modify the commandMap
+                               // + element properties are specific for each element
+                               // + common properties are the same for all elements
+                               self::KEY_GetElementPropertiesCallback => NULL,
+                               self::KEY_GetCommonPropertiesCallback => 'getCommonClearPropertiesCallback',
+                               // callback function used, when a new element to be checked is added
+                               self::KEY_ElementConstructCallback => NULL,
+                               // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
+                               self::KEY_ElementCreateChildReferenceCallback => 'createClearDependentElementChildReferenceCallback',
+                               self::KEY_ElementCreateParentReferenceCallback => 'createClearDependentElementParentReferenceCallback',
+                               // callback function used to get the correct record uid to be used in the error message
+                               self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
+                               // callback function used to fetch the correct record uid on modifying the commandMap
+                               self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
+                               // setting whether to use the uid of the live record instead of the workspace record
+                               self::KEY_TransformDependentElementsToUseLiveId => FALSE
+                       )
+               );
+       }
+
+       /**
+        * Gets data for a particular scope.
+        *
+        * @throws RuntimeException
+        * @param string $scope Scope identifier
+        * @param string $key
+        * @return string
+        */
+       protected function getScopeData($scope, $key) {
+               if (!isset($this->scopes[$scope])) {
+                       throw new \RuntimeException('Scope "' . $scope . '" is not defined.', 1289342187);
+               }
+               return $this->scopes[$scope][$key];
+       }
+
+       /**
+        * Gets a new callback to be used in the dependency resolver utility.
+        *
+        * @param string $method
+        * @param array $targetArguments
+        * @return \TYPO3\CMS\Version\Dependency\EventCallback
+        */
+       protected function getDependencyCallback($method, array $targetArguments = array()) {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Dependency\\EventCallback', $this, $method, $targetArguments);
+       }
+
+       /**
+        * Processes a local callback inside this object.
+        *
+        * @param string $method
+        * @param array $callbackArguments
+        * @return mixed
+        */
+       protected function processCallback($method, array $callbackArguments) {
+               return call_user_func_array(array($this, $method), $callbackArguments);
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Dependency/DependencyEntityFactory.php b/typo3/sysext/version/Classes/Dependency/DependencyEntityFactory.php
new file mode 100644 (file)
index 0000000..5a0446e
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+namespace TYPO3\CMS\Version\Dependency;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010-2013 Oliver Hader <oliver@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!
+ ***************************************************************/
+/**
+ * Object to create and keep track of element or reference entities.
+ */
+class DependencyEntityFactory {
+
+       /**
+        * @var array
+        */
+       protected $elements = array();
+
+       /**
+        * @var array
+        */
+       protected $references = array();
+
+       /**
+        * Gets and registers a new element.
+        *
+        * @param string $table
+        * @param integer $id
+        * @param array $data (optional)
+        * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
+        * @return \TYPO3\CMS\Version\Dependency\ElementEntity
+        */
+       public function getElement($table, $id, array $data = array(), \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency) {
+               $elementName = $table . ':' . $id;
+               if (!isset($this->elements[$elementName])) {
+                       $this->elements[$elementName] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Dependency\\ElementEntity', $table, $id, $data, $dependency);
+               }
+               return $this->elements[$elementName];
+       }
+
+       /**
+        * Gets and registers a new reference.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @param string $field
+        * @return \TYPO3\CMS\Version\Dependency\ReferenceEntity
+        */
+       public function getReference(\TYPO3\CMS\Version\Dependency\ElementEntity $element, $field) {
+               $referenceName = $element->__toString() . '.' . $field;
+               if (!isset($this->references[$referenceName][$field])) {
+                       $this->references[$referenceName][$field] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Dependency\\ReferenceEntity', $element, $field);
+               }
+               return $this->references[$referenceName][$field];
+       }
+
+       /**
+        * Gets and registers a new reference.
+        *
+        * @param string $table
+        * @param integer $id
+        * @param string $field
+        * @param array $data (optional
+        * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
+        * @return \TYPO3\CMS\Version\Dependency\ReferenceEntity
+        * @see getElement
+        * @see getReference
+        */
+       public function getReferencedElement($table, $id, $field, array $data = array(), \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency) {
+               return $this->getReference($this->getElement($table, $id, $data, $dependency), $field);
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Dependency/DependencyResolver.php b/typo3/sysext/version/Classes/Dependency/DependencyResolver.php
new file mode 100644 (file)
index 0000000..1dc5d99
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+namespace TYPO3\CMS\Version\Dependency;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010-2013 Oliver Hader <oliver@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!
+ ***************************************************************/
+/**
+ * Object to handle and determine dependent references of elements.
+ */
+class DependencyResolver {
+
+       /**
+        * @var \TYPO3\CMS\Version\Dependency\DependencyEntityFactory
+        */
+       protected $factory;
+
+       /**
+        * @var array
+        */
+       protected $elements = array();
+
+       /**
+        * @var array
+        */
+       protected $eventCallbacks = array();
+
+       /**
+        * @var boolean
+        */
+       protected $outerMostParentsRequireReferences = FALSE;
+
+       /**
+        * @var array
+        */
+       protected $outerMostParents;
+
+       /**
+        * Sets a callback for a particular event.
+        *
+        * @param string $eventName
+        * @param \TYPO3\CMS\Version\Dependency\EventCallback $callback
+        * @return \TYPO3\CMS\Version\Dependency\DependencyResolver
+        */
+       public function setEventCallback($eventName, \TYPO3\CMS\Version\Dependency\EventCallback $callback) {
+               $this->eventCallbacks[$eventName] = $callback;
+               return $this;
+       }
+
+       /**
+        * Executes a registered callback (if any) for a particular event.
+        *
+        * @param string $eventName
+        * @param object $caller
+        * @param array $callerArguments
+        * @return mixed
+        */
+       public function executeEventCallback($eventName, $caller, array $callerArguments = array()) {
+               if (isset($this->eventCallbacks[$eventName])) {
+                       /** @var $callback \TYPO3\CMS\Version\Dependency\EventCallback */
+                       $callback = $this->eventCallbacks[$eventName];
+                       return $callback->execute($callerArguments, $caller, $eventName);
+               }
+       }
+
+       /**
+        * Sets the condition that outermost parents required at least one child or parent reference.
+        *
+        * @param boolean $outerMostParentsRequireReferences
+        * @return \TYPO3\CMS\Version\Dependency\DependencyResolver
+        */
+       public function setOuterMostParentsRequireReferences($outerMostParentsRequireReferences) {
+               $this->outerMostParentsRequireReferences = (bool) $outerMostParentsRequireReferences;
+               return $this;
+       }
+
+       /**
+        * Adds an element to be checked for dependent references.
+        *
+        * @param string $table
+        * @param integer $id
+        * @param array $data
+        * @return \TYPO3\CMS\Version\Dependency\ElementEntity
+        */
+       public function addElement($table, $id, array $data = array()) {
+               $element = $this->getFactory()->getElement($table, $id, $data, $this);
+               $elementName = $element->__toString();
+               $this->elements[$elementName] = $element;
+               return $element;
+       }
+
+       /**
+        * Gets the outermost parents that define complete dependent structure each.
+        *
+        * @return array
+        */
+       public function getOuterMostParents() {
+               if (!isset($this->outerMostParents)) {
+                       $this->outerMostParents = array();
+                       /** @var $element \TYPO3\CMS\Version\Dependency\ElementEntity */
+                       foreach ($this->elements as $element) {
+                               $this->processOuterMostParent($element);
+                       }
+               }
+               return $this->outerMostParents;
+       }
+
+       /**
+        * Processes and registers the outermost parents accordant to the registered elements.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @return void
+        */
+       protected function processOuterMostParent(\TYPO3\CMS\Version\Dependency\ElementEntity $element) {
+               if ($this->outerMostParentsRequireReferences === FALSE || $element->hasReferences()) {
+                       $outerMostParent = $element->getOuterMostParent();
+                       if ($outerMostParent !== FALSE) {
+                               $outerMostParentName = $outerMostParent->__toString();
+                               if (!isset($this->outerMostParents[$outerMostParentName])) {
+                                       $this->outerMostParents[$outerMostParentName] = $outerMostParent;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Gets all nested elements (including the parent) of a particular outermost parent element.
+        *
+        * @throws RuntimeException
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $outerMostParent
+        * @return array
+        */
+       public function getNestedElements(\TYPO3\CMS\Version\Dependency\ElementEntity $outerMostParent) {
+               $outerMostParentName = $outerMostParent->__toString();
+               if (!isset($this->outerMostParents[$outerMostParentName])) {
+                       throw new \RuntimeException('Element "' . $outerMostParentName . '" was detected as outermost parent.', 1289318609);
+               }
+               $nestedStructure = array_merge(array($outerMostParentName => $outerMostParent), $outerMostParent->getNestedChildren());
+               return $nestedStructure;
+       }
+
+       /**
+        * Gets the registered elements.
+        *
+        * @return array
+        */
+       public function getElements() {
+               return $this->elements;
+       }
+
+       /**
+        * Gets an instance of the factory to keep track of element or reference entities.
+        *
+        * @return \TYPO3\CMS\Version\Dependency\DependencyEntityFactory
+        */
+       public function getFactory() {
+               if (!isset($this->factory)) {
+                       $this->factory = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Dependency\\DependencyEntityFactory');
+               }
+               return $this->factory;
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Dependency/ElementEntity.php b/typo3/sysext/version/Classes/Dependency/ElementEntity.php
new file mode 100644 (file)
index 0000000..53b0dfa
--- /dev/null
@@ -0,0 +1,313 @@
+<?php
+namespace TYPO3\CMS\Version\Dependency;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010-2013 Oliver Hader <oliver@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!
+ ***************************************************************/
+/**
+ * Object to hold information on a dependent database element in abstract.
+ */
+class ElementEntity {
+
+       const REFERENCES_ChildOf = 'childOf';
+       const REFERENCES_ParentOf = 'parentOf';
+       const EVENT_Construct = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity::construct';
+       const EVENT_CreateChildReference = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity::createChildReference';
+       const EVENT_CreateParentReference = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity::createParentReference';
+       const RESPONSE_Skip = 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity->skip';
+       /**
+        * @var string
+        */
+       protected $table;
+
+       /**
+        * @var integer
+        */
+       protected $id;
+
+       /**
+        * @var array
+        */
+       protected $data;
+
+       /**
+        * @var array
+        */
+       protected $record;
+
+       /**
+        * @var \TYPO3\CMS\Version\Dependency\DependencyResolver
+        */
+       protected $dependency;
+
+       /**
+        * @var array
+        */
+       protected $children;
+
+       /**
+        * @var array
+        */
+       protected $parents;
+
+       /**
+        * @var boolean
+        */
+       protected $traversingParents = FALSE;
+
+       /**
+        * @var \TYPO3\CMS\Version\Dependency\ElementEntity
+        */
+       protected $outerMostParent;
+
+       /**
+        * @var array
+        */
+       protected $nestedChildren;
+
+       /**
+        * Creates this object.
+        *
+        * @param string $table
+        * @param integer $id
+        * @param array $data (optional)
+        * @param \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency
+        */
+       public function __construct($table, $id, array $data = array(), \TYPO3\CMS\Version\Dependency\DependencyResolver $dependency) {
+               $this->table = $table;
+               $this->id = intval($id);
+               $this->data = $data;
+               $this->dependency = $dependency;
+               $this->dependency->executeEventCallback(self::EVENT_Construct, $this);
+       }
+
+       /**
+        * Gets the table.
+        *
+        * @return string
+        */
+       public function getTable() {
+               return $this->table;
+       }
+
+       /**
+        * Gets the id.
+        *
+        * @return integer
+        */
+       public function getId() {
+               return $this->id;
+       }
+
+       /**
+        * Gets the data.
+        *
+        * @return array
+        */
+       public function getData() {
+               return $this->data;
+       }
+
+       /**
+        * Gets a value for a particular key from the data.
+        *
+        * @param string $key
+        * @return mixed
+        */
+       public function getDataValue($key) {
+               $result = NULL;
+               if ($this->hasDataValue($key)) {
+                       $result = $this->data[$key];
+               }
+               return $result;
+       }
+
+       /**
+        * Sets a value for a particular key in the data.
+        *
+        * @param string $key
+        * @param mixed $value
+        * @return void
+        */
+       public function setDataValue($key, $value) {
+               $this->data[$key] = $value;
+       }
+
+       /**
+        * Determines whether a particular key holds data.
+        *
+        * @param string $key
+        * @return
+        */
+       public function hasDataValue($key) {
+               return isset($this->data[$key]);
+       }
+
+       /**
+        * Converts this object for string representation.
+        *
+        * @return string
+        */
+       public function __toString() {
+               return self::getIdentifier($this->table, $this->id);
+       }
+
+       /**
+        * Gets the parent dependency object.
+        *
+        * @return \TYPO3\CMS\Version\Dependency\DependencyResolver
+        */
+       public function getDependency() {
+               return $this->dependency;
+       }
+
+       /**
+        * Gets all child references.
+        *
+        * @return array
+        */
+       public function getChildren() {
+               if (!isset($this->children)) {
+                       $this->children = array();
+                       $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'sys_refindex', 'tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->table, 'sys_refindex') . ' AND recuid=' . $this->id);
+                       if (is_array($rows)) {
+                               foreach ($rows as $row) {
+                                       $reference = $this->getDependency()->getFactory()->getReferencedElement($row['ref_table'], $row['ref_uid'], $row['field'], array(), $this->getDependency());
+                                       $callbackResponse = $this->dependency->executeEventCallback(self::EVENT_CreateChildReference, $this, array('reference' => $reference));
+                                       if ($callbackResponse !== self::RESPONSE_Skip) {
+                                               $this->children[] = $reference;
+                                       }
+                               }
+                       }
+               }
+               return $this->children;
+       }
+
+       /**
+        * Gets all parent references.
+        *
+        * @return array
+        */
+       public function getParents() {
+               if (!isset($this->parents)) {
+                       $this->parents = array();
+                       $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', 'sys_refindex', 'ref_table=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->table, 'sys_refindex') . ' AND deleted=0 AND ref_uid=' . $this->id);
+                       if (is_array($rows)) {
+                               foreach ($rows as $row) {
+                                       $reference = $this->getDependency()->getFactory()->getReferencedElement($row['tablename'], $row['recuid'], $row['field'], array(), $this->getDependency());
+                                       $callbackResponse = $this->dependency->executeEventCallback(self::EVENT_CreateParentReference, $this, array('reference' => $reference));
+                                       if ($callbackResponse !== self::RESPONSE_Skip) {
+                                               $this->parents[] = $reference;
+                                       }
+                               }
+                       }
+               }
+               return $this->parents;
+       }
+
+       /**
+        * Determines whether there are child or parent references.
+        *
+        * @return boolean
+        */
+       public function hasReferences() {
+               return count($this->getChildren()) > 0 || count($this->getParents()) > 0;
+       }
+
+       /**
+        * Gets the outermost parent element.
+        *
+        * @return \TYPO3\CMS\Version\Dependency\ElementEntity
+        */
+       public function getOuterMostParent() {
+               if (!isset($this->outerMostParent)) {
+                       $parents = $this->getParents();
+                       if (count($parents) === 0) {
+                               $this->outerMostParent = $this;
+                       } else {
+                               $this->outerMostParent = FALSE;
+                               /** @var $parent \TYPO3\CMS\Version\Dependency\ReferenceEntity */
+                               foreach ($parents as $parent) {
+                                       $outerMostParent = $parent->getElement()->getOuterMostParent();
+                                       if ($outerMostParent instanceof \TYPO3\CMS\Version\Dependency\ElementEntity) {
+                                               $this->outerMostParent = $outerMostParent;
+                                               break;
+                                       } elseif ($outerMostParent === FALSE) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               return $this->outerMostParent;
+       }
+
+       /**
+        * Gets nested children accumulated.
+        *
+        * @return array
+        */
+       public function getNestedChildren() {
+               if (!isset($this->nestedChildren)) {
+                       $this->nestedChildren = array();
+                       $children = $this->getChildren();
+                       /** @var $child \TYPO3\CMS\Version\Dependency\ReferenceEntity */
+                       foreach ($children as $child) {
+                               $this->nestedChildren = array_merge($this->nestedChildren, array($child->getElement()->__toString() => $child->getElement()), $child->getElement()->getNestedChildren());
+                       }
+               }
+               return $this->nestedChildren;
+       }
+
+       /**
+        * Converts the object for string representation.
+        *
+        * @param string $table
+        * @param integer $id
+        * @return string
+        */
+       static public function getIdentifier($table, $id) {
+               return $table . ':' . $id;
+       }
+
+       /**
+        * Gets the database record of this element.
+        *
+        * @return array
+        */
+       public function getRecord() {
+               if (!isset($this->record)) {
+                       $this->record = array();
+                       $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $this->getTable(), 'uid=' . $this->getId());
+                       if (is_array($rows)) {
+                               $this->record = $rows[0];
+                       }
+               }
+               return $this->record;
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Dependency/EventCallback.php b/typo3/sysext/version/Classes/Dependency/EventCallback.php
new file mode 100644 (file)
index 0000000..d0e086a
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+namespace TYPO3\CMS\Version\Dependency;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010-2013 Oliver Hader <oliver@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!
+ ***************************************************************/
+/**
+ * Object to hold information on a callback to a defined object and method.
+ */
+class EventCallback {
+
+       /**
+        * @var object
+        */
+       protected $object;
+
+       /**
+        * @var string
+        */
+       protected $method;
+
+       /**
+        * @var array
+        */
+       protected $targetArguments;
+
+       /**
+        * Creates the objects.
+        *
+        * @param object $object
+        * @param string $method
+        * @param array $targetArguments (optional)
+        */
+       public function __construct($object, $method, array $targetArguments = array()) {
+               $this->object = $object;
+               $this->method = $method;
+               $this->targetArguments = $targetArguments;
+               $this->targetArguments['target'] = $object;
+       }
+
+       /**
+        * Executes the callback.
+        *
+        * @param array $callerArguments
+        * @param object $caller
+        * @param string $eventName
+        * @return mixed
+        */
+       public function execute(array $callerArguments = array(), $caller, $eventName) {
+               return call_user_func_array(array($this->object, $this->method), array($callerArguments, $this->targetArguments, $caller, $eventName));
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Dependency/ReferenceEntity.php b/typo3/sysext/version/Classes/Dependency/ReferenceEntity.php
new file mode 100644 (file)
index 0000000..2ef53ef
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+namespace TYPO3\CMS\Version\Dependency;
+
+/***************************************************************
+ * Copyright notice
+ *
+ * (c) 2010-2013 Oliver Hader <oliver@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!
+ ***************************************************************/
+/**
+ * Object to hold reference information of a database field and one accordant element.
+ */
+class ReferenceEntity {
+
+       /**
+        * @var \TYPO3\CMS\Version\Dependency\ElementEntity
+        */
+       protected $element;
+
+       /**
+        * @var string
+        */
+       protected $field;
+
+       /**
+        * Creates this object.
+        *
+        * @param \TYPO3\CMS\Version\Dependency\ElementEntity $element
+        * @param string $field
+        */
+       public function __construct(\TYPO3\CMS\Version\Dependency\ElementEntity $element, $field) {
+               $this->element = $element;
+               $this->field = $field;
+       }
+
+       /**
+        * Gets the elements.
+        *
+        * @return \TYPO3\CMS\Version\Dependency\ElementEntity
+        */
+       public function getElement() {
+               return $this->element;
+       }
+
+       /**
+        * Gets the field.
+        *
+        * @return string
+        */
+       public function getField() {
+               return $this->field;
+       }
+
+       /**
+        * Converts this object for string representation.
+        *
+        * @return string
+        */
+       public function __toString() {
+               return $this->element . '.' . $this->field;
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Hook/DataHandlerHook.php b/typo3/sysext/version/Classes/Hook/DataHandlerHook.php
new file mode 100644 (file)
index 0000000..338e5e8
--- /dev/null
@@ -0,0 +1,1306 @@
+<?php
+namespace TYPO3\CMS\Version\Hook;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
+ *  (c) 2010-2013 Benjamin Mack (benni@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!
+ ***************************************************************/
+/**
+ * Contains some parts for staging, versioning and workspaces
+ * to interact with the TYPO3 Core Engine
+ */
+class DataHandlerHook {
+
+       /**
+        * For accumulating information about workspace stages raised
+        * on elements so a single mail is sent as notification.
+        * previously called "accumulateForNotifEmail" in tcemain
+        *
+        * @var array
+        */
+       protected $notificationEmailInfo = array();
+
+       /**
+        * General comment, eg. for staging in workspaces
+        *
+        * @var string
+        */
+       protected $generalComment = '';
+
+       /**
+        * Contains remapped IDs.
+        *
+        * @var array
+        */
+       protected $remappedIds = array();
+
+       /****************************
+        *****  Cmdmap  Hooks  ******
+        ****************************/
+       /**
+        * hook that is called before any cmd of the commandmap is executed
+        *
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj reference to the main tcemain object
+        * @return void
+        */
+       public function processCmdmap_beforeStart(\TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               // Reset notification array
+               $this->notificationEmailInfo = array();
+               // Resolve dependencies of version/workspaces actions:
+               $tcemainObj->cmdmap = $this->getCommandMap($tcemainObj, $tcemainObj->cmdmap)->process()->get();
+       }
+
+       /**
+        * hook that is called when no prepared command was found
+        *
+        * @param string $command the command to be executed
+        * @param string $table the table of the record
+        * @param integer $id the ID of the record
+        * @param mixed $value the value containing the data
+        * @param boolean $commandIsProcessed can be set so that other hooks or
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj reference to the main tcemain object
+        * @return      void
+        */
+       public function processCmdmap($command, $table, $id, $value, &$commandIsProcessed, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               // custom command "version"
+               if ($command == 'version') {
+                       $commandIsProcessed = TRUE;
+                       $action = (string) $value['action'];
+                       $comment = (isset($value['comment']) && $value['comment'] ? $value['comment'] : $this->generalComment);
+                       $notificationAlternativeRecipients = (isset($value['notificationAlternativeRecipients'])) && is_array($value['notificationAlternativeRecipients']) ? $value['notificationAlternativeRecipients'] : array();
+                       switch ($action) {
+                       case 'new':
+                               // check if page / branch versioning is needed,
+                               // or if "element" version can be used
+                               $versionizeTree = -1;
+                               if (isset($value['treeLevels'])) {
+                                       $versionizeTree = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($value['treeLevels'], -1, 100);
+                               }
+                               if ($table == 'pages' && $versionizeTree >= 0) {
+                                       $this->versionizePages($id, $value['label'], $versionizeTree, $tcemainObj);
+                               } else {
+                                       $tcemainObj->versionizeRecord($table, $id, $value['label']);
+                               }
+                               break;
+                       case 'swap':
+                                       $this->version_swap($table, $id, $value['swapWith'], $value['swapIntoWS'],
+                                               $tcemainObj,
+                                               $comment,
+                                               TRUE,
+                                               $notificationAlternativeRecipients
+                                       );
+                               break;
+                       case 'clearWSID':
+                               $this->version_clearWSID($table, $id, FALSE, $tcemainObj);
+                               break;
+                       case 'flush':
+                               $this->version_clearWSID($table, $id, TRUE, $tcemainObj);
+                               break;
+                       case 'setStage':
+                               $elementIds = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $id, TRUE);
+                               foreach ($elementIds as $elementId) {
+                                               $this->version_setStage($table, $elementId, $value['stageId'],
+                                                       $comment,
+                                                       TRUE,
+                                                       $tcemainObj,
+                                                       $notificationAlternativeRecipients
+                                               );
+                               }
+                               break;
+                       }
+               }
+       }
+
+       /**
+        * Hook that is called after tcemain made most of its decisions.
+        *
+        * NOTE: This fixes an issue related to moving/creating initial-placeholders - if such a new page
+        * is intended to be place behind a move-placeholder tcemain handles the movement/creation,
+        * but does not respect the wsPlaceholder, which leads the new page to be located at the old location of the
+        * page where it was intended to be placed behind.
+        *
+        * @param string $command
+        * @param string $table
+        * @param int $id
+        * @param mixed $value
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemain
+        */
+       public function processCmdmap_postProcess($command, $table, $id, $value, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemain) {
+               if ($command === 'move') {
+                       if ($value < 0) {
+                               $movePlaceHolder = \TYPO3\CMS\Backend\Utility\BackendUtility::getMovePlaceholder($table, abs($value), 'uid');
+                               if ($movePlaceHolder !== FALSE) {
+                                       $destPid = -$movePlaceHolder['uid'];
+                                       $tcemain->moveRecord_raw($table, $id, $destPid);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * hook that is called AFTER all commands of the commandmap was
+        * executed
+        *
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj reference to the main tcemain object
+        * @return      void
+        */
+       public function processCmdmap_afterFinish(\TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               // Empty accumulation array:
+               foreach ($this->notificationEmailInfo as $notifItem) {
+                       $this->notifyStageChange($notifItem['shared'][0], $notifItem['shared'][1], implode(', ', $notifItem['elements']), 0, $notifItem['shared'][2], $tcemainObj, $notifItem['alternativeRecipients']);
+               }
+               // Reset notification array
+               $this->notificationEmailInfo = array();
+               // Reset remapped IDs
+               $this->remappedIds = array();
+       }
+
+       /**
+        * hook that is called AFTER all commands of the commandmap was
+        * executed
+        *
+        * @param string $table the table of the record
+        * @param integer $id the ID of the record
+        * @param array $record The accordant database record
+        * @param boolean $recordWasDeleted can be set so that other hooks or
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj reference to the main tcemain object
+        * @return      void
+        */
+       public function processCmdmap_deleteAction($table, $id, array $record, &$recordWasDeleted, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               // only process the hook if it wasn't processed
+               // by someone else before
+               if (!$recordWasDeleted) {
+                       $recordWasDeleted = TRUE;
+                       // For Live version, try if there is a workspace version because if so, rather "delete" that instead
+                       // Look, if record is an offline version, then delete directly:
+                       if ($record['pid'] != -1) {
+                               if ($wsVersion = \TYPO3\CMS\Backend\Utility\BackendUtility::getWorkspaceVersionOfRecord($tcemainObj->BE_USER->workspace, $table, $id)) {
+                                       $record = $wsVersion;
+                                       $id = $record['uid'];
+                               }
+                       }
+                       // Look, if record is an offline version, then delete directly:
+                       if ($record['pid'] == -1) {
+                               if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
+                                       // In Live workspace, delete any. In other workspaces there must be match.
+                                       if ($tcemainObj->BE_USER->workspace == 0 || (int) $record['t3ver_wsid'] == $tcemainObj->BE_USER->workspace) {
+                                               $liveRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getLiveVersionOfRecord($table, $id, 'uid,t3ver_state');
+                                               // Processing can be skipped if a delete placeholder shall be swapped/published
+                                               // during the current request. Thus it will be deleted later on...
+                                               if ($record['t3ver_state'] == 2 && !empty($liveRec['uid']) && !empty($tcemainObj->cmdmap[$table][$liveRec['uid']]['version']['action']) && !empty($tcemainObj->cmdmap[$table][$liveRec['uid']]['version']['swapWith']) && $tcemainObj->cmdmap[$table][$liveRec['uid']]['version']['action'] === 'swap' && $tcemainObj->cmdmap[$table][$liveRec['uid']]['version']['swapWith'] == $id) {
+                                                       return NULL;
+                                               }
+                                               // Delete those in WS 0 + if their live records state was not "Placeholder".
+                                               if ($record['t3ver_wsid'] == 0 || (int) $liveRec['t3ver_state'] <= 0) {
+                                                       $tcemainObj->deleteEl($table, $id);
+                                               } else {
+                                                       // If live record was placeholder (new/deleted), rather clear
+                                                       // it from workspace (because it clears both version and placeholder).
+                                                       $this->version_clearWSID($table, $id, FALSE, $tcemainObj);
+                                               }
+                                       } else {
+                                               $tcemainObj->newlog('Tried to delete record from another workspace', 1);
+                                       }
+                               } else {
+                                       $tcemainObj->newlog('Versioning not enabled for record with PID = -1!', 2);
+                               }
+                       } elseif ($res = $tcemainObj->BE_USER->workspaceAllowLiveRecordsInPID($record['pid'], $table)) {
+                               // Look, if record is "online" or in a versionized branch, then delete directly.
+                               if ($res > 0) {
+                                       $tcemainObj->deleteEl($table, $id);
+                               } else {
+                                       $tcemainObj->newlog('Stage of root point did not allow for deletion', 1);
+                               }
+                       } elseif ((int) $record['t3ver_state'] === 3) {
+                               // Placeholders for moving operations are deletable directly.
+                               // Get record which its a placeholder for and reset the t3ver_state of that:
+                               if ($wsRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getWorkspaceVersionOfRecord($record['t3ver_wsid'], $table, $record['t3ver_move_id'], 'uid')) {
+                                       // Clear the state flag of the workspace version of the record
+                                       // Setting placeholder state value for version (so it can know it is currently a new version...)
+                                       $updateFields = array(
+                                               't3ver_state' => 0
+                                       );
+                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($wsRec['uid']), $updateFields);
+                               }
+                               $tcemainObj->deleteEl($table, $id);
+                       } else {
+                               // Otherwise, try to delete by versioning:
+                               $copyMappingArray = $tcemainObj->copyMappingArray;
+                               $tcemainObj->versionizeRecord($table, $id, 'DELETED!', TRUE);
+                               // Determine newly created versions:
+                               // (remove placeholders are copied and modified, thus they appear in the copyMappingArray)
+                               $versionizedElements = \TYPO3\CMS\Core\Utility\GeneralUtility::arrayDiffAssocRecursive($tcemainObj->copyMappingArray, $copyMappingArray);
+                               // Delete localization overlays:
+                               foreach ($versionizedElements as $versionizedTableName => $versionizedOriginalIds) {
+                                       foreach ($versionizedOriginalIds as $versionizedOriginalId => $_) {
+                                               $tcemainObj->deleteL10nOverlayRecords($versionizedTableName, $versionizedOriginalId);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Hook for \TYPO3\CMS\Core\DataHandling\DataHandler::moveRecord that cares about
+        * moving records that are *not* in the live workspace
+        *
+        * @param string $table the table of the record
+        * @param integer $id the ID of the record
+        * @param integer $destPid Position to move to: $destPid: >=0 then it points to
+        * @param array $propArr Record properties, like header and pid (includes workspace overlay)
+        * @param array $moveRec Record properties, like header and pid (without workspace overlay)
+        * @param integer $resolvedPid The final page ID of the record
+        * @param boolean $recordWasMoved can be set so that other hooks or
+        * @param       $table  the table
+        */
+       public function moveRecord($table, $uid, $destPid, array $propArr, array $moveRec, $resolvedPid, &$recordWasMoved, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               // Only do something in Draft workspace
+               if ($tcemainObj->BE_USER->workspace !== 0) {
+                       $recordWasMoved = TRUE;
+                       // Get workspace version of the source record, if any:
+                       $WSversion = \TYPO3\CMS\Backend\Utility\BackendUtility::getWorkspaceVersionOfRecord($tcemainObj->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
+                       // If no version exists and versioningWS is in version 2, a new placeholder is made automatically:
+                       if (!$WSversion['uid'] && (int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2 && (int) $moveRec['t3ver_state'] != 3) {
+                               $tcemainObj->versionizeRecord($table, $uid, 'Placeholder version for moving record');
+                               $WSversion = \TYPO3\CMS\Backend\Utility\BackendUtility::getWorkspaceVersionOfRecord($tcemainObj->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
+                       }
+                       // Check workspace permissions:
+                       $workspaceAccessBlocked = array();
+                       // Element was in "New/Deleted/Moved" so it can be moved...
+                       $recIsNewVersion = (int) $moveRec['t3ver_state'] > 0;
+                       $destRes = $tcemainObj->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid, $table);
+                       $canMoveRecord = $recIsNewVersion || (int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2;
+                       // Workspace source check:
+                       if (!$recIsNewVersion) {
+                               $errorCode = $tcemainObj->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid);
+                               if ($errorCode) {
+                                       $workspaceAccessBlocked['src1'] = 'Record could not be edited in workspace: ' . $errorCode . ' ';
+                               } elseif (!$canMoveRecord && $tcemainObj->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'], $table) <= 0) {
+                                       $workspaceAccessBlocked['src2'] = 'Could not remove record from table "' . $table . '" from its page "' . $moveRec['pid'] . '" ';
+                               }
+                       }
+                       // Workspace destination check:
+                       // All records can be inserted if $destRes is greater than zero.
+                       // Only new versions can be inserted if $destRes is FALSE.
+                       // NO RECORDS can be inserted if $destRes is negative which indicates a stage
+                       //  not allowed for use. If "versioningWS" is version 2, moving can take place of versions.
+                       if (!($destRes > 0 || $canMoveRecord && !$destRes)) {
+                               $workspaceAccessBlocked['dest1'] = 'Could not insert record from table "' . $table . '" in destination PID "' . $resolvedPid . '" ';
+                       } elseif ($destRes == 1 && $WSversion['uid']) {
+                               $workspaceAccessBlocked['dest2'] = 'Could not insert other versions in destination PID ';
+                       }
+                       if (!count($workspaceAccessBlocked)) {
+                               // If the move operation is done on a versioned record, which is
+                               // NOT new/deleted placeholder and versioningWS is in version 2, then...
+                               if ($WSversion['uid'] && !$recIsNewVersion && (int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) {
+                                       $this->moveRecord_wsPlaceholders($table, $uid, $destPid, $WSversion['uid'], $tcemainObj);
+                               } else {
+                                       // moving not needed, just behave like in live workspace
+                                       $recordWasMoved = FALSE;
+                               }
+                       } else {
+                               $tcemainObj->newlog('Move attempt failed due to workspace restrictions: ' . implode(' // ', $workspaceAccessBlocked), 1);
+                       }
+               }
+       }
+
+       /****************************
+        *****  Notifications  ******
+        ****************************/
+       /**
+        * Send an email notification to users in workspace
+        *
+        * @param array $stat Workspace access array from \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::checkWorkspace()
+        * @param integer $stageId New Stage number: 0 = editing, 1= just ready for review, 10 = ready for publication, -1 = rejected!
+        * @param string $table Table name of element (or list of element names if $id is zero)
+        * @param integer $id Record uid of element (if zero, then $table is used as reference to element(s) alone)
+        * @param string $comment User comment sent along with action
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @param array $notificationAlternativeRecipients List of recipients to notify instead of be_users selected by sys_workspace, list is generated by workspace extension module
+        * @return void
+        */
+       protected function notifyStageChange(array $stat, $stageId, $table, $id, $comment, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj, array $notificationAlternativeRecipients = array()) {
+               $workspaceRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('sys_workspace', $stat['uid']);
+               // So, if $id is not set, then $table is taken to be the complete element name!
+               $elementName = $id ? $table . ':' . $id : $table;
+               if (is_array($workspaceRec)) {
+                       // Get the new stage title from workspaces library, if workspaces extension is installed
+                       if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
+                               $stageService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Workspaces\\Service\\StagesService');
+                               $newStage = $stageService->getStageTitle((int) $stageId);
+                       } else {
+                               // TODO: CONSTANTS SHOULD BE USED - tx_service_workspace_workspaces
+                               // TODO: use localized labels
+                               // Compile label:
+                               switch ((int) $stageId) {
+                               case 1:
+                                       $newStage = 'Ready for review';
+                                       break;
+                               case 10:
+                                       $newStage = 'Ready for publishing';
+                                       break;
+                               case -1:
+                                       $newStage = 'Element was rejected!';
+                                       break;
+                               case 0:
+                                       $newStage = 'Rejected element was noticed and edited';
+                                       break;
+                               default:
+                                       $newStage = 'Unknown state change!?';
+                                       break;
+                               }
+                       }
+                       if (count($notificationAlternativeRecipients) == 0) {
+                               // Compile list of recipients:
+                               $emails = array();
+                               switch ((int) $stat['stagechg_notification']) {
+                               case 1:
+                                       switch ((int) $stageId) {
+                                       case 1:
+                                               $emails = $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']);
+                                               break;
+                                       case 10:
+                                               $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE);
+                                               break;
+                                       case -1:
+                                               // List of elements to reject:
+                                               $allElements = explode(',', $elementName);
+                                               // Traverse them, and find the history of each
+                                               foreach ($allElements as $elRef) {
+                                                       list($eTable, $eUid) = explode(':', $elRef);
+                                                       $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('log_data,tstamp,userid', 'sys_log', 'action=6 and details_nr=30
+                                                                                       AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($eTable, 'sys_log') . '
+                                                                                       AND recuid=' . intval($eUid), '', 'uid DESC');
+                                                       // Find all implicated since the last stage-raise from editing to review:
+                                                       foreach ($rows as $dat) {
+                                                               $data = unserialize($dat['log_data']);
+                                                               $emails = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge($emails, $this->getEmailsForStageChangeNotification($dat['userid'], TRUE));
+                                                               if ($data['stage'] == 1) {
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               break;
+                                       case 0:
+                                               $emails = $this->getEmailsForStageChangeNotification($workspaceRec['members']);
+                                               break;
+                                       default:
+                                               $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE);
+                                               break;
+                                       }
+                                       break;
+                               case 10:
+                                       $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE);
+                                       $emails = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge($emails, $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']));
+                                       $emails = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge($emails, $this->getEmailsForStageChangeNotification($workspaceRec['members']));
+                                       break;
+                               }
+                       } else {
+                               $emails = $notificationAlternativeRecipients;
+                       }
+                       // prepare and then send the emails
+                       if (count($emails)) {
+                               // Path to record is found:
+                               list($elementTable, $elementUid) = explode(':', $elementName);
+                               $elementUid = intval($elementUid);
+                               $elementRecord = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($elementTable, $elementUid);
+                               $recordTitle = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($elementTable, $elementRecord);
+                               if ($elementTable == 'pages') {
+                                       $pageUid = $elementUid;
+                               } else {
+                                       \TYPO3\CMS\Backend\Utility\BackendUtility::fixVersioningPid($elementTable, $elementRecord);
+                                       $pageUid = ($elementUid = $elementRecord['pid']);
+                               }
+                               // fetch the TSconfig settings for the email
+                               // old way, options are TCEMAIN.notificationEmail_body/subject
+                               $TCEmainTSConfig = $tcemainObj->getTCEMAIN_TSconfig($pageUid);
+                               // new way, options are
+                               // pageTSconfig: tx_version.workspaces.stageNotificationEmail.subject
+                               // userTSconfig: page.tx_version.workspaces.stageNotificationEmail.subject
+                               $pageTsConfig = \TYPO3\CMS\Backend\Utility\BackendUtility::getPagesTSconfig($pageUid);
+                               $emailConfig = $pageTsConfig['tx_version.']['workspaces.']['stageNotificationEmail.'];
+                               $markers = array(
+                                       '###RECORD_TITLE###' => $recordTitle,
+                                       '###RECORD_PATH###' => \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordPath($elementUid, '', 20),
+                                       '###SITE_NAME###' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
+                                       '###SITE_URL###' => \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir,
+                                       '###WORKSPACE_TITLE###' => $workspaceRec['title'],
+                                       '###WORKSPACE_UID###' => $workspaceRec['uid'],
+                                       '###ELEMENT_NAME###' => $elementName,
+                                       '###NEXT_STAGE###' => $newStage,
+                                       '###COMMENT###' => $comment,
+                                       // See: #30212 - keep both markers for compatibility
+                                       '###USER_REALNAME###' => $tcemainObj->BE_USER->user['realName'],
+                                       '###USER_FULLNAME###' => $tcemainObj->BE_USER->user['realName'],
+                                       '###USER_USERNAME###' => $tcemainObj->BE_USER->user['username']
+                               );
+                               // add marker for preview links if workspace extension is loaded
+                               if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
+                                       $this->workspaceService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_Workspaces_Service_Workspaces');
+                                       // only generate the link if the marker is in the template - prevents database from getting to much entries
+                                       if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($emailConfig['message'], 'LLL:')) {
+                                               $tempEmailMessage = $GLOBALS['LANG']->sL($emailConfig['message']);
+                                       } else {
+                                               $tempEmailMessage = $emailConfig['message'];
+                                       }
+                                       if (strpos($tempEmailMessage, '###PREVIEW_LINK###') !== FALSE) {
+                                               $markers['###PREVIEW_LINK###'] = $this->workspaceService->generateWorkspacePreviewLink($elementUid);
+                                       }
+                                       unset($tempEmailMessage);
+                                       $markers['###SPLITTED_PREVIEW_LINK###'] = $this->workspaceService->generateWorkspaceSplittedPreviewLink($elementUid, TRUE);
+                               }
+                               // Hook for preprocessing of the content for formmails:
+                               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/version/class.tx_version_tcemain.php']['notifyStageChange-postModifyMarkers'])) {
+                                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/version/class.tx_version_tcemain.php']['notifyStageChange-postModifyMarkers'] as $_classRef) {
+                                               $_procObj =& \TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($_classRef);
+                                               $markers = $_procObj->postModifyMarkers($markers, $this);
+                                       }
+                               }
+                               // send an email to each individual user, to ensure the
+                               // multilanguage version of the email
+                               $emailRecipients = array();
+                               // an array of language objects that are needed
+                               // for emails with different languages
+                               $languageObjects = array(
+                                       $GLOBALS['LANG']->lang => $GLOBALS['LANG']
+                               );
+                               // loop through each recipient and send the email
+                               foreach ($emails as $recipientData) {
+                                       // don't send an email twice
+                                       if (isset($emailRecipients[$recipientData['email']])) {
+                                               continue;
+                                       }
+                                       $emailSubject = $emailConfig['subject'];
+                                       $emailMessage = $emailConfig['message'];
+                                       $emailRecipients[$recipientData['email']] = $recipientData['email'];
+                                       // check if the email needs to be localized
+                                       // in the users' language
+                                       if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($emailSubject, 'LLL:') || \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($emailMessage, 'LLL:')) {
+                                               $recipientLanguage = $recipientData['lang'] ? $recipientData['lang'] : 'default';
+                                               if (!isset($languageObjects[$recipientLanguage])) {
+                                                       // a LANG object in this language hasn't been
+                                                       // instantiated yet, so this is done here
+                                                       /** @var $languageObject \TYPO3\CMS\Lang\LanguageService */
+                                                       $languageObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Lang\\LanguageService');
+                                                       $languageObject->init($recipientLanguage);
+                                                       $languageObjects[$recipientLanguage] = $languageObject;
+                                               } else {
+                                                       $languageObject = $languageObjects[$recipientLanguage];
+                                               }
+                                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($emailSubject, 'LLL:')) {
+                                                       $emailSubject = $languageObject->sL($emailSubject);
+                                               }
+                                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($emailMessage, 'LLL:')) {
+                                                       $emailMessage = $languageObject->sL($emailMessage);
+                                               }
+                                       }
+                                       $emailSubject = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($emailSubject, $markers, '', TRUE, TRUE);
+                                       $emailMessage = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($emailMessage, $markers, '', TRUE, TRUE);
+                                       // Send an email to the recipient
+                                       /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
+                                       $mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
+                                       if (!empty($recipientData['realName'])) {
+                                               $recipient = array($recipientData['email'] => $recipientData['realName']);
+                                       } else {
+                                               $recipient = $recipientData['email'];
+                                       }
+                                       $mail->setTo($recipient)
+                                               ->setSubject($emailSubject)
+                                               ->setFrom(\TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom())
+                                               ->setBody($emailMessage);
+                                       $mail->send();
+                               }
+                               $emailRecipients = implode(',', $emailRecipients);
+                               $tcemainObj->newlog2('Notification email for stage change was sent to "' . $emailRecipients . '"', $table, $id);
+                       }
+               }
+       }
+
+       /**
+        * Return be_users that should be notified on stage change from input list.
+        * previously called notifyStageChange_getEmails() in tcemain
+        *
+        * @param       string          $listOfUsers List of backend users, on the form "be_users_10,be_users_2" or "10,2" in case noTablePrefix is set.
+        * @param       boolean         $noTablePrefix If set, the input list are integers and not strings.
+        * @return      array           Array of emails
+        */
+       protected function getEmailsForStageChangeNotification($listOfUsers, $noTablePrefix = FALSE) {
+               $users = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $listOfUsers, 1);
+               $emails = array();
+               foreach ($users as $userIdent) {
+                       if ($noTablePrefix) {
+                               $id = intval($userIdent);
+                       } else {
+                               list($table, $id) = \TYPO3\CMS\Core\Utility\GeneralUtility::revExplode('_', $userIdent, 2);
+                       }
+                       if ($table === 'be_users' || $noTablePrefix) {
+                               if ($userRecord = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('be_users', $id, 'uid,email,lang,realName', \TYPO3\CMS\Backend\Utility\BackendUtility::BEenableFields('be_users'))) {
+                                       if (strlen(trim($userRecord['email']))) {
+                                               $emails[$id] = $userRecord;
+                                       }
+                               }
+                       }
+               }
+               return $emails;
+       }
+
+       /****************************
+        *****  Stage Changes  ******
+        ****************************/
+       /**
+        * Setting stage of record
+        *
+        * @param string $table Table name
+        * @param integer $integer Record UID
+        * @param integer $stageId Stage ID to set
+        * @param string $comment Comment that goes into log
+        * @param boolean $notificationEmailInfo Accumulate state changes in memory for compiled notification email?
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @param array $notificationAlternativeRecipients comma separated list of recipients to notify instead of normal be_users
+        * @return void
+        */
+       protected function version_setStage($table, $id, $stageId, $comment = '', $notificationEmailInfo = FALSE, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj, array $notificationAlternativeRecipients = array()) {
+               if ($errorCode = $tcemainObj->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
+                       $tcemainObj->newlog('Attempt to set stage for record failed: ' . $errorCode, 1);
+               } elseif ($tcemainObj->checkRecordUpdateAccess($table, $id)) {
+                       $record = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $id);
+                       $stat = $tcemainObj->BE_USER->checkWorkspace($record['t3ver_wsid']);
+                       // check if the usere is allowed to the current stage, so it's also allowed to send to next stage
+                       if ($GLOBALS['BE_USER']->workspaceCheckStageForCurrent($record['t3ver_stage'])) {
+                               // Set stage of record:
+                               $updateData = array(
+                                       't3ver_stage' => $stageId
+                               );
+                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $updateData);
+                               $tcemainObj->newlog2('Stage for record was changed to ' . $stageId . '. Comment was: "' . substr($comment, 0, 100) . '"', $table, $id);
+                               // TEMPORARY, except 6-30 as action/detail number which is observed elsewhere!
+                               $tcemainObj->log($table, $id, 6, 0, 0, 'Stage raised...', 30, array('comment' => $comment, 'stage' => $stageId));
+                               if ((int) $stat['stagechg_notification'] > 0) {
+                                       if ($notificationEmailInfo) {
+                                               $this->notificationEmailInfo[$stat['uid'] . ':' . $stageId . ':' . $comment]['shared'] = array($stat, $stageId, $comment);
+                                               $this->notificationEmailInfo[$stat['uid'] . ':' . $stageId . ':' . $comment]['elements'][] = $table . ':' . $id;
+                                               $this->notificationEmailInfo[$stat['uid'] . ':' . $stageId . ':' . $comment]['alternativeRecipients'] = $notificationAlternativeRecipients;
+                                       } else {
+                                               $this->notifyStageChange($stat, $stageId, $table, $id, $comment, $tcemainObj, $notificationAlternativeRecipients);
+                                       }
+                               }
+                       } else {
+                               $tcemainObj->newlog('The member user tried to set a stage value "' . $stageId . '" that was not allowed', 1);
+                       }
+               } else {
+                       $tcemainObj->newlog('Attempt to set stage for record failed because you do not have edit access', 1);
+               }
+       }
+
+       /*****************************
+        *****  CMD versioning  ******
+        *****************************/
+       /**
+        * Creates a new version of a page including content and possible subpages.
+        *
+        * @param integer $uid Page uid to create new version of.
+        * @param string $label Version label
+        * @param integer $versionizeTree Indicating "treeLevel" - "page" (0) or "branch" (>=1) ["element" type must call versionizeRecord() directly]
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @return void
+        * @see copyPages()
+        */
+       protected function versionizePages($uid, $label, $versionizeTree, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               $uid = intval($uid);
+               // returns the branch
+               $brExist = $tcemainObj->doesBranchExist('', $uid, $tcemainObj->pMap['show'], 1);
+               // Checks if we had permissions
+               if ($brExist != -1) {
+                       // Make list of tables that should come along with a new version of the page:
+                       $verTablesArray = array();
+                       $allTables = array_keys($GLOBALS['TCA']);
+                       foreach ($allTables as $tableName) {
+                               if ($tableName != 'pages' && ($versionizeTree > 0 || $GLOBALS['TCA'][$tableName]['ctrl']['versioning_followPages'])) {
+                                       $verTablesArray[] = $tableName;
+                               }
+                       }
+                       // Remove the possible inline child tables from the tables to be versioniozed automatically:
+                       $verTablesArray = array_diff($verTablesArray, $this->getPossibleInlineChildTablesOfParentTable('pages'));
+                       // Begin to copy pages if we're allowed to:
+                       if ($versionizeTree === -1) {
+                               // Versionize this page:
+                               $theNewRootID = $tcemainObj->versionizeRecord('pages', $uid, $label, FALSE, $versionizeTree);
+                               if ($theNewRootID) {
+                                       $this->rawCopyPageContent($uid, $theNewRootID, $verTablesArray, $tcemainObj);
+                                       // If we're going to copy recursively...:
+                                       if ($versionizeTree > 0) {
+                                               // Get ALL subpages to copy (read permissions respected - they should NOT be...):
+                                               $CPtable = $tcemainObj->int_pageTreeInfo(array(), $uid, intval($versionizeTree), $theNewRootID);
+                                               // Now copying the subpages
+                                               foreach ($CPtable as $thePageUid => $thePagePid) {
+                                                       $newPid = $tcemainObj->copyMappingArray['pages'][$thePagePid];
+                                                       if (isset($newPid)) {
+                                                               $theNewRootID = $tcemainObj->copyRecord_raw('pages', $thePageUid, $newPid);
+                                                               $this->rawCopyPageContent($thePageUid, $theNewRootID, $verTablesArray, $tcemainObj);
+                                                       } else {
+                                                               $tcemainObj->newlog('Something went wrong during copying branch (for versioning)', 1);
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               } else {
+                                       $tcemainObj->newlog('The root version could not be created!', 1);
+                               }
+                       } else {
+                               $tcemainObj->newlog('Versioning type "' . $versionizeTree . '" was not allowed in workspace', 1);
+                       }
+               } else {
+                       $tcemainObj->newlog('Could not read all subpages to versionize.', 1);
+               }
+       }
+
+       /**
+        * Swapping versions of a record
+        * Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also
+        *
+        * @param string $table Table name
+        * @param integer $id UID of the online record to swap
+        * @param integer $swapWith UID of the archived version to swap with!
+        * @param boolean $swapIntoWS If set, swaps online into workspace instead of publishing out of workspace.
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @param string $comment Notification comment
+        * @param boolean $notificationEmailInfo Accumulate state changes in memory for compiled notification email?
+        * @param array $notificationAlternativeRecipients comma separated list of recipients to notificate instead of normal be_users
+        * @return void
+        */
+       protected function version_swap($table, $id, $swapWith, $swapIntoWS = 0, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj, $comment = '', $notificationEmailInfo = FALSE, $notificationAlternativeRecipients = array()) {
+               // First, check if we may actually edit the online record
+               if ($tcemainObj->checkRecordUpdateAccess($table, $id)) {
+                       // Select the two versions:
+                       $curVersion = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $id, '*');
+                       $swapVersion = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $swapWith, '*');
+                       $movePlh = array();
+                       $movePlhID = 0;
+                       if (is_array($curVersion) && is_array($swapVersion)) {
+                               if ($tcemainObj->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) {
+                                       $wsAccess = $tcemainObj->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']);
+                                       if ($swapVersion['t3ver_wsid'] <= 0 || !($wsAccess['publish_access'] & 1) || (int) $swapVersion['t3ver_stage'] === -10) {
+                                               if ($tcemainObj->doesRecordExist($table, $swapWith, 'show') && $tcemainObj->checkRecordUpdateAccess($table, $swapWith)) {
+                                                       if (!$swapIntoWS || $tcemainObj->BE_USER->workspaceSwapAccess()) {
+                                                               // Check if the swapWith record really IS a version of the original!
+                                                               if (((int) $swapVersion['pid'] == -1 && (int) $curVersion['pid'] >= 0) && !strcmp($swapVersion['t3ver_oid'], $id)) {
+                                                                       // Lock file name:
+                                                                       $lockFileName = PATH_site . 'typo3temp/swap_locking/' . $table . ':' . $id . '.ser';
+                                                                       if (!@is_file($lockFileName)) {
+                                                                               // Write lock-file:
+                                                                               \TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($lockFileName, serialize(array(
+                                                                                       'tstamp' => $GLOBALS['EXEC_TIME'],
+                                                                                       'user' => $tcemainObj->BE_USER->user['username'],
+                                                                                       'curVersion' => $curVersion,
+                                                                                       'swapVersion' => $swapVersion
+                                                                               )));
+                                                                               // Find fields to keep
+                                                                               $keepFields = $this->getUniqueFields($table);
+                                                                               if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
+                                                                                       $keepFields[] = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
+                                                                               }
+                                                                               // l10n-fields must be kept otherwise the localization
+                                                                               // will be lost during the publishing
+                                                                               if (!isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
+                                                                                       $keepFields[] = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
+                                                                               }
+                                                                               // Swap "keepfields"
+                                                                               foreach ($keepFields as $fN) {
+                                                                                       $tmp = $swapVersion[$fN];
+                                                                                       $swapVersion[$fN] = $curVersion[$fN];
+                                                                                       $curVersion[$fN] = $tmp;
+                                                                               }
+                                                                               // Preserve states:
+                                                                               $t3ver_state = array();
+                                                                               $t3ver_state['swapVersion'] = $swapVersion['t3ver_state'];
+                                                                               $t3ver_state['curVersion'] = $curVersion['t3ver_state'];
+                                                                               // Modify offline version to become online:
+                                                                               $tmp_wsid = $swapVersion['t3ver_wsid'];
+                                                                               // Set pid for ONLINE
+                                                                               $swapVersion['pid'] = intval($curVersion['pid']);
+                                                                               // We clear this because t3ver_oid only make sense for offline versions
+                                                                               // and we want to prevent unintentional misuse of this
+                                                                               // value for online records.
+                                                                               $swapVersion['t3ver_oid'] = 0;
+                                                                               // In case of swapping and the offline record has a state
+                                                                               // (like 2 or 4 for deleting or move-pointer) we set the
+                                                                               // current workspace ID so the record is not deselected
+                                                                               // in the interface by \TYPO3\CMS\Backend\Utility\BackendUtility::versioningPlaceholderClause()
+                                                                               $swapVersion['t3ver_wsid'] = 0;
+                                                                               if ($swapIntoWS) {
+                                                                                       if ($t3ver_state['swapVersion'] > 0) {
+                                                                                               $swapVersion['t3ver_wsid'] = $tcemainObj->BE_USER->workspace;
+                                                                                       } else {
+                                                                                               $swapVersion['t3ver_wsid'] = intval($curVersion['t3ver_wsid']);
+                                                                                       }
+                                                                               }
+                                                                               $swapVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
+                                                                               $swapVersion['t3ver_stage'] = 0;
+                                                                               if (!$swapIntoWS) {
+                                                                                       $swapVersion['t3ver_state'] = 0;
+                                                                               }
+                                                                               // Moving element.
+                                                                               if ((int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) {
+                                                                                       //  && $t3ver_state['swapVersion']==4   // Maybe we don't need this?
+                                                                                       if ($plhRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getMovePlaceholder($table, $id, 't3ver_state,pid,uid' . ($GLOBALS['TCA'][$table]['ctrl']['sortby'] ? ',' . $GLOBALS['TCA'][$table]['ctrl']['sortby'] : ''))) {
+                                                                                               $movePlhID = $plhRec['uid'];
+                                                                                               $movePlh['pid'] = $swapVersion['pid'];
+                                                                                               $swapVersion['pid'] = intval($plhRec['pid']);
+                                                                                               $curVersion['t3ver_state'] = intval($swapVersion['t3ver_state']);
+                                                                                               $swapVersion['t3ver_state'] = 0;
+                                                                                               if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
+                                                                                                       // sortby is a "keepFields" which is why this will work...
+                                                                                                       $movePlh[$GLOBALS['TCA'][$table]['ctrl']['sortby']] = $swapVersion[$GLOBALS['TCA'][$table]['ctrl']['sortby']];
+                                                                                                       $swapVersion[$GLOBALS['TCA'][$table]['ctrl']['sortby']] = $plhRec[$GLOBALS['TCA'][$table]['ctrl']['sortby']];
+                                                                                               }
+                                                                                       }
+                                                                               }
+                                                                               // Take care of relations in each field (e.g. IRRE):
+                                                                               if (is_array($GLOBALS['TCA'][$table]['columns'])) {
+                                                                                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $fieldConf) {
+                                                                                               $this->version_swap_procBasedOnFieldType($table, $field, $fieldConf['config'], $curVersion, $swapVersion, $tcemainObj);
+                                                                                       }
+                                                                               }
+                                                                               unset($swapVersion['uid']);
+                                                                               // Modify online version to become offline:
+                                                                               unset($curVersion['uid']);
+                                                                               // Set pid for OFFLINE
+                                                                               $curVersion['pid'] = -1;
+                                                                               $curVersion['t3ver_oid'] = intval($id);
+                                                                               $curVersion['t3ver_wsid'] = $swapIntoWS ? intval($tmp_wsid) : 0;
+                                                                               $curVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
+                                                                               $curVersion['t3ver_count'] = $curVersion['t3ver_count'] + 1;
+                                                                               // Increment lifecycle counter
+                                                                               $curVersion['t3ver_stage'] = 0;
+                                                                               if (!$swapIntoWS) {
+                                                                                       $curVersion['t3ver_state'] = 0;
+                                                                               }
+                                                                               // Registering and swapping MM relations in current and swap records:
+                                                                               $tcemainObj->version_remapMMForVersionSwap($table, $id, $swapWith);
+                                                                               // Generating proper history data to prepare logging
+                                                                               $tcemainObj->compareFieldArrayWithCurrentAndUnset($table, $id, $swapVersion);
+                                                                               $tcemainObj->compareFieldArrayWithCurrentAndUnset($table, $swapWith, $curVersion);
+                                                                               // Execute swapping:
+                                                                               $sqlErrors = array();
+                                                                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $swapVersion);
+                                                                               if ($GLOBALS['TYPO3_DB']->sql_error()) {
+                                                                                       $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
+                                                                               } else {
+                                                                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($swapWith), $curVersion);
+                                                                                       if ($GLOBALS['TYPO3_DB']->sql_error()) {
+                                                                                               $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
+                                                                                       } else {
+                                                                                               unlink($lockFileName);
+                                                                                       }
+                                                                               }
+                                                                               if (!count($sqlErrors)) {
+                                                                                       // Register swapped ids for later remapping:
+                                                                                       $this->remappedIds[$table][$id] = $swapWith;
+                                                                                       $this->remappedIds[$table][$swapWith] = $id;
+                                                                                       // If a moving operation took place...:
+                                                                                       if ($movePlhID) {
+                                                                                               // Remove, if normal publishing:
+                                                                                               if (!$swapIntoWS) {
+                                                                                                       // For delete + completely delete!
+                                                                                                       $tcemainObj->deleteEl($table, $movePlhID, TRUE, TRUE);
+                                                                                               } else {
+                                                                                                       // Otherwise update the movePlaceholder:
+                                                                                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($movePlhID), $movePlh);
+                                                                                                       $tcemainObj->addRemapStackRefIndex($table, $movePlhID);
+                                                                                               }
+                                                                                       }
+                                                                                       // Checking for delete:
+                                                                                       // Delete only if new/deleted placeholders are there.
+                                                                                       if (!$swapIntoWS && ((int) $t3ver_state['swapVersion'] === 1 || (int) $t3ver_state['swapVersion'] === 2)) {
+                                                                                               // Force delete
+                                                                                               $tcemainObj->deleteEl($table, $id, TRUE);
+                                                                                       }
+                                                                                       $tcemainObj->newlog2(($swapIntoWS ? 'Swapping' : 'Publishing') . ' successful for table "' . $table . '" uid ' . $id . '=>' . $swapWith, $table, $id, $swapVersion['pid']);
+                                                                                       // Update reference index of the live record:
+                                                                                       $tcemainObj->addRemapStackRefIndex($table, $id);
+                                                                                       // Set log entry for live record:
+                                                                                       $propArr = $tcemainObj->getRecordPropertiesFromRow($table, $swapVersion);
+                                                                                       if ($propArr['_ORIG_pid'] == -1) {
+                                                                                               $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xml:version_swap.offline_record_updated');
+                                                                                       } else {
+                                                                                               $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xml:version_swap.online_record_updated');
+                                                                                       }
+                                                                                       $theLogId = $tcemainObj->log($table, $id, 2, $propArr['pid'], 0, $label, 10, array($propArr['header'], $table . ':' . $id), $propArr['event_pid']);
+                                                                                       $tcemainObj->setHistory($table, $id, $theLogId);
+                                                                                       // Update reference index of the offline record:
+                                                                                       $tcemainObj->addRemapStackRefIndex($table, $swapWith);
+                                                                                       // Set log entry for offline record:
+                                                                                       $propArr = $tcemainObj->getRecordPropertiesFromRow($table, $curVersion);
+                                                                                       if ($propArr['_ORIG_pid'] == -1) {
+                                                                                               $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xml:version_swap.offline_record_updated');
+                                                                                       } else {
+                                                                                               $label = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tcemain.xml:version_swap.online_record_updated');
+                                                                                       }
+                                                                                       $theLogId = $tcemainObj->log($table, $swapWith, 2, $propArr['pid'], 0, $label, 10, array($propArr['header'], $table . ':' . $swapWith), $propArr['event_pid']);
+                                                                                       $tcemainObj->setHistory($table, $swapWith, $theLogId);
+
+                                                                                       $stageId = -20; // Tx_Workspaces_Service_Stages::STAGE_PUBLISH_EXECUTE_ID;
+                                                                                       if ($notificationEmailInfo) {
+                                                                                               $notificationEmailInfoKey = $wsAccess['uid'] . ':' . $stageId . ':' . $comment;
+                                                                                               $this->notificationEmailInfo[$notificationEmailInfoKey]['shared'] = array($wsAccess, $stageId, $comment);
+                                                                                               $this->notificationEmailInfo[$notificationEmailInfoKey]['elements'][] = $table . ':' . $id;
+                                                                                               $this->notificationEmailInfo[$notificationEmailInfoKey]['alternativeRecipients'] = $notificationAlternativeRecipients;
+                                                                                       } else {
+                                                                                               $this->notifyStageChange($wsAccess, $stageId, $table, $id, $comment, $tcemainObj, $notificationAlternativeRecipients);
+                                                                                       }
+                                                                                               // Write to log with stageId -20
+                                                                                       $tcemainObj->newlog2('Stage for record was changed to ' . $stageId . '. Comment was: "' . substr($comment, 0, 100) . '"', $table, $id);
+                                                                                       $tcemainObj->log($table, $id, 6, 0, 0, 'Published', 30, array('comment' => $comment, 'stage' => $stageId));
+
+                                                                                       // Clear cache:
+                                                                                       $tcemainObj->clear_cache($table, $id);
+                                                                                       // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!):
+                                                                                       if (!$swapIntoWS && $t3ver_state['curVersion'] > 0) {
+                                                                                               // For delete + completely delete!
+                                                                                               $tcemainObj->deleteEl($table, $swapWith, TRUE, TRUE);
+                                                                                       }
+                                                                               } else {
+                                                                                       $tcemainObj->newlog('During Swapping: SQL errors happened: ' . implode('; ', $sqlErrors), 2);
+                                                                               }
+                                                                       } else {
+                                                                               $tcemainObj->newlog('A swapping lock file was present. Either another swap process is already running or a previous swap process failed. Ask your administrator to handle the situation.', 2);
+                                                                       }
+                                                               } else {
+                                                                       $tcemainObj->newlog('In swap version, either pid was not -1 or the t3ver_oid didn\'t match the id of the online version as it must!', 2);
+                                                               }
+                                                       } else {
+                                                               $tcemainObj->newlog('Workspace #' . $swapVersion['t3ver_wsid'] . ' does not support swapping.', 1);
+                                                       }
+                                               } else {
+                                                       $tcemainObj->newlog('You cannot publish a record you do not have edit and show permissions for', 1);
+                                               }
+                                       } else {
+                                               $tcemainObj->newlog('Records in workspace #' . $swapVersion['t3ver_wsid'] . ' can only be published when in "Publish" stage.', 1);
+                                       }
+                               } else {
+                                       $tcemainObj->newlog('User could not publish records from workspace #' . $swapVersion['t3ver_wsid'], 1);
+                               }
+                       } else {
+                               $tcemainObj->newlog('Error: Either online or swap version could not be selected!', 2);
+                       }
+               } else {
+                       $tcemainObj->newlog('Error: You cannot swap versions for a record you do not have access to edit!', 1);
+               }
+       }
+
+       /**
+        * Update relations on version/workspace swapping.
+        *
+        * @param string $table: Record Table
+        * @param string $field: Record field
+        * @param array $conf: TCA configuration of current field
+        * @param array $curVersion: Reference to the current (original) record
+        * @param array $swapVersion: Reference to the record (workspace/versionized) to publish in or swap with
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @return void
+        */
+       protected function version_swap_procBasedOnFieldType($table, $field, array $conf, array &$curVersion, array &$swapVersion, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               $inlineType = $tcemainObj->getInlineFieldType($conf);
+               // Process pointer fields on normalized database:
+               if ($inlineType == 'field') {
+                       // Read relations that point to the current record (e.g. live record):
+                       /** @var $dbAnalysisCur \TYPO3\CMS\Core\Database\RelationHandler */
+                       $dbAnalysisCur = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
+                       $dbAnalysisCur->setUpdateReferenceIndex(FALSE);
+                       $dbAnalysisCur->start('', $conf['foreign_table'], '', $curVersion['uid'], $table, $conf);
+                       // Read relations that point to the record to be swapped with e.g. draft record):
+                       /** @var $dbAnalysisSwap \TYPO3\CMS\Core\Database\RelationHandler */
+                       $dbAnalysisSwap = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
+                       $dbAnalysisSwap->setUpdateReferenceIndex(FALSE);
+                       $dbAnalysisSwap->start('', $conf['foreign_table'], '', $swapVersion['uid'], $table, $conf);
+                       // Update relations for both (workspace/versioning) sites:
+                       if (count($dbAnalysisCur->itemArray)) {
+                               $dbAnalysisCur->writeForeignField($conf, $curVersion['uid'], $swapVersion['uid']);
+                               $tcemainObj->addRemapAction($table, $curVersion['uid'], array($this, 'writeRemappedForeignField'), array($dbAnalysisCur, $conf, $swapVersion['uid']));
+                       }
+                       if (count($dbAnalysisSwap->itemArray)) {
+                               $dbAnalysisSwap->writeForeignField($conf, $swapVersion['uid'], $curVersion['uid']);
+                               $tcemainObj->addRemapAction($table, $curVersion['uid'], array($this, 'writeRemappedForeignField'), array($dbAnalysisSwap, $conf, $curVersion['uid']));
+                       }
+                       $items = array_merge($dbAnalysisCur->itemArray, $dbAnalysisSwap->itemArray);
+                       foreach ($items as $item) {
+                               $tcemainObj->addRemapStackRefIndex($item['table'], $item['id']);
+                       }
+               } elseif ($inlineType == 'list') {
+                       $tempValue = $curVersion[$field];
+                       $curVersion[$field] = $swapVersion[$field];
+                       $swapVersion[$field] = $tempValue;
+               }
+       }
+
+       /**
+        * Writes remapped foreign field (IRRE).
+        *
+        * @param \TYPO3\CMS\Core\Database\RelationHandler $dbAnalysis Instance that holds the sorting order of child records
+        * @param array $configuration The TCA field configuration
+        * @param integer $parentId The uid of the parent record
+        * @return void
+        */
+       public function writeRemappedForeignField(\TYPO3\CMS\Core\Database\RelationHandler $dbAnalysis, array $configuration, $parentId) {
+               foreach ($dbAnalysis->itemArray as &$item) {
+                       if (isset($this->remappedIds[$item['table']][$item['id']])) {
+                               $item['id'] = $this->remappedIds[$item['table']][$item['id']];
+                       }
+               }
+               $dbAnalysis->writeForeignField($configuration, $parentId);
+       }
+
+       /**
+        * Release version from this workspace (and into "Live" workspace but as an offline version).
+        *
+        * @param string $table Table name
+        * @param integer $id Record UID
+        * @param boolean $flush If set, will completely delete element
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @return      void
+        */
+       protected function version_clearWSID($table, $id, $flush = FALSE, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               if ($errorCode = $tcemainObj->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
+                       $tcemainObj->newlog('Attempt to reset workspace for record failed: ' . $errorCode, 1);
+               } elseif ($tcemainObj->checkRecordUpdateAccess($table, $id)) {
+                       if ($liveRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getLiveVersionOfRecord($table, $id, 'uid,t3ver_state')) {
+                               // Clear workspace ID:
+                               $updateData = array(
+                                       't3ver_wsid' => 0,
+                                       't3ver_tstamp' => $GLOBALS['EXEC_TIME']
+                               );
+                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $updateData);
+                               // Clear workspace ID for live version AND DELETE IT as well because it is a new record!
+                               if ((int) $liveRec['t3ver_state'] == 1 || (int) $liveRec['t3ver_state'] == 2) {
+                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($liveRec['uid']), $updateData);
+                                       // THIS assumes that the record was placeholder ONLY for ONE record (namely $id)
+                                       $tcemainObj->deleteEl($table, $liveRec['uid'], TRUE);
+                               }
+                               // If "deleted" flag is set for the version that got released
+                               // it doesn't make sense to keep that "placeholder" anymore and we delete it completly.
+                               $wsRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $id);
+                               if ($flush || ((int) $wsRec['t3ver_state'] == 1 || (int) $wsRec['t3ver_state'] == 2)) {
+                                       $tcemainObj->deleteEl($table, $id, TRUE, TRUE);
+                               }
+                               // Remove the move-placeholder if found for live record.
+                               if ((int) $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] >= 2) {
+                                       if ($plhRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getMovePlaceholder($table, $liveRec['uid'], 'uid')) {
+                                               $tcemainObj->deleteEl($table, $plhRec['uid'], TRUE, TRUE);
+                                       }
+                               }
+                       }
+               } else {
+                       $tcemainObj->newlog('Attempt to reset workspace for record failed because you do not have edit access', 1);
+               }
+       }
+
+       /*******************************
+        *****  helper functions  ******
+        *******************************/
+       /**
+        * Copies all records from tables in $copyTablesArray from page with $old_pid to page with $new_pid
+        * Uses raw-copy for the operation (meant for versioning!)
+        *
+        * @param integer $oldPageId Current page id.
+        * @param integer $newPageId New page id
+        * @param array $copyTablesArray Array of tables from which to copy
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @return void
+        * @see versionizePages()
+        */
+       protected function rawCopyPageContent($oldPageId, $newPageId, array $copyTablesArray, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               if ($newPageId) {
+                       foreach ($copyTablesArray as $table) {
+                               // all records under the page is copied.
+                               if ($table && is_array($GLOBALS['TCA'][$table]) && $table !== 'pages') {
+                                       $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid=' . intval($oldPageId) . $tcemainObj->deleteClause($table));
+                                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
+                                               // Check, if this record has already been copied by a parent record as relation:
+                                               if (!$tcemainObj->copyMappingArray[$table][$row['uid']]) {
+                                                       // Copying each of the underlying records (method RAW)
+                                                       $tcemainObj->copyRecord_raw($table, $row['uid'], $newPageId);
+                                               }
+                                       }
+                                       $GLOBALS['TYPO3_DB']->sql_free_result($mres);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Finds all elements for swapping versions in workspace
+        *
+        * @param string $table Table name of the original element to swap
+        * @param integer $id UID of the original element to swap (online)
+        * @param integer $offlineId As above but offline
+        * @return array Element data. Key is table name, values are array with first element as online UID, second - offline UID
+        */
+       public function findPageElementsForVersionSwap($table, $id, $offlineId) {
+               $rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $offlineId, 't3ver_wsid');
+               $workspaceId = $rec['t3ver_wsid'];
+               $elementData = array();
+               if ($workspaceId != 0) {
+                       // Get page UID for LIVE and workspace
+                       if ($table != 'pages') {
+                               $rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $id, 'pid');
+                               $pageId = $rec['pid'];
+                               $rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $pageId);
+                               \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL('pages', $rec, $workspaceId);
+                               $offlinePageId = $rec['_ORIG_uid'];
+                       } else {
+                               $pageId = $id;
+                               $offlinePageId = $offlineId;
+                       }
+                       // Traversing all tables supporting versioning:
+                       foreach ($GLOBALS['TCA'] as $table => $cfg) {
+                               if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $table !== 'pages') {
+                                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('A.uid AS offlineUid, B.uid AS uid', $table . ' A,' . $table . ' B', 'A.pid=-1 AND B.pid=' . $pageId . ' AND A.t3ver_wsid=' . $workspaceId . ' AND B.uid=A.t3ver_oid' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'A') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'B'));
+                                       while (FALSE != ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
+                                               $elementData[$table][] = array($row[1], $row[0]);
+                                       }
+                                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
+                               }
+                       }
+                       if ($offlinePageId && $offlinePageId != $pageId) {
+                               $elementData['pages'][] = array($pageId, $offlinePageId);
+                       }
+               }
+               return $elementData;
+       }
+
+       /**
+        * Searches for all elements from all tables on the given pages in the same workspace.
+        *
+        * @param array $pageIdList List of PIDs to search
+        * @param integer $workspaceId Workspace ID
+        * @param array $elementList List of found elements. Key is table name, value is array of element UIDs
+        * @return void
+        */
+       public function findPageElementsForVersionStageChange(array $pageIdList, $workspaceId, array &$elementList) {
+               if ($workspaceId != 0) {
+                       // Traversing all tables supporting versioning:
+                       foreach ($GLOBALS['TCA'] as $table => $cfg) {
+                               if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $table !== 'pages') {
+                                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT A.uid', $table . ' A,' . $table . ' B', 'A.pid=-1' . ' AND A.t3ver_wsid=' . $workspaceId . ' AND B.pid IN (' . implode(',', $pageIdList) . ') AND A.t3ver_oid=B.uid' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'A') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'B'));
+                                       while (FALSE !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
+                                               $elementList[$table][] = $row[0];
+                                       }
+                                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
+                                       if (is_array($elementList[$table])) {
+                                               // Yes, it is possible to get non-unique array even with DISTINCT above!
+                                               // It happens because several UIDs are passed in the array already.
+                                               $elementList[$table] = array_unique($elementList[$table]);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Finds page UIDs for the element from table <code>$table</code> with UIDs from <code>$idList</code>
+        *
+        * @param string $table Table to search
+        * @param array $idList List of records' UIDs
+        * @param integer $workspaceId Workspace ID. We need this parameter because user can be in LIVE but he still can publisg DRAFT from ws module!
+        * @param array $pageIdList List of found page UIDs
+        * @param array $elementList List of found element UIDs. Key is table name, value is list of UIDs
+        * @return void
+        */
+       public function findPageIdsForVersionStateChange($table, array $idList, $workspaceId, array &$pageIdList, array &$elementList) {
+               if ($workspaceId != 0) {
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT B.pid', $table . ' A,' . $table . ' B', 'A.pid=-1' . ' AND A.t3ver_wsid=' . $workspaceId . ' AND A.uid IN (' . implode(',', $idList) . ') AND A.t3ver_oid=B.uid' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'A') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'B'));
+                       while (FALSE !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
+                               $pageIdList[] = $row[0];
+                               // Find ws version
+                               // Note: cannot use \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordWSOL()
+                               // here because it does not accept workspace id!
+                               $rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $row[0]);
+                               \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL('pages', $rec, $workspaceId);
+                               if ($rec['_ORIG_uid']) {
+                                       $elementList['pages'][$row[0]] = $rec['_ORIG_uid'];
+                               }
+                       }
+                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
+                       // The line below is necessary even with DISTINCT
+                       // because several elements can be passed by caller
+                       $pageIdList = array_unique($pageIdList);
+               }
+       }
+
+       /**
+        * Finds real page IDs for state change.
+        *
+        * @param       array   $idList List of page UIDs, possibly versioned
+        * @return      void
+        */
+       public function findRealPageIds(array &$idList) {
+               foreach ($idList as $key => $id) {
+                       $rec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $id, 't3ver_oid');
+                       if ($rec['t3ver_oid'] > 0) {
+                               $idList[$key] = $rec['t3ver_oid'];
+                       }
+               }
+       }
+
+       /**
+        * Creates a move placeholder for workspaces.
+        * USE ONLY INTERNALLY
+        * Moving placeholder: Can be done because the system sees it as a placeholder for NEW elements like t3ver_state=1
+        * Moving original: Will either create the placeholder if it doesn't exist or move existing placeholder in workspace.
+        *
+        * @param string $table Table name to move
+        * @param integer $uid Record uid to move (online record)
+        * @param integer $destPid Position to move to: $destPid: >=0 then it points to a page-id on which to insert the record (as the first element). <0 then it points to a uid from its own table after which to insert it (works if
+        * @param integer $wsUid UID of offline version of online record
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj TCEmain object
+        * @return void
+        * @see moveRecord()
+        */
+       protected function moveRecord_wsPlaceholders($table, $uid, $destPid, $wsUid, \TYPO3\CMS\Core\DataHandling\DataHandler $tcemainObj) {
+               // If a record gets moved after a record that already has a placeholder record
+               // then the new placeholder record needs to be after the existing one
+               $originalRecordDestinationPid = $destPid;
+               if ($destPid < 0) {
+                       $movePlaceHolder = \TYPO3\CMS\Backend\Utility\BackendUtility::getMovePlaceholder($table, abs($destPid), 'uid');
+                       if ($movePlaceHolder !== FALSE) {
+                               $destPid = -$movePlaceHolder['uid'];
+                       }
+               }
+               if ($plh = \TYPO3\CMS\Backend\Utility\BackendUtility::getMovePlaceholder($table, $uid, 'uid')) {
+                       // If already a placeholder exists, move it:
+                       $tcemainObj->moveRecord_raw($table, $plh['uid'], $destPid);
+               } else {
+                       // First, we create a placeholder record in the Live workspace that
+                       // represents the position to where the record is eventually moved to.
+                       $newVersion_placeholderFieldArray = array();
+                       if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
+                               $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
+                       }
+                       if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
+                               $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['cruser_id']] = $tcemainObj->userid;
+                       }
+                       if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
+                               $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
+                       }
+                       if ($table == 'pages') {
+                               // Copy page access settings from original page to placeholder
+                               $perms_clause = $tcemainObj->BE_USER->getPagePermsClause(1);
+                               $access = \TYPO3\CMS\Backend\Utility\BackendUtility::readPageAccess($uid, $perms_clause);
+                               $newVersion_placeholderFieldArray['perms_userid'] = $access['perms_userid'];
+                               $newVersion_placeholderFieldArray['perms_groupid'] = $access['perms_groupid'];
+                               $newVersion_placeholderFieldArray['perms_user'] = $access['perms_user'];
+                               $newVersion_placeholderFieldArray['perms_group'] = $access['perms_group'];
+                               $newVersion_placeholderFieldArray['perms_everybody'] = $access['perms_everybody'];
+                       }
+                       $newVersion_placeholderFieldArray['t3ver_label'] = 'MOVE-TO PLACEHOLDER for #' . $uid;
+                       $newVersion_placeholderFieldArray['t3ver_move_id'] = $uid;
+                       // Setting placeholder state value for temporary record
+                       $newVersion_placeholderFieldArray['t3ver_state'] = 3;
+                       // Setting workspace - only so display of place holders can filter out those from other workspaces.
+                       $newVersion_placeholderFieldArray['t3ver_wsid'] = $tcemainObj->BE_USER->workspace;
+                       $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['label']] = '[MOVE-TO PLACEHOLDER for #' . $uid . ', WS#' . $tcemainObj->BE_USER->workspace . ']';
+                       // moving localized records requires to keep localization-settings for the placeholder too
+                       if (array_key_exists('languageField', $GLOBALS['TCA'][$table]['ctrl']) && array_key_exists('transOrigPointerField', $GLOBALS['TCA'][$table]['ctrl'])) {
+                               $l10nParentRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $uid);
+                               $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['languageField']] = $l10nParentRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
+                               $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] = $l10nParentRec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']];
+                               unset($l10nParentRec);
+                       }
+                       // Initially, create at root level.
+                       $newVersion_placeholderFieldArray['pid'] = 0;
+                       $id = 'NEW_MOVE_PLH';
+                       // Saving placeholder as 'original'
+                       $tcemainObj->insertDB($table, $id, $newVersion_placeholderFieldArray, FALSE);
+                       // Move the new placeholder from temporary root-level to location:
+                       $tcemainObj->moveRecord_raw($table, $tcemainObj->substNEWwithIDs[$id], $destPid);
+                       // Move the workspace-version of the original to be the version of the move-to-placeholder:
+                       // Setting placeholder state value for version (so it can know it is currently a new version...)
+                       $updateFields = array(
+                               't3ver_state' => 4
+                       );
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($wsUid), $updateFields);
+               }
+               // Check for the localizations of that element and move them as well
+               $tcemainObj->moveL10nOverlayRecords($table, $uid, $destPid, $originalRecordDestinationPid);
+       }
+
+       /**
+        * Gets all possible child tables that are used on each parent table as field.
+        *
+        * @param string $parentTable Name of the parent table
+        * @param array $possibleInlineChildren Collected possible inline children
+        * @return array
+        */
+       protected function getPossibleInlineChildTablesOfParentTable($parentTable, array $possibleInlineChildren = array()) {
+               foreach ($GLOBALS['TCA'][$parentTable]['columns'] as $parentField => $parentFieldDefinition) {
+                       if (isset($parentFieldDefinition['config']['type'])) {
+                               $parentFieldConfiguration = $parentFieldDefinition['config'];
+                               if ($parentFieldConfiguration['type'] == 'inline' && isset($parentFieldConfiguration['foreign_table'])) {
+                                       if (!in_array($parentFieldConfiguration['foreign_table'], $possibleInlineChildren)) {
+                                               $possibleInlineChildren = $this->getPossibleInlineChildTablesOfParentTable($parentFieldConfiguration['foreign_table'], array_merge($possibleInlineChildren, $parentFieldConfiguration['foreign_table']));
+                                       }
+                               }
+                       }
+               }
+               return $possibleInlineChildren;
+       }
+
+       /**
+        * Gets an instance of the command map helper.
+        *
+        * @param \TYPO3\CMS\Core\DataHandling\DataHandler $tceMain TCEmain object
+        * @param array $commandMap The command map as submitted to \TYPO3\CMS\Core\DataHandling\DataHandler
+        * @return \TYPO3\CMS\Version\DataHandler\CommandMap
+        */
+       public function getCommandMap(\TYPO3\CMS\Core\DataHandling\DataHandler $tceMain, array $commandMap) {
+               return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\DataHandler\\CommandMap', $this, $tceMain, $commandMap);
+       }
+
+       /**
+        * Returns all fieldnames from a table which have the unique evaluation type set.
+        *
+        * @param string $table Table name
+        * @return array Array of fieldnames
+        */
+       protected function getUniqueFields($table) {
+               $listArr = array();
+               if ($GLOBALS['TCA'][$table]['columns']) {
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $configArr) {
+                               if ($configArr['config']['type'] === 'input') {
+                                       $evalCodesArray = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $configArr['config']['eval'], 1);
+                                       if (in_array('uniqueInPid', $evalCodesArray) || in_array('unique', $evalCodesArray)) {
+                                               $listArr[] = $field;
+                                       }
+                               }
+                       }
+               }
+               return $listArr;
+       }
+
+}
+
+
+?>
diff --git a/typo3/sysext/version/Classes/Hook/IconUtilityHook.php b/typo3/sysext/version/Classes/Hook/IconUtilityHook.php
new file mode 100644 (file)
index 0000000..ca781c8
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+namespace TYPO3\CMS\Version\Hook;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2011-2013 Francois Suter (francois.suter@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!
+ ***************************************************************/
+
+/**
+ * Implements a hook for \TYPO3\CMS\Backend\Utility\IconUtility
+ */
+class IconUtilityHook {
+
+       /**
+        * Visualizes the deleted status for a versionized record.
+        *
+        * @param string $table Name of the table
+        * @param array $row Record row containing the field values
+        * @param array $status Status to be used for rendering the icon
+        * @return void
+        */
+       public function overrideIconOverlay($table, array $row, array &$status) {
+               if (isset($row['t3ver_state']) && $row['t3ver_state'] == 2) {
+                       $status['deleted'] = TRUE;
+               }
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Hook/PreviewHook.php b/typo3/sysext/version/Classes/Hook/PreviewHook.php
new file mode 100644 (file)
index 0000000..1c68323
--- /dev/null
@@ -0,0 +1,260 @@
+<?php
+namespace TYPO3\CMS\Version\Hook;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2011-2013 TYPO3 Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
+ *  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!
+ ***************************************************************/
+/**
+ * Hook for checking if the preview mode is activated
+ * preview mode = show a page of a workspace without having to log in
+ *
+ * @author Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
+ */
+class PreviewHook implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * the GET parameter to be used
+        *
+        * @var string
+        */
+       protected $previewKey = 'ADMCMD_prev';
+
+       /**
+        * instance of the tslib_fe object
+        *
+        * @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
+        */
+       protected $tsfeObj;
+
+       /**
+        * preview configuration
+        *
+        * @var array
+        */
+       protected $previewConfiguration = FALSE;
+
+       /**
+        * hook to check if the preview is activated
+        * right now, this hook is called at the end of "$TSFE->connectToDB"
+        *
+        * @param $params (not needed right now)
+        * @param $pObj the instance of the tslib_fe object
+        * @return void
+        */
+       public function checkForPreview($params, &$pObj) {
+               $this->tsfeObj = $pObj;
+               $this->previewConfiguration = $this->getPreviewConfiguration();
+               if (is_array($this->previewConfiguration)) {
+                       // In case of a keyword-authenticated preview,
+                       // re-initialize the TSFE object:
+                       // because the GET variables are taken from the preview
+                       // configuration
+                       $GLOBALS['TSFE'] = ($this->tsfeObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController', $GLOBALS['TYPO3_CONF_VARS'], \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('id'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('type'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('no_cache'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('cHash'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('jumpurl'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('MP'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('RDCT')));
+                       // Configuration after initialization of TSFE object.
+                       // Basically this unsets the BE cookie if any and forces
+                       // the BE user set according to the preview configuration.
+                       // @previouslyknownas TSFE->ADMCMD_preview_postInit
+                       // Clear cookies:
+                       unset($_COOKIE['be_typo_user']);
+               }
+       }
+
+       /**
+        * hook after the regular BE user has been initialized
+        * if there is no BE user login, but a preview configuration
+        * the BE user of the preview configuration gets initialized
+        *
+        * @param $params holding the BE_USER object
+        * @param $pObj the instance of the tslib_fe object
+        * @return void
+        */
+       public function initializePreviewUser(&$params, &$pObj) {
+               if ((is_null($params['BE_USER']) || $params['BE_USER'] === FALSE) && $this->previewConfiguration !== FALSE && $this->previewConfiguration['BEUSER_uid'] > 0) {
+                       // New backend user object
+                       $BE_USER = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\FrontendBackendUserAuthentication');
+                       $BE_USER->userTS_dontGetCached = 1;
+                       $BE_USER->OS = TYPO3_OS;
+                       $BE_USER->setBeUserByUid($this->previewConfiguration['BEUSER_uid']);
+                       $BE_USER->unpack_uc('');
+                       if ($BE_USER->user['uid']) {
+                               $BE_USER->fetchGroupData();
+                               $pObj->beUserLogin = 1;
+                       } else {
+                               $BE_USER = NULL;
+                               $pObj->beUserLogin = 0;
+                               $_SESSION['TYPO3-TT-start'] = FALSE;
+                       }
+                       $params['BE_USER'] = $BE_USER;
+               }
+               // if there is a valid BE user, and the full workspace should be
+               // previewed, the workspacePreview option shouldbe set
+               $workspaceUid = $this->previewConfiguration['fullWorkspace'];
+               if ($pObj->beUserLogin && is_object($params['BE_USER']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($workspaceUid)) {
+                       if ($workspaceUid == 0 || $workspaceUid >= -1 && $params['BE_USER']->checkWorkspace($workspaceUid)) {
+                               // Check Access to workspace. Live (0) is OK to preview for all.
+                               $pObj->workspacePreview = intval($workspaceUid);
+                       } else {
+                               // No preview, will default to "Live" at the moment
+                               $pObj->workspacePreview = -99;
+                       }
+               }
+       }
+
+       /**
+        * Looking for a ADMCMD_prev code, looks it up if found and returns configuration data.
+        * Background: From the backend a request to the frontend to show a page, possibly with workspace preview can be "recorded" and associated with a keyword. When the frontend is requested with this keyword the associated request parameters are restored from the database AND the backend user is loaded - only for that request.
+        * The main point is that a special URL valid for a limited time, eg. http://localhost/typo3site/index.php?ADMCMD_prev=035d9bf938bd23cb657735f68a8cedbf will open up for a preview that doesn't require login. Thus it's useful for sending in an email to someone without backend account.
+        * This can also be used to generate previews of hidden pages, start/endtimes, usergroups and those other settings from the Admin Panel - just not implemented yet.
+        *
+        * @return      array           Preview configuration array from sys_preview record.
+        */
+       public function getPreviewConfiguration() {
+               $inputCode = $this->getPreviewInputCode();
+               // If inputcode is available, look up the settings
+               if ($inputCode) {
+                       // "log out"
+                       if ($inputCode == 'LOGOUT') {
+                               setcookie($this->previewKey, '', 0, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
+                               if ($this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']) {
+                                       $templateFile = PATH_site . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate'];
+                                       if (@is_file($templateFile)) {
+                                               $message = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl(PATH_site . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']);
+                                       } else {
+                                               $message = '<strong>ERROR!</strong><br>Template File "' . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate'] . '" configured with $TYPO3_CONF_VARS["FE"]["workspacePreviewLogoutTemplate"] not found. Please contact webmaster about this problem.';
+                                       }
+                               } else {
+                                       $message = 'You logged out from Workspace preview mode. Click this link to <a href="%1$s">go back to the website</a>';
+                               }
+                               $returnUrl = \TYPO3\CMS\Core\Utility\GeneralUtility::sanitizeLocalUrl(\TYPO3\CMS\Core\Utility\GeneralUtility::_GET('returnUrl'));
+                               die(sprintf($message, htmlspecialchars(preg_replace('/\\&?' . $this->previewKey . '=[[:alnum:]]+/', '', $returnUrl))));
+                       }
+                       // Look for keyword configuration record:
+                       $previewData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'sys_preview', 'keyword=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($inputCode, 'sys_preview') . ' AND endtime>' . $GLOBALS['EXEC_TIME']);
+                       // Get: Backend login status, Frontend login status
+                       // - Make sure to remove fe/be cookies (temporarily);
+                       // BE already done in ADMCMD_preview_postInit()
+                       if (is_array($previewData)) {
+                               if (!count(\TYPO3\CMS\Core\Utility\GeneralUtility::_POST())) {
+                                       // Unserialize configuration:
+                                       $previewConfig = unserialize($previewData['config']);
+                                       // For full workspace preview we only ADD a get variable
+                                       // to set the preview of the workspace - so all other Get
+                                       // vars are accepted. Hope this is not a security problem.
+                                       // Still posting is not allowed and even if a backend user
+                                       // get initialized it shouldn't lead to situations where
+                                       // users can use those credentials.
+                                       if ($previewConfig['fullWorkspace']) {
+                                               // Set the workspace preview value:
+                                               \TYPO3\CMS\Core\Utility\GeneralUtility::_GETset($previewConfig['fullWorkspace'], 'ADMCMD_previewWS');
+                                               // If ADMCMD_prev is set the $inputCode value cannot come
+                                               // from a cookie and we set that cookie here. Next time it will
+                                               // be found from the cookie if ADMCMD_prev is not set again...
+                                               if (\TYPO3\CMS\Core\Utility\GeneralUtility::_GP($this->previewKey)) {
+                                                       // Lifetime is 1 hour, does it matter much?
+                                                       // Requires the user to click the link from their email again if it expires.
+                                                       SetCookie($this->previewKey, \TYPO3\CMS\Core\Utility\GeneralUtility::_GP($this->previewKey), 0, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
+                                               }
+                                               return $previewConfig;
+                                       } elseif (\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' . $inputCode === \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
+                                               // Set GET variables
+                                               $GET_VARS = '';
+                                               parse_str($previewConfig['getVars'], $GET_VARS);
+                                               \TYPO3\CMS\Core\Utility\GeneralUtility::_GETset($GET_VARS);
+                                               // Return preview keyword configuration
+                                               return $previewConfig;
+                                       } else {
+                                               // This check is to prevent people from setting additional
+                                               // GET vars via realurl or other URL path based ways of passing parameters.
+                                               throw new \Exception(htmlspecialchars('Request URL did not match "' . \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' . $inputCode . '"', 1294585190));
+                                       }
+                               } else {
+                                       throw new \Exception('POST requests are incompatible with keyword preview.', 1294585191);
+                               }
+                       } else {
+                               throw new \Exception('ADMCMD command could not be executed! (No keyword configuration found)', 1294585192);
+                       }
+               }
+               return FALSE;
+       }
+
+       /**
+        * returns the input code value from the admin command variable
+        *
+        * @return input code
+        */
+       protected function getPreviewInputCode() {
+               $inputCode = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP($this->previewKey);
+               // If no inputcode and a cookie is set, load input code from cookie:
+               if (!$inputCode && $_COOKIE[$this->previewKey]) {
+                       $inputCode = $_COOKIE[$this->previewKey];
+               }
+               return $inputCode;
+       }
+
+       /**
+        * Set preview keyword, eg:
+        * $previewUrl = \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.$this->compilePreviewKeyword('id='.$pageId.'&L='.$language.'&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS='.$this->workspace, $GLOBALS['BE_USER']->user['uid'], 120);
+        *
+        * todo for sys_preview:
+        * - Add a comment which can be shown to previewer in frontend in some way (plus maybe ability to write back, take other action?)
+        * - Add possibility for the preview keyword to work in the backend as well: So it becomes a quick way to a certain action of sorts?
+        *
+        * @param       string          Get variables to preview, eg. 'id=1150&L=0&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS=8'
+        * @param       string          32 byte MD5 hash keyword for the URL: "?ADMCMD_prev=[keyword]
+        * @param       integer         Time-To-Live for keyword
+        * @param       integer         Which workspace to preview. Workspace UID, -1 or >0. If set, the getVars is ignored in the frontend, so that string can be empty
+        * @return      string          Returns keyword to use in URL for ADMCMD_prev=
+        */
+       public function compilePreviewKeyword($getVarsStr, $backendUserUid, $ttl = 172800, $fullWorkspace = NULL) {
+               $fieldData = array(
+                       'keyword' => md5(uniqid(microtime())),
+                       'tstamp' => $GLOBALS['EXEC_TIME'],
+                       'endtime' => $GLOBALS['EXEC_TIME'] + $ttl,
+                       'config' => serialize(array(
+                               'fullWorkspace' => $fullWorkspace,
+                               'getVars' => $getVarsStr,
+                               'BEUSER_uid' => $backendUserUid
+                       ))
+               );
+               $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_preview', $fieldData);
+               return $fieldData['keyword'];
+       }
+
+       /**
+        * easy function to just return the number of hours
+        * a preview link is valid, based on the TSconfig value "options.workspaces.previewLinkTTLHours"
+        * by default, it's 48hs
+        *
+        * @return integer      the hours as a number
+        */
+       public function getPreviewLinkLifetime() {
+               $ttlHours = intval($GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.previewLinkTTLHours'));
+               return $ttlHours ? $ttlHours : 24 * 2;
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Preview.php b/typo3/sysext/version/Classes/Preview.php
new file mode 100644 (file)
index 0000000..166fb80
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+/*
+ * @deprecated since 6.0, the classname Tx_Version_Preview and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/Hook/PreviewHook.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/Hook/PreviewHook.php';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Task/AutoPublishTask.php b/typo3/sysext/version/Classes/Task/AutoPublishTask.php
new file mode 100644 (file)
index 0000000..bff4c93
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+namespace TYPO3\CMS\Version\Task;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010-2013 François Suter <francois@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.
+ *
+ *  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!
+ ***************************************************************/
+/**
+ * This class provides a wrapper around the autopublication
+ * mechanism of workspaces, as a Scheduler task
+ *
+ * @author             François Suter <francois@typo3.org>
+ */
+class AutoPublishTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask {
+
+       /**
+        * Method executed from the Scheduler.
+        * Call on the workspace logic to publish workspaces whose publication date
+        * is in the past
+        *
+        * @return      void
+        */
+       public function execute() {
+               // Load the workspace library class and instatiate it
+               require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'ws/class.wslib.php';
+               $autopubObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Utility\\WorkspacesUtility');
+               // Publish the workspaces that need to be
+               $autopubObj->autoPublishWorkspaces();
+               // There's no feedback from the publishing process,
+               // so there can't be any failure.
+               // TODO: This could certainly be improved.
+               return TRUE;
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/Utility/WorkspacesUtility.php b/typo3/sysext/version/Classes/Utility/WorkspacesUtility.php
new file mode 100644 (file)
index 0000000..5ee1b99
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+namespace TYPO3\CMS\Version\Utility;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2005-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
+ *  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!
+ ***************************************************************/
+/**
+ * Library with Workspace related functionality
+ *
+ * @author     Kasper Skårhøj <kasperYYYY@typo3.com>
+ */
+class WorkspacesUtility {
+
+       /**
+        * Building tcemain CMD-array for swapping all versions in a workspace.
+        *
+        * @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       [type]          $pageId: ...
+        * @return      array           Command array for tcemain
+        * @todo Define visibility
+        */
+       public function getCmdArrayForPublishWS($wsid, $doSwap, $pageId = 0) {
+               $wsid = intval($wsid);
+               $cmd = array();
+               if ($wsid >= -1 && $wsid !== 0) {
+                       // Define stage to select:
+                       $stage = -99;
+                       if ($wsid > 0) {
+                               $workspaceRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('sys_workspace', $wsid);
+                               if ($workspaceRec['publish_access'] & 1) {
+                                       $stage = 10;
+                               }
+                       }
+                       // Select all versions to swap:
+                       $versions = $this->selectVersionsInWorkspace($wsid, 0, $stage, $pageId ? $pageId : -1);
+                       // Traverse the selection to build CMD array:
+                       foreach ($versions as $table => $records) {
+                               foreach ($records as $rec) {
+                                       // Build the cmd Array:
+                                       $cmd[$table][$rec['t3ver_oid']]['version'] = array(
+                                               'action' => 'swap',
+                                               'swapWith' => $rec['uid'],
+                                               'swapIntoWS' => $doSwap ? 1 : 0
+                                       );
+                               }
+                       }
+               }
+               return $cmd;
+       }
+
+       /**
+        * Select all records from workspace pending for publishing
+        * Used from backend to display workspace overview
+        * User for auto-publishing for selecting versions for publication
+        *
+        * @param       integer         Workspace ID. If -99, will select ALL versions from ANY workspace. If -98 will select all but ONLINE. >=-1 will select from the actual workspace
+        * @param       integer         Lifecycle filter: 1 = select all drafts (never-published), 2 = select all published one or more times (archive/multiple), anything else selects all.
+        * @param       integer         Stage filter: -99 means no filtering, otherwise it will be used to select only elements with that stage. For publishing, that would be "10
+        * @param       integer         Page id: Live page for which to find versions in workspace!
+        * @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_oid fields. The REAL pid of the online record is found as "realpid
+        * @todo Define visibility
+        */
+       public function selectVersionsInWorkspace($wsid, $filter = 0, $stage = -99, $pageId = -1) {
+               $wsid = intval($wsid);
+               $filter = intval($filter);
+               $output = array();
+               // Traversing all tables supporting versioning:
+               foreach ($GLOBALS['TCA'] as $table => $cfg) {
+                       if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
+                               // Select all records from this table in the database from the workspace
+                               // This joins the online version with the offline version as tables A and B
+                               $recs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('A.uid, A.t3ver_oid, B.pid AS realpid', $table . ' A,' . $table . ' B', 'A.pid=-1' . ($pageId != -1 ? ($table === 'pages' ? ' AND B.uid=' . intval($pageId) : ' AND B.pid=' . intval($pageId)) : '') . ($wsid > -98 ? ' AND A.t3ver_wsid=' . $wsid : ($wsid === -98 ? ' AND A.t3ver_wsid!=0' : '')) . ($filter === 1 ? ' AND A.t3ver_count=0' : ($filter === 2 ? ' AND A.t3ver_count>0' : '')) . ($stage != -99 ? ' AND A.t3ver_stage=' . intval($stage) : '') . ' AND B.pid>=0' . ' AND A.t3ver_oid=B.uid' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'A') . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause($table, 'B'), '', 'B.uid');
+                               if (count($recs)) {
+                                       $output[$table] = $recs;
+                               }
+                       }
+               }
+               return $output;
+       }
+
+       /****************************
+        *
+        * Scheduler methods
+        *
+        ****************************/
+       /**
+        * This method is called by the Scheduler task that triggers
+        * the autopublication process
+        * It searches for workspaces whose publication date is in the past
+        * and publishes them
+        *
+        * @return      void
+        * @todo Define visibility
+        */
+       public function autoPublishWorkspaces() {
+               // Temporarily set admin rights
+               // FIXME: once workspaces are cleaned up a better solution should be implemented
+               $currentAdminStatus = $GLOBALS['BE_USER']->user['admin'];
+               $GLOBALS['BE_USER']->user['admin'] = 1;
+               // Select all workspaces that needs to be published / unpublished:
+               $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,swap_modes,publish_time,unpublish_time', 'sys_workspace', 'pid=0
+                               AND
+                               ((publish_time!=0 AND publish_time<=' . intval($GLOBALS['EXEC_TIME']) . ')
+                               OR (publish_time=0 AND unpublish_time!=0 AND unpublish_time<=' . intval($GLOBALS['EXEC_TIME']) . '))' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('sys_workspace'));
+               foreach ($workspaces as $rec) {
+                       // First, clear start/end time so it doesn't get select once again:
+                       $fieldArray = $rec['publish_time'] != 0 ? array('publish_time' => 0) : array('unpublish_time' => 0);
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_workspace', 'uid=' . intval($rec['uid']), $fieldArray);
+                       // Get CMD array:
+                       $cmd = $this->getCmdArrayForPublishWS($rec['uid'], $rec['swap_modes'] == 1);
+                       // $rec['swap_modes']==1 means that auto-publishing will swap versions, not just publish and empty the workspace.
+                       // Execute CMD array:
+                       $tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
+                       $tce->stripslashes_values = 0;
+                       $tce->start(array(), $cmd);
+                       $tce->process_cmdmap();
+               }
+               // Restore admin status
+               $GLOBALS['BE_USER']->user['admin'] = $currentAdminStatus;
+       }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Classes/View/VersionView.php b/typo3/sysext/version/Classes/View/VersionView.php
new file mode 100644 (file)
index 0000000..9aec36f
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+namespace TYPO3\CMS\Version\View;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
+ *  (c) 2010-2013 Benjamin Mack (benni@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!
+ ***************************************************************/
+/**
+ * Contains some parts for staging, versioning and workspaces
+ * to interact with the TYPO3 Core Engine
+ */
+class VersionView {
+
+       /**
+        * Creates the version selector for the page id inputted.
+        * Moved out of the core file typo3/template.php
+        *
+        * @param       integer         Page id to create selector for.
+        * @param       boolean         If set, there will be no button for swapping page.
+        * @return      void
+        */
+       public function getVersionSelector($id, $noAction = FALSE) {
+               if ($id <= 0) {
+                       return;
+               }
+               if ($GLOBALS['BE_USER']->workspace == 0) {
+                       // Get Current page record:
+                       $curPage = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $id);
+                       // If the selected page is not online, find the right ID
+                       $onlineId = $curPage['pid'] == -1 ? $curPage['t3ver_oid'] : $id;
+                       // Select all versions of online version:
+                       $versions = \TYPO3\CMS\Backend\Utility\BackendUtility::selectVersionsOfRecord('pages', $onlineId, 'uid,pid,t3ver_label,t3ver_oid,t3ver_wsid,t3ver_id');
+                       // If more than one was found...:
+                       if (count($versions) > 1) {
+                               $selectorLabel = '<strong>' . $GLOBALS['LANG']->sL('LLL:EXT:version/locallang.xml:versionSelect.label', TRUE) . '</strong>';
+                               // Create selector box entries:
+                               $opt = array();
+                               foreach ($versions as $vRow) {
+                                       if ($vRow['uid'] == $onlineId) {
+                                               // Live version
+                                               $label = '[' . $GLOBALS['LANG']->sL('LLL:EXT:version/locallang.xml:versionSelect.live', TRUE) . ']';
+                                       } else {
+                                               $label = $vRow['t3ver_label'] . ' (' . $GLOBALS['LANG']->sL('LLL:EXT:version/locallang.xml:versionId', TRUE) . ' ' . $vRow['t3ver_id'] . ($vRow['t3ver_wsid'] != 0 ? ' ' . $GLOBALS['LANG']->sL('LLL:EXT:version/locallang.xml:workspaceId', TRUE) . ' ' . $vRow['t3ver_wsid'] : '') . ')';
+                                       }
+                                       $opt[] = '<option value="' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript(array('id' => $vRow['uid']))) . '"' . ($id == $vRow['uid'] ? ' selected="selected"' : '') . '>' . htmlspecialchars($label) . '</option>';
+                               }
+                               // Add management link:
+                               $management = '<input type="button" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:ver.mgm', TRUE) . '" onclick="window.location.href=\'' . htmlspecialchars(($GLOBALS['BACK_PATH'] . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('version') . 'cm1/index.php?table=pages&uid=' . $onlineId)) . '\';" />';
+                               // Create onchange handler:
+                               $onChange = 'window.location.href=this.options[this.selectedIndex].value;';
+                               // Controls:
+                               if ($id == $onlineId) {
+                                       $controls .= '<img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($GLOBALS['BACK_PATH'], 'gfx/blinkarrow_left.gif', 'width="5" height="9"') . ' class="absmiddle" alt="" /> <strong>' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:ver.online', TRUE) . '</strong>';
+                               } elseif (!$noAction) {
+                                       $controls .= '<a href="' . $GLOBALS['TBE_TEMPLATE']->issueCommand(('&cmd[pages][' . $onlineId . '][version][swapWith]=' . $id . '&cmd[pages][' . $onlineId . '][version][action]=swap'), \TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript(array('id' => $onlineId))) . '" class="nobr">' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-version-swap-version', array(
+                                               'title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:ver.swapPage', TRUE),
+                                               'style' => 'margin-left:5px;vertical-align:bottom;'
+                                       )) . '<strong>' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:ver.swap', TRUE) . '</strong></a>';
+                               }
+                               // Write out HTML code:
+                               return '
+                                       <!--
+                                               Version selector:
+                                       -->
+                                       <table border="0" cellpadding="0" cellspacing="0" id="typo3-versionSelector">
+                                               <tr>
+                                                       <td>' . $selectorLabel . '</td>
+                                                       <td>
+                                                               <select onchange="' . htmlspecialchars($onChange) . '">
+                                                                       ' . implode('', $opt) . '
+                                                               </select></td>
+                                                       <td>' . $controls . '</td>
+                                                       <td>' . $management . '</td>
+                                               </tr>
+                                       </table>
+                               ';
+                       }
+               }
+       }
+}
+
+?>
diff --git a/typo3/sysext/version/Migrations/Code/ClassAliasMap.php b/typo3/sysext/version/Migrations/Code/ClassAliasMap.php
new file mode 100644 (file)
index 0000000..02d7d4b
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+return array(
+       'tx_version_cm1' => 'TYPO3\\CMS\\Version\\Controller\\VersionModuleController',
+       'tx_version_tcemain_CommandMap' => 'TYPO3\\CMS\\Version\\DataHandler\\CommandMap',
+       't3lib_utility_Dependency_Factory' => 'TYPO3\\CMS\\Version\\Dependency\\DependencyEntityFactory',
+       't3lib_utility_Dependency' => 'TYPO3\\CMS\\Version\\Dependency\\DependencyResolver',
+       't3lib_utility_Dependency_Element' => 'TYPO3\\CMS\\Version\\Dependency\\ElementEntity',
+       't3lib_utility_Dependency_Callback' => 'TYPO3\\CMS\\Version\\Dependency\\EventCallback',
+       't3lib_utility_Dependency_Reference' => 'TYPO3\\CMS\\Version\\Dependency\\ReferenceEntity',
+       'tx_version_tcemain' => 'TYPO3\\CMS\\Version\\Hook\\DataHandlerHook',
+       'tx_version_iconworks' => 'TYPO3\\CMS\\Version\\Hook\\IconUtilityHook',
+       'Tx_Version_Preview' => 'TYPO3\\CMS\\Version\\Hook\\PreviewHook',
+       'tx_version_tasks_AutoPublish' => 'TYPO3\\CMS\\Version\\Task\\AutoPublishTask',
+       'wslib' => 'TYPO3\\CMS\\Version\\Utility\\WorkspacesUtility',
+       'tx_version_gui' => 'TYPO3\\CMS\\Version\\View\\VersionView',
+);
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/Resources/Private/Language/locallang_emails.xlf b/typo3/sysext/version/Resources/Private/Language/locallang_emails.xlf
new file mode 100644 (file)
index 0000000..f23105d
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xliff version="1.0">
+       <file source-language="en" datatype="plaintext" original="messages" date="2011-10-17T20:22:37Z" product-name="version">
+               <header/>
+               <body>
+                       <trans-unit id="subject" xml:space="preserve">
+                               <source>TYPO3 Workspace Note: Stage Change for ###ELEMENT_NAME###</source>
+                       </trans-unit>
+                       <trans-unit id="message" xml:space="preserve">
+                               <source>At the TYPO3 site "###SITE_NAME###" (###SITE_URL###)
+in workspace "###WORKSPACE_TITLE###" (###WORKSPACE_UID###)
+the stage has changed for the element(s) "###RECORD_TITLE###" (###ELEMENT_NAME###) at location "###RECORD_PATH###" in the page tree:
+
+=&gt; ###NEXT_STAGE###
+
+User Comment:
+"###COMMENT###"
+
+State was changed by ###USER_FULLNAME### (username: ###USER_USERNAME###)</source>
+                       </trans-unit>
+               </body>
+       </file>
+</xliff>
diff --git a/typo3/sysext/version/class.tx_version_cm1.php b/typo3/sysext/version/class.tx_version_cm1.php
new file mode 100755 (executable)
index 0000000..c0705d4
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+/*
+ * @deprecated since 6.0, the classname tx_version_cm1 and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/ClickMenu/VersionClickMenu.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/ClickMenu/VersionClickMenu.php';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/class.tx_version_gui.php b/typo3/sysext/version/class.tx_version_gui.php
new file mode 100644 (file)
index 0000000..3364a54
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+/*
+ * @deprecated since 6.0, the classname tx_version_gui and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/View/VersionView.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/View/VersionView.php';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/class.tx_version_iconworks.php b/typo3/sysext/version/class.tx_version_iconworks.php
new file mode 100644 (file)
index 0000000..4935a51
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+/*
+ * @deprecated since 6.0, the classname tx_version_iconworks and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/Hook/IconUtilityHook.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/Hook/IconUtilityHook.php';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/class.tx_version_tcemain.php b/typo3/sysext/version/class.tx_version_tcemain.php
new file mode 100644 (file)
index 0000000..bf89aaa
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+/*
+ * @deprecated since 6.0, the classname tx_version_tcemain and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/Hook/DataHandlerHook.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/Hook/DataHandlerHook.php';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/class.tx_version_tcemain_commandmap.php b/typo3/sysext/version/class.tx_version_tcemain_commandmap.php
new file mode 100644 (file)
index 0000000..6f0b544
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+/*
+ * @deprecated since 6.0, the classname tx_version_tcemain_CommandMap and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/DataHandler/CommandMap.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/DataHandler/CommandMap.php';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/cm1/clear.gif b/typo3/sysext/version/cm1/clear.gif
new file mode 100755 (executable)
index 0000000..9ed1269
Binary files /dev/null and b/typo3/sysext/version/cm1/clear.gif differ
diff --git a/typo3/sysext/version/cm1/cm_icon.gif b/typo3/sysext/version/cm1/cm_icon.gif
new file mode 100755 (executable)
index 0000000..f253bca
Binary files /dev/null and b/typo3/sysext/version/cm1/cm_icon.gif differ
diff --git a/typo3/sysext/version/cm1/conf.php b/typo3/sysext/version/cm1/conf.php
new file mode 100755 (executable)
index 0000000..9d47ed9
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+// DO NOT REMOVE OR CHANGE THESE 3 LINES:
+define('TYPO3_MOD_PATH', 'sysext/version/cm1/');
+$BACK_PATH = '../../../';
+$MCONF['name'] = 'web_txversionM1';
+$MLANG['default']['tabs_images']['tab'] = 'cm_icon.gif';
+$MLANG['default']['ll_ref'] = 'LLL:EXT:version/locallang.xml';
+$MCONF['script'] = 'index.php';
+$MCONF['access'] = 'user,group';
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/cm1/index.php b/typo3/sysext/version/cm1/index.php
new file mode 100755 (executable)
index 0000000..433981e
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2004-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
+ *  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.
+ *
+ *  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!
+ ***************************************************************/
+// DEFAULT initialization of a module [BEGIN]
+unset($MCONF);
+require 'conf.php';
+require $BACK_PATH . 'init.php';
+require $BACK_PATH . 'template.php';
+$GLOBALS['LANG']->includeLLFile('EXT:version/locallang.xml');
+// DEFAULT initialization of a module [END]
+require_once '../ws/class.wslib.php';
+/*
+ * @deprecated since 6.0, the classname tx_version_cm1 and this file is obsolete
+ * and will be removed with 6.2. The class was renamed and is now located at:
+ * typo3/sysext/version/Classes/Controller/VersionModuleController.php
+ */
+require_once \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version') . 'Classes/Controller/VersionModuleController.php';
+// Make instance:
+$SOBE = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Version\\Controller\\VersionModuleController');
+$SOBE->init();
+$SOBE->main();
+$SOBE->printContent();
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/ext_autoload.php b/typo3/sysext/version/ext_autoload.php
new file mode 100644 (file)
index 0000000..acc15cc
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+// DO NOT CHANGE THIS FILE! It is automatically generated by extdeveval::buildAutoloadRegistry.
+// This file was generated on 2010-11-13 17:45
+$extensionPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version');
+return array(
+       'tx_version_tcemain_commandmap' => $extensionPath . 'class.tx_version_tcemain_commandmap.php',
+       'tx_version_cm1' => $extensionPath . 'cm1/index.php',
+       'tx_version_gui' => $extensionPath . 'class.tx_version_gui.php',
+       'tx_version_tcemain' => $extensionPath . 'class.tx_version_tcemain.php',
+       'tx_version_tasks_autopublish' => $extensionPath . 'tasks/class.tx_version_tasks_autopublish.php',
+       'tx_version_preview' => $extensionPath . 'Classes/Preview.php'
+);
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/ext_emconf.php b/typo3/sysext/version/ext_emconf.php
new file mode 100644 (file)
index 0000000..56394f3
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/***************************************************************
+ * Extension Manager/Repository config file for ext "version".
+ *
+ * Auto generated 13-03-2012 16:03
+ *
+ * Manual updates:
+ * Only the data in the array - everything else is removed by next
+ * writing. "version" and "dependencies" must not be touched!
+ ***************************************************************/
+$EM_CONF[$_EXTKEY] = array(
+       'title' => 'Versioning Management',
+       'description' => 'Backend Interface for management of the versioning API.',
+       'category' => 'be',
+       'author' => 'Kasper Skaarhoj',
+       'author_email' => 'kasperYYYY@typo3.com',
+       'shy' => '',
+       'dependencies' => '',
+       'conflicts' => '',
+       'priority' => '',
+       'module' => 'cm1',
+       'doNotLoadInFE' => 0,
+       'state' => 'stable',
+       'internal' => '',
+       'uploadfolder' => 0,
+       'createDirs' => '',
+       'modify_tables' => '',
+       'clearCacheOnLoad' => 0,
+       'lockType' => '',
+       'author_company' => '',
+       'version' => '6.1.0',
+       '_md5_values_when_last_written' => '',
+       'constraints' => array(
+               'depends' => array(
+                       'typo3' => '6.1.0-6.1.99',
+               ),
+               'conflicts' => array(),
+               'suggests' => array()
+       ),
+       'suggests' => array()
+);
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/ext_icon.gif b/typo3/sysext/version/ext_icon.gif
new file mode 100755 (executable)
index 0000000..f253bca
Binary files /dev/null and b/typo3/sysext/version/ext_icon.gif differ
diff --git a/typo3/sysext/version/ext_localconf.php b/typo3/sysext/version/ext_localconf.php
new file mode 100644 (file)
index 0000000..332e6b6
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+/* $Id: ext_localconf.php 7251 2010-04-06 18:57:45Z francois $ */
+if (!defined('TYPO3_MODE')) {
+       die('Access denied.');
+}
+// register the hook to actually do the work within TCEmain
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['version'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version', 'class.tx_version_tcemain.php:&TYPO3\\CMS\\Version\\Hook\\DataHandlerHook');
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass']['version'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version', 'class.tx_version_tcemain.php:&TYPO3\\CMS\\Version\\Hook\\DataHandlerHook');
+// Register hook for overriding the icon status overlay
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_iconworks.php']['overrideIconOverlay']['version'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('version', 'class.tx_version_iconworks.php:&TYPO3\\CMS\\Version\\Hook\\IconUtilityHook');
+// Register hook to check for the preview mode in the FE
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB']['version_preview'] = 'EXT:version/Classes/Preview.php:TYPO3\\CMS\\Version\\Hook\\PreviewHook->checkForPreview';
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser']['version_preview'] = 'EXT:version/Classes/Preview.php:TYPO3\\CMS\\Version\\Hook\\PreviewHook->initializePreviewUser';
+if (TYPO3_MODE == 'BE') {
+       // add default notification options to every page
+       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSconfig('
+       tx_version.workspaces.stageNotificationEmail.subject = LLL:EXT:version/Resources/Private/Language/locallang_emails.xml:subject
+       tx_version.workspaces.stageNotificationEmail.message = LLL:EXT:version/Resources/Private/Language/locallang_emails.xml:message
+       # tx_version.workspaces.stageNotificationEmail.additionalHeaders =
+');
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/ext_tables.php b/typo3/sysext/version/ext_tables.php
new file mode 100644 (file)
index 0000000..25d9ee6
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+if (!defined('TYPO3_MODE')) {
+       die('Access denied.');
+}
+if (TYPO3_MODE == 'BE') {
+       if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
+               $GLOBALS['TBE_MODULES_EXT']['xMOD_alt_clickmenu']['extendCMclasses'][] = array(
+                       'name' => 'TYPO3\\CMS\\Version\\ClickMenu\\VersionClickMenu',
+               );
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/version/ext_tables.sql b/typo3/sysext/version/ext_tables.sql
new file mode 100644 (file)
index 0000000..e1c3a47
--- /dev/null
@@ -0,0 +1,11 @@
+
+#
+# Table structure for table 'sys_preview'
+#
+CREATE TABLE sys_preview (
+  keyword varchar(32) DEFAULT '' NOT NULL,
+  tstamp int(11) DEFAULT '0' NOT NULL,
+  endtime int(11) DEFAULT '0' NOT NULL,
+  config text,
+  PRIMARY KEY (keyword)
+);
diff --git a/typo3/sysext/version/locallang.xlf b/typo3/sysext/version/locallang.xlf
new file mode 100644 (file)
index 0000000..cbb8982
--- /dev/null
@@ -0,0 +1,374 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xliff version="1.0">
+       <file source-language="en" datatype="plaintext" original="messages" date="2011-10-17T20:22:37Z" product-name="version">
+               <header/>
+               <body>
+                       <trans-unit id="title" xml:space="preserve">
+                               <source>Versioning</source>
+                       </trans-unit>
+                       <trans-unit id="title_review" xml:space="preserve">
+                               <source>Send to review/pub</source>
+                       </trans-unit>
+                       <trans-unit id="mlang_labels_tablabel" xml:space="preserve">
+                               <source>Page and content versioning and workspace management for pages.</source>
+                       </trans-unit>
+                       <trans-unit id="mlang_labels_tabdescr" xml:space="preserve">
+                               <source>The Web&gt;Versioning module provides access to versioning features for the page tree.</source>
+                       </trans-unit>
+                       <trans-unit id="mlang_tabs_tab" xml:space="preserve">
+                               <source>Versioning</source>
+                       </trans-unit>
+                       <trans-unit id="clickAPage_header" xml:space="preserve">
+                               <source>Web&gt;Versioning module</source>
+                       </trans-unit>
+                       <trans-unit id="clickAPage_content" xml:space="preserve">
+                               <source>Please click a page in the pagetree frame to see the versioning module.</source>
+                       </trans-unit>
+                       <trans-unit id="filter_drafts" xml:space="preserve">
+                               <source>Drafts</source>
+                       </trans-unit>
+                       <trans-unit id="filter_archive" xml:space="preserve">
+                               <source>Archive</source>
+                       </trans-unit>
+                       <trans-unit id="filter_all" xml:space="preserve">
+                               <source>All</source>
+                       </trans-unit>
+                       <trans-unit id="diffing" xml:space="preserve">
+                               <source>DIFFING</source>
+                       </trans-unit>
+                       <trans-unit id="fieldname" xml:space="preserve">
+                               <source>Fieldname</source>
+                       </trans-unit>
+                       <trans-unit id="coloredDiffView" xml:space="preserve">
+                               <source>Colored diff-view</source>
+                       </trans-unit>
+                       <trans-unit id="recordsMatchesCompletely" xml:space="preserve">
+                               <source>Records matches completely on all editable fields!</source>
+                       </trans-unit>
+                       <trans-unit id="errorRecordsNotFound" xml:space="preserve">
+                               <source>ERROR: Records could strangely not be found!</source>
+                       </trans-unit>
+                       <trans-unit id="errorDiffSources" xml:space="preserve">
+                               <source>ERROR: You didn't select two sources for diffing!</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_title" xml:space="preserve">
+                               <source>Title</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_uid" xml:space="preserve">
+                               <source>UID</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_oid" xml:space="preserve">
+                               <source>oid</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_id" xml:space="preserve">
+                               <source>id</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_wsid" xml:space="preserve">
+                               <source>wsid</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_state" xml:space="preserve">
+                               <source>state</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_stage" xml:space="preserve">
+                               <source>stage</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_count" xml:space="preserve">
+                               <source>count</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_pid" xml:space="preserve">
+                               <source>pid</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeader_t3ver_label" xml:space="preserve">
+                               <source>Label</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_title" xml:space="preserve">
+                               <source>Header of element</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_uid" xml:space="preserve">
+                               <source>Unique ID of element</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_oid" xml:space="preserve">
+                               <source>t3ver_oid - Reference to live version UID</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_id" xml:space="preserve">
+                               <source>t3ver_id - Version number, incremental integer</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_wsid" xml:space="preserve">
+                               <source>t3ver_wsid - Workspace ID. There can be only one version of an element per ID (except ID zero).</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_state" xml:space="preserve">
+                               <source>t3ver_state - Special states of a version: 1=Placeholder for "New". 2=Marked deleted.</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_stage" xml:space="preserve">
+                               <source>t3ver_stage - Publishing stage: Editing (0), review (1), publish (10), rejected (-1).</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_count" xml:space="preserve">
+                               <source>t3ver_count - Life cycle counter. Incremented each time element is unpublished.</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_pid" xml:space="preserve">
+                               <source>Page ID</source>
+                       </trans-unit>
+                       <trans-unit id="tblHeaderDesc_t3ver_label" xml:space="preserve">
+                               <source>Label</source>
+                       </trans-unit>
+                       <trans-unit id="diff" xml:space="preserve">
+                               <source>Diff</source>
+                       </trans-unit>
+                       <trans-unit id="swapWithCurrent" xml:space="preserve">
+                               <source>SWAP with current</source>
+                       </trans-unit>
+                       <trans-unit id="currentOnlineVersion" xml:space="preserve">
+                               <source>CURRENT ONLINE VERSION!</source>
+                       </trans-unit>
+                       <trans-unit id="cmdPid0" xml:space="preserve">
+                               <source>Page: Page + content</source>
+                       </trans-unit>
+                       <trans-unit id="cmdPid100" xml:space="preserve">
+                               <source>Branch: All subpages</source>
+                       </trans-unit>
+                       <trans-unit id="cmdPid1" xml:space="preserve">
+                               <source>Element: Just record</source>
+                       </trans-unit>
+                       <trans-unit id="createNewVersion" xml:space="preserve">
+                               <source>Create new version</source>
+                       </trans-unit>
+                       <trans-unit id="previewLink" xml:space="preserve">
+                               <source>Preview Link</source>
+                       </trans-unit>
+                       <trans-unit id="publishPage" xml:space="preserve">
+                               <source>Publish Page</source>
+                       </trans-unit>
+                       <trans-unit id="swapPage" xml:space="preserve">
+                               <source>Swap page</source>
+                       </trans-unit>
+                       <trans-unit id="publishPageQuestion" xml:space="preserve">
+                               <source>Are you sure you want to publish all content %sfrom this page?</source>
+                       </trans-unit>
+                       <trans-unit id="swapPageQuestion" xml:space="preserve">
+                               <source>Are you sure you want to publish (swap) all content %sfrom this page?</source>
+                       </trans-unit>
+                       <trans-unit id="publishPageQuestionStage" xml:space="preserve">
+                               <source>in "Publish" stage </source>
+                       </trans-unit>
+                       <trans-unit id="previewUrl" xml:space="preserve">
+                               <source>Preview Url:</source>
+                       </trans-unit>
+                       <trans-unit id="previewInstruction" xml:space="preserve">
+                               <source>You can preview this page from the workspace using this link for the next %s hours (does not require backend login):</source>
+                       </trans-unit>
+                       <trans-unit id="versionDetails" xml:space="preserve">
+                               <source>Details for version</source>
+                       </trans-unit>
+                       <trans-unit id="wsManagement" xml:space="preserve">
+                               <source>Workspace management</source>
+                       </trans-unit>
+                       <trans-unit id="showDiffView" xml:space="preserve">
+                               <source>Show difference view</source>
+                       </trans-unit>
+                       <trans-unit id="liveVersion" xml:space="preserve">
+                               <source>Live Version:</source>
+                       </trans-unit>
+                       <trans-unit id="wsVersions" xml:space="preserve">
+                               <source>Workspace Versions:</source>
+                       </trans-unit>
+                       <trans-unit id="controls" xml:space="preserve">
+                               <source>Controls:</source>
+                       </trans-unit>
+                       <trans-unit id="goBack" xml:space="preserve">
+                               <source>Click here to go back</source>
+                       </trans-unit>
+                       <trans-unit id="showAllInformation" xml:space="preserve">
+                               <source>Show all information</source>
+                       </trans-unit>
+                       <trans-unit id="newElement" xml:space="preserve">
+                               <source>New element</source>
+                       </trans-unit>
+                       <trans-unit id="deletedElement" xml:space="preserve">
+                               <source>Deleted element</source>
+                       </trans-unit>
+                       <trans-unit id="moveToPlaceholder" xml:space="preserve">
+                               <source>Move-to placeholder (destination)</source>
+                       </trans-unit>
+                       <trans-unit id="moveToPointer" xml:space="preserve">
+                               <source>Move-to pointer (source)</source>
+                       </trans-unit>
+                       <trans-unit id="notAvailable" xml:space="preserve">
+                               <source>N/A</source>
+                       </trans-unit>
+                       <trans-unit id="change" xml:space="preserve">
+                               <source>change:</source>
+                       </trans-unit>
+                       <trans-unit id="element" xml:space="preserve">
+                               <source> [Element]</source>
+                       </trans-unit>
+                       <trans-unit id="page" xml:space="preserve">
+                               <source> [Page]</source>
+                       </trans-unit>
+                       <trans-unit id="branch" xml:space="preserve">
+                               <source> [Branch]</source>
+                       </trans-unit>
+                       <trans-unit id="otherVersions" xml:space="preserve">
+                               <source>Other version(s) in workspace </source>
+                       </trans-unit>
+                       <trans-unit id="multipleVersions" xml:space="preserve">
+                               <source>Multiple versions in same workspace!</source>
+                       </trans-unit>
+                       <trans-unit id="versionInVersion" xml:space="preserve">
+                               <source>Version inside version!</source>
+                       </trans-unit>
+                       <trans-unit id="lifecycle" xml:space="preserve">
+                               <source>Lifecycle</source>
+                       </trans-unit>
+                       <trans-unit id="workspace" xml:space="preserve">
+                               <source>Workspace</source>
+                       </trans-unit>
+                       <trans-unit id="diffToLiveElement" xml:space="preserve">
+                               <source>Difference to live element:</source>
+                       </trans-unit>
+                       <trans-unit id="commentForReviewer" xml:space="preserve">
+                               <source>Comment for Reviewer:</source>
+                       </trans-unit>
+                       <trans-unit id="commentForPublisher" xml:space="preserve">
+                               <source>Comment for Publisher:</source>
+                       </trans-unit>
+                       <trans-unit id="sendItemsToReview" xml:space="preserve">
+                               <source>Sending %s item(s) to review. </source>
+                       </trans-unit>
+                       <trans-unit id="sendAllToReview" xml:space="preserve">
+                               <source>Send all to Review</source>
+                       </trans-unit>
+                       <trans-unit id="approveToPublish" xml:space="preserve">
+                               <source>Approving %s item(s) to publishing. </source>
+                       </trans-unit>
+                       <trans-unit id="approveAllToPublish" xml:space="preserve">
+                               <source>Approve all for Publishing</source>
+                       </trans-unit>
+                       <trans-unit id="offline" xml:space="preserve">
+                               <source>[Offline]</source>
+                       </trans-unit>
+                       <trans-unit id="draft" xml:space="preserve">
+                               <source>Draft</source>
+                       </trans-unit>
+                       <trans-unit id="archive" xml:space="preserve">
+                               <source>Archive</source>
+                       </trans-unit>
+                       <trans-unit id="publishedXTimes" xml:space="preserve">
+                               <source>Published %s times</source>
+                       </trans-unit>
+                       <trans-unit id="stage.sentToReview" xml:space="preserve">
+                               <source>sent element to "Review"</source>
+                       </trans-unit>
+                       <trans-unit id="stage.approvedForPublish" xml:space="preserve">
+                               <source>approved for "Publish"</source>
+                       </trans-unit>
+                       <trans-unit id="stage.rejectedElement" xml:space="preserve">
+                               <source>rejected element!</source>
+                       </trans-unit>
+                       <trans-unit id="stage.resetToEdit" xml:space="preserve">
+                               <source>reset to "Editing"</source>
+                       </trans-unit>
+                       <trans-unit id="stage.undefined" xml:space="preserve">
+                               <source>[undefined]</source>
+                       </trans-unit>
+                       <trans-unit id="userComment" xml:space="preserve">
+                               <source>User Comment</source>
+                       </trans-unit>
+                       <trans-unit id="subElementsClick" xml:space="preserve">
+                               <source>[Sub elements, click for details]</source>
+                       </trans-unit>
+                       <trans-unit id="subElements" xml:space="preserve">
+                               <source>[Sub elements]</source>
+                       </trans-unit>
+                       <trans-unit id="completeMatch" xml:space="preserve">
+                               <source>Complete match on editable fields.</source>
+                       </trans-unit>
+                       <trans-unit id="editing" xml:space="preserve">
+                               <source>Editing</source>
+                       </trans-unit>
+                       <trans-unit id="sendToReview" xml:space="preserve">
+                               <source>Send to Review</source>
+                       </trans-unit>
+                       <trans-unit id="review" xml:space="preserve">
+                               <source>Review</source>
+                       </trans-unit>
+                       <trans-unit id="swap" xml:space="preserve">
+                               <source>Swap</source>
+                       </trans-unit>
+                       <trans-unit id="approveForPublishing" xml:space="preserve">
+                               <source>Approve for Publishing</source>
+                       </trans-unit>
+                       <trans-unit id="publish" xml:space="preserve">
+                               <source>Publish</source>
+                       </trans-unit>
+                       <trans-unit id="rejected" xml:space="preserve">
+                               <source>Rejected</source>
+                       </trans-unit>
+                       <trans-unit id="comment" xml:space="preserve">
+                               <source>Comment:</source>
+                       </trans-unit>
+                       <trans-unit id="resetStage" xml:space="preserve">
+                               <source>Reset stage</source>
+                       </trans-unit>
+                       <trans-unit id="undefined" xml:space="preserve">
+                               <source>Undefined</source>
+                       </trans-unit>
+                       <trans-unit id="rejectExplain" xml:space="preserve">
+                               <source>Please explain why you reject:</source>
+                       </trans-unit>
+                       <trans-unit id="removeFromWorkspace" xml:space="preserve">
+                               <source>Remove from workspace</source>
+                       </trans-unit>
+                       <trans-unit id="showLog" xml:space="preserve">
+                               <source>Show Log</source>
+                       </trans-unit>
+                       <trans-unit id="liveWorkspace" xml:space="preserve">
+                               <source>[Live workspace]</source>
+                       </trans-unit>
+                       <trans-unit id="draftWorkspaces" xml:space="preserve">
+                               <source>Draft Workspaces</source>
+                       </trans-unit>
+                       <trans-unit id="defaultDraft" xml:space="preserve">
+                               <source>[Default Draft]</source>
+                       </trans-unit>
+                       <trans-unit id="errors" xml:space="preserve">
+                               <source>Errors:</source>
+                       </trans-unit>
+                       <trans-unit id="refresh" xml:space="preserve">
+                               <source>Refresh</source>
+                       </trans-unit>
+                       <trans-unit id="allowPreviewOfWholeWorkspace" xml:space="preserve">
+                               <source>Allow preview of whole workspace</source>
+                       </trans-unit>
+                       <trans-unit id="versionId" xml:space="preserve">
+                               <source>ID:</source>
+                       </trans-unit>
+                       <trans-unit id="workspaceId" xml:space="preserve">
+                               <source>Workspace-Id:</source>
+                       </trans-unit>
+                       <trans-unit id="live" xml:space="preserve">
+                               <source>LIVE</source>
+                       </trans-unit>
+                       <trans-unit id="versionSelect.label" xml:space="preserve">
+                               <source>Versions of this page:</source>
+                       </trans-unit>
+                       <trans-unit id="versionSelect.live" xml:space="preserve">
+                               <source>LIVE version</source>
+                       </trans-unit>
+                       <trans-unit id="versionSelect.publish" xml:space="preserve">
+                               <source>Publish page</source>
+                       </trans-unit>
+                       <trans-unit id="versionSelect.inBranch" xml:space="preserve">
+                               <source>Inside branch, no further versioning possible</source>
+                       </trans-unit>
+      &n