[BUGFIX] Page module: Allow to paste in empty columns 32/25532/8
authorBernhard Kraft <kraft@webconsulting.at>
Wed, 20 Nov 2013 10:49:52 +0000 (11:49 +0100)
committerStefan Neufeind <typo3.neufeind@speedpartner.de>
Sun, 24 Nov 2013 17:54:50 +0000 (18:54 +0100)
Since quite a long time it was not possible to paste a content
element into an empty column of the page module. This patch
introduces an icon in the top right corner of each column if
a tt_content element is on the clipboard (Default pad). When
the icon gets clicked the content elements from the clipboard
get moved/copied into the selected column/language.

Resolves: #15080
Releases: 6.2, 6.1
Change-Id: If52905446eb11c268d0fee83f150efae0945fa29
Reviewed-on: https://review.typo3.org/25532
Reviewed-by: Markus Klein
Tested-by: Markus Klein
Reviewed-by: Stefan Neufeind
Tested-by: Stefan Neufeind
typo3/sysext/backend/Classes/Clipboard/Clipboard.php
typo3/sysext/backend/Classes/Controller/SimpleDataHandlerController.php
typo3/sysext/backend/Classes/View/PageLayoutView.php
typo3/sysext/core/Classes/DataHandling/DataHandler.php
typo3/sysext/core/Tests/Functional/DataHandling/DataHandlerTest.php
typo3/sysext/lang/locallang_core.xlf

index 24457c7..57d91e5 100644 (file)
@@ -549,11 +549,19 @@ class Clipboard {
         * @param string $table Tablename (_FILE for files)
         * @param mixed $uid "destination": can be positive or negative indicating how the paste is done (paste into / paste after)
         * @param boolean $setRedirect If set, then the redirect URL will point back to the current script, but with CB reset.
+        * @param array|NULL $update Additional key/value pairs which should get set in the moved/copied record (via DataHandler)
         * @return string
         * @todo Define visibility
         */
-       public function pasteUrl($table, $uid, $setRedirect = 1) {
-               $rU = $this->backPath . ($table == '_FILE' ? 'tce_file.php' : 'tce_db.php') . '?' . ($setRedirect ? 'redirect=' . rawurlencode(GeneralUtility::linkThisScript(array('CB' => ''))) : '') . '&vC=' . $GLOBALS['BE_USER']->veriCode() . '&prErr=1&uPT=1' . '&CB[paste]=' . rawurlencode(($table . '|' . $uid)) . '&CB[pad]=' . $this->current . BackendUtility::getUrlToken('tceAction');
+       public function pasteUrl($table, $uid, $setRedirect = TRUE, array $update = NULL) {
+               $rU = $this->backPath . ($table == '_FILE' ? 'tce_file.php' : 'tce_db.php') . '?' .
+                       ($setRedirect ? 'redirect=' . rawurlencode(GeneralUtility::linkThisScript(array('CB' => ''))) : '') .
+                       '&vC=' . $GLOBALS['BE_USER']->veriCode() .
+                       '&prErr=1&uPT=1' .
+                       '&CB[paste]=' .  rawurlencode($table . '|' . $uid) .
+                       '&CB[pad]=' . $this->current .
+                       (is_array($update) ? GeneralUtility::implodeArrayForUrl('CB[update]', $update) : '') .
+                       BackendUtility::getUrlToken('tceAction');
                return $rU;
        }
 
@@ -610,13 +618,14 @@ class Clipboard {
         * @param mixed $rec For records its an array, for files its a string (path)
         * @param string $type Type-code
         * @param array $clElements Array of selected elements
+        * @param string $columnLabel Name of the content column
         * @return string JavaScript "confirm" message
         * @todo Define visibility
         */
-       public function confirmMsg($table, $rec, $type, $clElements) {
+       public function confirmMsg($table, $rec, $type, $clElements, $columnLabel = '') {
                if ($GLOBALS['BE_USER']->jsConfirmation(2)) {
                        $labelKey = 'LLL:EXT:lang/locallang_core.xlf:mess.' . ($this->currentMode() == 'copy' ? 'copy' : 'move') . ($this->current == 'normal' ? '' : 'cb') . '_' . $type;
-                       $msg = $GLOBALS['LANG']->sL($labelKey);
+                       $msg = $GLOBALS['LANG']->sL($labelKey . ($columnLabel ? '_colPos': ''));
                        if ($table == '_FILE') {
                                $thisRecTitle = basename($rec);
                                if ($this->current == 'normal') {
@@ -634,8 +643,16 @@ class Clipboard {
                                        $selRecTitle = count($clElements);
                                }
                        }
+                       // @TODO
+                       // This can get removed as soon as the "_colPos" label is translated
+                       // into all available locallang languages.
+                       if (!$msg && $columnLabel) {
+                               $thisRecTitle .= ' | ' . $columnLabel;
+                               $msg = $GLOBALS['LANG']->sL($labelKey);
+                       }
+
                        // Message
-                       $conf = 'confirm(' . $GLOBALS['LANG']->JScharCode(sprintf($msg, GeneralUtility::fixed_lgd_cs($selRecTitle, 30), GeneralUtility::fixed_lgd_cs($thisRecTitle, 30))) . ')';
+                       $conf = 'confirm(' . $GLOBALS['LANG']->JScharCode(sprintf($msg, GeneralUtility::fixed_lgd_cs($selRecTitle, 30), GeneralUtility::fixed_lgd_cs($thisRecTitle, 30), GeneralUtility::fixed_lgd_cs($columnLabel, 30))) . ')';
                } else {
                        $conf = '';
                }
@@ -857,10 +874,11 @@ class Clipboard {
         *
         * @param string $ref [tablename]:[paste-uid], see description
         * @param array $CMD Command-array
+        * @param NULL|array If additional values should get set in the copied/moved record this will be an array containing key=>value pairs
         * @return array Modified Command-array
         * @todo Define visibility
         */
-       public function makePasteCmdArray($ref, $CMD) {
+       public function makePasteCmdArray($ref, $CMD, array $update = NULL) {
                list($pTable, $pUid) = explode('|', $ref);
                $pUid = intval($pUid);
                // pUid must be set and if pTable is not set (that means paste ALL elements)
@@ -876,7 +894,15 @@ class Clipboard {
                                if (!is_array($CMD[$table])) {
                                        $CMD[$table] = array();
                                }
-                               $CMD[$table][$uid][$mode] = $pUid;
+                               if (is_array($update)) {
+                                       $CMD[$table][$uid][$mode] = array(
+                                               'action' => 'paste',
+                                               'target' => $pUid,
+                                               'update' => $update,
+                                       );
+                               } else {
+                                       $CMD[$table][$uid][$mode] = $pUid;
+                               }
                                if ($mode == 'move') {
                                        $this->removeElement($tP);
                                }
index 835410e..0f3cfd3 100644 (file)
@@ -189,7 +189,11 @@ class SimpleDataHandlerController {
                        $clipObj->initializeClipboard();
                        if ($this->CB['paste']) {
                                $clipObj->setCurrentPad($this->CB['pad']);
-                               $this->cmd = $clipObj->makePasteCmdArray($this->CB['paste'], $this->cmd);
+                               $this->cmd = $clipObj->makePasteCmdArray(
+                                       $this->CB['paste'],
+                                       $this->cmd,
+                                       isset($this->CB['update']) ? $this->CB['update'] : NULL
+                               );
                        }
                        if ($this->CB['delete']) {
                                $clipObj->setCurrentPad($this->CB['pad']);
index 62c2e40..b4b429d 100644 (file)
@@ -174,6 +174,11 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
         */
        protected $rteSetup = array();
 
+       /**
+        * @var \TYPO3\CMS\Backend\Clipboard\Clipboard
+        */
+       protected $clipboard;
+
        /*****************************************
         *
         * Renderings
@@ -360,6 +365,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
         */
        public function getTable_tt_content($id) {
                $this->initializeLanguages();
+               $this->initializeClipboard();
                // Initialize:
                $RTE = $GLOBALS['BE_USER']->isRTE();
                $lMarg = 1;
@@ -498,7 +504,8 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                                        $colTitle = $GLOBALS['LANG']->sL($item[0]);
                                                }
                                        }
-                                       $head[$key] .= $this->tt_content_drawColHeader($colTitle, $this->doEdit && count($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : '', $newP);
+                                       $pasteP = array('colPos' => $key, 'sys_language_uid' => $lP);
+                                       $head[$key] .= $this->tt_content_drawColHeader($colTitle, $this->doEdit && count($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : '', $newP, $pasteP);
                                        $editUidList = '';
                                }
                                // For each column, fit the rendered content into a table cell:
@@ -721,12 +728,13 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                        }
                                        // Add section header:
                                        $newP = $this->newContentElementOnClick($id, $key, $this->tt_contentConfig['sys_language_uid']);
+                                       $pasteP = array('colPos' => $key, 'sys_language_uid' => $this->tt_contentConfig['sys_language_uid']);
                                        $out .= '
 
                                                <!-- Column header: -->
                                                <tr>
                                                        <td></td>
-                                                       <td valign="top" colspan="3">' . $this->tt_content_drawColHeader(BackendUtility::getProcessedValue('tt_content', 'colPos', $key), ($this->doEdit && count($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : ''), $newP) . $theNewButton . '<br /></td>
+                                                       <td valign="top" colspan="3">' . $this->tt_content_drawColHeader(BackendUtility::getProcessedValue('tt_content', 'colPos', $key), ($this->doEdit && count($rowArr) ? '&edit[tt_content][' . $editUidList . ']=edit' . $pageTitleParamForAltDoc : ''), $newP, $pasteP) . $theNewButton . '<br /></td>
                                                </tr>';
                                        // Finally, add the content from the records in this column:
                                        $out .= $rowOut;
@@ -1069,20 +1077,36 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
         * @param string $colName Column name
         * @param string $editParams Edit params (Syntax: &edit[...] for alt_doc.php)
         * @param string $newParams New element params (Syntax: &edit[...] for alt_doc.php) OBSOLETE
+        * @param array|NULL $pasteParams Paste element params (i.e. array(colPos => 1, sys_language_uid => 2))
         * @return string HTML table
         * @todo Define visibility
         */
-       public function tt_content_drawColHeader($colName, $editParams, $newParams) {
-               $icons = '';
+       public function tt_content_drawColHeader($colName, $editParams, $newParams, array $pasteParams = NULL) {
+               $iconsArr = array();
                // Create command links:
                if ($this->tt_contentConfig['showCommands']) {
                        // Edit whole of column:
                        if ($editParams) {
-                               $icons .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($editParams, $this->backPath)) . '" title="' . $GLOBALS['LANG']->getLL('editColumn', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-open') . '</a>';
+                               $iconsArr['edit'] = '<a href="#" onclick="'
+                                       . htmlspecialchars(BackendUtility::editOnClick($editParams, $this->backPath)) . '" title="'
+                                       . $GLOBALS['LANG']->getLL('editColumn', TRUE) . '">'
+                                       . IconUtility::getSpriteIcon('actions-document-open') . '</a>';
+                       }
+                       if ($pasteParams) {
+                               $elFromTable = $this->clipboard->elFromTable('tt_content');
+                               if (count($elFromTable)) {
+                                       $iconsArr['paste'] = '<a href="'
+                                               . htmlspecialchars($this->clipboard->pasteUrl('tt_content', $this->id, TRUE, $pasteParams))
+                                               . '" onclick="' . htmlspecialchars(('return '
+                                               . $this->clipboard->confirmMsg('pages', $this->pageRecord, 'into', $elFromTable, $colName)))
+                                               . '" title="' . $GLOBALS['LANG']->getLL('clip_paste', TRUE) . '">'
+                                               . IconUtility::getSpriteIcon('actions-document-paste-into') . '</a>';
+                               }
                        }
                }
-               if (strlen($icons)) {
-                       $icons = '<div class="t3-page-colHeader-icons">' . $icons . '</div>';
+               $icons = '';
+               if (count($iconsArr)) {
+                       $icons = '<div class="t3-page-colHeader-icons">' . implode('', $iconsArr) . '</div>';
                }
                // Create header row:
                $out = '<div class="t3-page-colHeader t3-row-header">
@@ -1595,6 +1619,33 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
         * Various helper functions
         *
         ********************************/
+
+       /**
+        * Initializes the clipboard for generating paste links
+        *
+        * @return void
+        *
+        * @see \TYPO3\CMS\Recordlist\RecordList::main()
+        * @see \TYPO3\CMS\Backend\Controller\ClickMenuController::main()
+        * @see \TYPO3\CMS\Filelist\Controller\FileListController::main()
+        */
+       protected function initializeClipboard() {
+               // Start clipboard
+               $this->clipboard = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Clipboard\\Clipboard');
+
+               // Initialize - reads the clipboard content from the user session
+               $this->clipboard->initializeClipboard();
+
+               // This locks the clipboard to the Normal for this request.
+               $this->clipboard->lockToNormal();
+
+               // Clean up pad
+               $this->clipboard->cleanCurrent();
+
+               // Save the clipboard content
+               $this->clipboard->endClipboard();
+       }
+
        /**
         * Counts and returns the number of records on the page with $pid
         *
index a56925f..bdfb895 100644 (file)
@@ -2836,6 +2836,7 @@ class DataHandler {
                                $hookObjectsArr[] = $hookObj;
                        }
                }
+               $pasteDatamap = array();
                // Traverse command map:
                foreach (array_keys($this->cmdmap) as $table) {
                        // Check if the table may be modified!
@@ -2849,15 +2850,23 @@ class DataHandler {
                        if (isset($GLOBALS['TCA'][$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) {
                                // Traverse the command map:
                                foreach ($this->cmdmap[$table] as $id => $incomingCmdArray) {
+                                       $pasteUpdate = FALSE;
                                        if (is_array($incomingCmdArray)) {
                                                // have found a command.
                                                // Get command and value (notice, only one command is observed at a time!):
                                                reset($incomingCmdArray);
                                                $command = key($incomingCmdArray);
                                                $value = current($incomingCmdArray);
+                                               if (is_array($value) && isset($value['action']) && $value['action'] === 'paste') {
+                                                       // Extended paste command: $command is set to "move" or "copy"
+                                                       // $value['update'] holds field/value pairs which should be updated after copy/move operation
+                                                       // $value['target'] holds original $value (target of move/copy)
+                                                       $pasteUpdate = $value['update'];
+                                                       $value = $value['target'];
+                                               }
                                                foreach ($hookObjectsArr as $hookObj) {
                                                        if (method_exists($hookObj, 'processCmdmap_preProcess')) {
-                                                               $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this);
+                                                               $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this, $pasteUpdate);
                                                        }
                                                }
                                                // Init copyMapping array:
@@ -2868,11 +2877,12 @@ class DataHandler {
                                                $commandIsProcessed = FALSE;
                                                foreach ($hookObjectsArr as $hookObj) {
                                                        if (method_exists($hookObj, 'processCmdmap')) {
-                                                               $hookObj->processCmdmap($command, $table, $id, $value, $commandIsProcessed, $this);
+                                                               $hookObj->processCmdmap($command, $table, $id, $value, $commandIsProcessed, $this, $pasteUpdate);
                                                        }
                                                }
                                                // Only execute default commands if a hook hasn't been processed the command already
                                                if (!$commandIsProcessed) {
+                                                       $procId = $id;
                                                        // Branch, based on command
                                                        switch ($command) {
                                                                case 'move':
@@ -2884,6 +2894,7 @@ class DataHandler {
                                                                        } else {
                                                                                $this->copyRecord($table, $id, $value, 1);
                                                                        }
+                                                                       $procId = $this->copyMappingArray[$table][$id];
                                                                        break;
                                                                case 'localize':
                                                                        $this->localize($table, $id, $value);
@@ -2898,10 +2909,13 @@ class DataHandler {
                                                                        $this->undeleteRecord($table, $id);
                                                                        break;
                                                        }
+                                                       if (is_array($pasteUpdate)) {
+                                                               $pasteDatamap[$table][$procId] = $pasteUpdate;
+                                                       }
                                                }
                                                foreach ($hookObjectsArr as $hookObj) {
                                                        if (method_exists($hookObj, 'processCmdmap_postProcess')) {
-                                                               $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this);
+                                                               $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this, $pasteUpdate, $pasteDatamap);
                                                        }
                                                }
                                                // Merging the copy-array info together for remapping purposes.
@@ -2910,6 +2924,13 @@ class DataHandler {
                                }
                        }
                }
+               /** @var $copyTCE \TYPO3\CMS\Core\DataHandling\DataHandler */
+               $copyTCE = $this->getLocalTCE();
+               $copyTCE->start($pasteDatamap, '', $this->BE_USER);
+               $copyTCE->process_datamap();
+               $this->errorLog = array_merge($this->errorLog, $copyTCE->errorLog);
+               unset($copyTCE);
+
                // Finally, before exit, check if there are ID references to remap.
                // This might be the case if versioning or copying has taken place!
                $this->remapListedDBRecords();
@@ -3012,13 +3033,7 @@ class DataHandler {
                                                }
                                                // Do the copy by simply submitting the array through TCEmain:
                                                /** @var $copyTCE \TYPO3\CMS\Core\DataHandling\DataHandler */
-                                               $copyTCE = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
-                                               $copyTCE->stripslashes_values = 0;
-                                               $copyTCE->copyTree = $this->copyTree;
-                                               // Copy forth the cached TSconfig
-                                               $copyTCE->cachedTSconfig = $this->cachedTSconfig;
-                                               // Transformations should NOT be carried out during copy
-                                               $copyTCE->dontProcessTransformations = 1;
+                                               $copyTCE = $this->getLocalTCE();
                                                $copyTCE->start($data, '', $this->BE_USER);
                                                $copyTCE->process_datamap();
                                                // Getting the new UID:
@@ -3971,12 +3986,7 @@ class DataHandler {
                                                                                } else {
                                                                                        // Create new record:
                                                                                        /** @var $copyTCE \TYPO3\CMS\Core\DataHandling\DataHandler */
-                                                                                       $copyTCE = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
-                                                                                       // Copy forth the cached TSconfig
-                                                                                       $copyTCE->stripslashes_values = 0;
-                                                                                       $copyTCE->cachedTSconfig = $this->cachedTSconfig;
-                                                                                       // Transformations should NOT be carried out during copy
-                                                                                       $copyTCE->dontProcessTransformations = 1;
+                                                                                       $copyTCE = $this->getLocalTCE();
                                                                                        $copyTCE->start(array($Ttable => array('NEW' => $overrideValues)), '', $this->BE_USER);
                                                                                        $copyTCE->process_datamap();
                                                                                        // Getting the new UID as if it had been copied:
@@ -4845,6 +4855,25 @@ class DataHandler {
         * Cmd: Helper functions
         *
         ********************************************/
+
+       /**
+        * Returns a instance of TCEmain for handling local datamaps/cmdmaps
+        *
+        * @param boolean $stripslashesValues If TRUE, incoming values in the data-array have their slashes stripped.
+        * @param boolean $dontProcessTransformations If set, then transformations are NOT performed on the input.
+        * @return DataHandler
+        */
+       protected function getLocalTCE($stripslashesValues = FALSE, $dontProcessTransformations = TRUE) {
+               $copyTCE = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
+               $copyTCE->stripslashes_values = $stripslashesValues;
+               $copyTCE->copyTree = $this->copyTree;
+               // Copy forth the cached TSconfig
+               $copyTCE->cachedTSconfig = $this->cachedTSconfig;
+               // Transformations should NOT be carried out during copy
+               $copyTCE->dontProcessTransformations = $dontProcessTransformations;
+               return $copyTCE;
+       }
+
        /**
         * Processes the fields with references as registered during the copy process. This includes all FlexForm fields which had references.
         *
index 2f22811..9cb8fb5 100644 (file)
@@ -39,6 +39,8 @@ class DataHandlerTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
                /** @var $backendUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */
                $backendUser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication');
                $backendUser->user['admin'] = 1;
+               // By default make tests on live workspace
+               $backendUser->workspace = 0;
                $GLOBALS['BE_USER'] = $backendUser;
                \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->initializeLanguageObject();
                $this->importDataSet(dirname(__FILE__) . '/../Fixtures/pages.xml');
@@ -111,6 +113,100 @@ class DataHandlerTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase {
        }
 
        /**
+        * @test
+        */
+       public function canCopyPasteTtContent() {
+               $this->importDataSet(dirname(__FILE__) . '/../Fixtures/tt_content.xml');
+               $database = $this->getDatabase();
+
+               $dataHandler = $this->getDataHandler();
+               $originalRecordId  = 1;
+               $originalRecord = $database->exec_SELECTgetSingleRow('*', 'tt_content', 'uid = ' . $originalRecordId);
+               $targetPage = 2;
+               $targetColumn = 3;
+
+               $commandMap = array(
+                       'tt_content' => array(
+                               $originalRecordId => array(
+                                       'copy' => array(
+                                               'action' => 'paste',
+                                               'target' => $targetPage,
+                                               'update' => array(
+                                                       'colPos' => $targetColumn
+                                               )
+                                       ),
+                               )
+                       )
+               );
+
+               $dataHandler->start(array(), $commandMap);
+               $dataHandler->process_cmdmap();
+
+               $rows = $database->exec_SELECTgetRows('*', 'tt_content', '1=1');
+
+               // Check whether there are exactly two records now
+               $rowCount = $database->exec_SELECTcountRows('*', 'tt_content', '1=1');
+               $this->assertEquals(2, $rowCount);
+
+               // Retrieve the UID of the copied record. Should be 2 (auto_increment)
+               $uid = $dataHandler->copyMappingArray_merged['tt_content'][$originalRecordId];
+               $this->assertGreaterThanOrEqual(2, $uid);
+
+               // Retrieve copied record
+               $row = $database->exec_SELECTgetSingleRow('*', 'tt_content', 'uid = ' . $uid);
+               $this->assertNotEmpty($row);
+
+               // Check whether the copy&pasted record is at expected page and column
+               $this->assertEquals($targetPage, $row['pid']);
+               $this->assertEquals($targetColumn, $row['colPos']);
+
+               // Check whether original record has not changed
+               $checkRecord = $database->exec_SELECTgetSingleRow('*', 'tt_content', 'uid = ' . $originalRecordId);
+               $this->assertEquals($checkRecord, $originalRecord);
+       }
+
+       /**
+        * @test
+        */
+       public function canCutPasteTtContent() {
+               $this->importDataSet(dirname(__FILE__) . '/../Fixtures/tt_content.xml');
+               $database = $this->getDatabase();
+
+               $dataHandler = $this->getDataHandler();
+               $originalRecordId  = 1;
+               $targetPage = 2;
+               $targetColumn = 3;
+
+               $commandMap = array(
+                       'tt_content' => array(
+                               $originalRecordId => array(
+                                       'move' => array(
+                                               'action' => 'paste',
+                                               'target' => $targetPage,
+                                               'update' => array(
+                                                       'colPos' => $targetColumn
+                                               )
+                                       ),
+                               )
+                       )
+               );
+
+               $dataHandler->start(array(), $commandMap);
+               $dataHandler->process_cmdmap();
+
+               $row = $database->exec_SELECTgetSingleRow('*', 'tt_content', 'uid = ' . $originalRecordId);
+               $this->assertNotEmpty($row);
+
+               // Check whether the pasted record is at expected page and column
+               $this->assertEquals($targetPage, $row['pid']);
+               $this->assertEquals($targetColumn, $row['colPos']);
+
+               // Check whether this was the only existing record
+               $rowCount = $database->exec_SELECTcountRows('*', 'tt_content', '1=1');
+               $this->assertEquals(1, $rowCount);
+       }
+
+       /**
         * @return \TYPO3\CMS\Core\DataHandling\DataHandler
         */
        protected function getDataHandler() {
index 3ea5720..521ccad 100644 (file)
@@ -591,12 +591,18 @@ Would you like to save now in order to refresh the display?</source>
                        <trans-unit id="mess.move_into" xml:space="preserve">
                                <source>Move "%s" into "%s"?</source>
                        </trans-unit>
+                       <trans-unit id="mess.move_into_colPos" xml:space="preserve">
+                               <source>Move "%1$s" into column "%3$s" on page "%2$s"?</source>
+                       </trans-unit>
                        <trans-unit id="mess.move_after" xml:space="preserve">
                                <source>Move "%s" to after "%s"?</source>
                        </trans-unit>
                        <trans-unit id="mess.copy_into" xml:space="preserve">
                                <source>Copy "%s" into "%s"?</source>
                        </trans-unit>
+                       <trans-unit id="mess.copy_into_colPos" xml:space="preserve">
+                               <source>Copy "%1$s" into column "%3$s" on page "%2$s"?</source>
+                       </trans-unit>
                        <trans-unit id="mess.copy_after" xml:space="preserve">
                                <source>Copy "%s" to after "%s"?</source>
                        </trans-unit>