2 namespace TYPO3\CMS\Backend\Clipboard;
110 $clNP = $this->
getBackendUser()->getTSConfigVal(
'options.clipboardNumberPads');
115 $this->clipData[
'normal'] = is_array(
$clipData[
'normal']) ?
$clipData[
'normal'] : array();
117 $this->clipData[
'tab_' . $a] = is_array(
$clipData[
'tab_' . $a]) ?
$clipData[
'tab_' . $a] : array();
120 $this->clipData[
'current'] = ($this->current = isset($this->clipData[
$clipData[
'current']]) ? $clipData[
'current'] :
'normal');
133 $this->current =
'normal';
150 if (is_array($cmd[
'el'])) {
151 foreach ($cmd[
'el'] as $k => $v) {
152 if ($this->current ==
'normal') {
153 unset($this->clipData[
'normal']);
168 if ($cmd[
'remove']) {
173 if ($cmd[
'removeAll']) {
174 $this->clipData[$cmd[
'removeAll']] = array();
178 if (isset($cmd[
'setCopyMode'])) {
193 if (!$this->
lockToNormal && $this->current != $padIdent) {
194 if (isset($this->clipData[$padIdent])) {
195 $this->clipData[
'current'] = ($this->current = $padIdent);
197 if ($this->current !=
'normal' || !$this->
isElements()) {
213 if ($this->changed) {
227 public function cleanUpCBC($CBarr, $table, $removeDeselected = 0)
229 if (is_array($CBarr)) {
230 foreach ($CBarr as $k => $v) {
231 $p = explode(
'|', $k);
232 if ((
string)$p[0] != (
string)$table || $removeDeselected && !$v) {
253 $elementCount = count($this->
elFromTable($this->fileMode ?
'_FILE' :
''));
256 $moveLabel = htmlspecialchars($this->
getLanguageService()->sL(
'LLL:EXT:lang/locallang_misc.xlf:moveElements'));
257 $copyLabel = htmlspecialchars($this->
getLanguageService()->sL(
'LLL:EXT:lang/locallang_misc.xlf:copyElements'));
259 $copymodeSelector =
'
260 <div class="btn-group">
261 <button class="btn btn-default dropdown-toggle" type="button" id="copymodeSelector" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
262 ' . ($this->
currentMode() ===
'copy' ? $copyLabel : $moveLabel) .
'
263 <span class="caret"></span>
265 <ul class="dropdown-menu" aria-labelledby="copymodeSelector">
266 <li><a href="#" onclick="document.getElementById(\'clipboard_form\').method=\'POST\'; document.getElementById(\'clipboard_form\').action=' . htmlspecialchars(
GeneralUtility::quoteJSvalue($copymodeUrl .
'&CB[setCopyMode]=')) .
'; document.getElementById(\'clipboard_form\').submit(); return true;">' . $moveLabel .
'</a></li>
267 <li><a href="#" onclick="document.getElementById(\'clipboard_form\').method=\'POST\'; document.getElementById(\'clipboard_form\').action=' . htmlspecialchars(
GeneralUtility::quoteJSvalue($copymodeUrl .
'&CB[setCopyMode]=1')) .
'; document.getElementById(\'clipboard_form\').submit(); return true;">' . $copyLabel .
'</a></li>
278 $optionArray = array();
285 if (!$this->fileMode) {
286 $optionArray[] =
'<li><a href="#" onclick="' . htmlspecialchars((
'window.location.href=' .
GeneralUtility::quoteJSvalue($this->
editUrl() .
'&returnUrl=') .
'+top.rawurlencode(window.location.href);')) .
'">' . $this->
clLabel(
'edit',
'rm') .
'</a></li>';
299 $optionArray[] =
'<li><a href="#" onclick="' . htmlspecialchars($js) .
'">' . $this->
getLanguageService()->sL(
'LLL:EXT:lang/locallang_core.xlf:labels.clipboard.delete_elements',
true) .
'</a></li>';
302 $optionArray[] =
'<li><a href="#" onclick="' . htmlspecialchars(
'window.location.href=' .
GeneralUtility::quoteJSvalue($removeAllUrl .
'#clip_head')) .
'">' . $this->
getLanguageService()->sL(
'LLL:EXT:lang/locallang_core.xlf:labels.clipboard.clear_clipboard',
true) .
'</a></li>';
307 <div class="btn-group">
308 <button class="btn btn-default dropdown-toggle" type="button" id="menuSelector" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
309 ' . $this->
clLabel(
'menu',
'rm') .
'
310 <span class="caret"></span>
312 <ul class="dropdown-menu" aria-labelledby="menuSelector">
313 ' . implode(
'', $optionArray) .
'
321 <td colspan="2" nowrap="nowrap" width="95%">' . $copymodeSelector .
' ' . $menuSelector .
'</td>
322 <td nowrap="nowrap" class="col-control">' . $deleteLink .
'</td>
327 $current = ($this->current ==
'normal');
330 <td colspan="3"><a href="' . htmlspecialchars(
GeneralUtility::linkThisScript(array(
'CB' => array(
'setP' =>
'normal')))) .
'#clip_head" title="' . $this->
clLabel(
'normal-description') .
'">'
331 .
'<span class="t3-icon fa ' . (
$current ?
'fa-check-circle' :
'fa-circle-o') .
'"></span>'
335 if ($this->current ==
'normal') {
341 $current = ($this->current ==
'tab_' . $a);
344 <td colspan="3"><a href="' . htmlspecialchars(
GeneralUtility::linkThisScript(array(
'CB' => array(
'setP' => (
'tab_' . $a))))) .
'#clip_head" title="' . $this->
clLabel(
'cliptabs-description') .
'">'
345 .
'<span class="t3-icon fa ' . (
$current ?
'fa-check-circle' :
'fa-circle-o') .
'"></span>'
349 if ($this->current ==
'tab_' . $a) {
354 $output =
'<a name="clip_head"></a>
360 <div class="col-sm-12">
361 <div class="panel panel-default">
362 <div class="panel-heading">' . BackendUtility::wrapInHelp(
'xMOD_csh_corebe',
'list_clipboard', $this->
clLabel(
'clipboard',
'buttons')) .
'</div>
363 <table class="table">
364 ' . implode(
'', $out) .
'
371 $output =
'<form action="" id="clipboard_form">' . $output .
'</form>';
386 if (is_array($this->clipData[$pad][
'el'])) {
387 foreach ($this->clipData[$pad][
'el'] as $k => $v) {
389 list($table, $uid) = explode(
'|', $k);
390 $bgColClass = $table ==
'_FILE' && $this->fileMode || $table !=
'_FILE' && !$this->fileMode ?
'bgColor4-20' :
'bgColor4';
392 if ($table ==
'_FILE') {
396 $folder = $fileObject instanceof \TYPO3\CMS\Core\Resource\Folder;
398 $icon =
'<span title="' . htmlspecialchars($fileObject->getName() .
' ' . $size) .
'">' . $this->iconFactory->getIconForResource($fileObject,
Icon::SIZE_SMALL)->render() .
'</span>';
400 $processedFile = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, array());
401 if ($processedFile) {
402 $thumbUrl = $processedFile->getPublicUrl(
true);
403 $thumb =
'<br /><img src="' . htmlspecialchars($thumbUrl) .
'" ' .
404 'width="' . $processedFile->getProperty(
'width') .
'" ' .
405 'height="' . $processedFile->getProperty(
'height') .
'" ' .
406 'title="' . htmlspecialchars($fileObject->getName()) .
'" alt="" />';
411 <td nowrap="nowrap" class="col-icon">' . $icon .
'</td>
412 <td nowrap="nowrap" width="95%">' . $this->
linkItemText(htmlspecialchars(
GeneralUtility::fixed_lgd_cs($fileObject->getName(), $this->
getBackendUser()->uc[
'titleLen'])), $fileObject->getName()) . ($pad ==
'normal' ?
' <strong>(' . ($this->clipData[
'normal'][
'mode'] ==
'copy' ? $this->
clLabel(
'copy',
'cm') : $this->
clLabel(
'cut',
'cm')) .
')</strong>' :
'') .
' ' . $thumb .
'</td>
413 <td nowrap="nowrap" class="col-control">
414 <div class="btn-group">
415 <a class="btn btn-default" href="#" onclick="' . htmlspecialchars((
'top.launchView(' .
GeneralUtility::quoteJSvalue($table) .
', ' .
GeneralUtility::quoteJSvalue($v) .
'); return false;')) .
'"title="' . $this->
clLabel(
'info',
'cm') .
'">' . $this->iconFactory->getIcon(
'actions-document-info',
Icon::SIZE_SMALL)->render() .
'</a>' .
'<a class="btn btn-default" href="' . htmlspecialchars($this->
removeUrl(
'_FILE', GeneralUtility::shortmd5($v))) .
'#clip_head" title="' . $this->
clLabel(
'removeItem') .
'">' . $this->iconFactory->getIcon(
'actions-selection-delete',
Icon::SIZE_SMALL)->render() .
'</a>
421 unset($this->clipData[$pad][
'el'][$k]);
427 if (is_array($rec)) {
430 <td nowrap="nowrap" class="col-icon">' . $this->
linkItemText($this->iconFactory->getIconForRecord($table, $rec,
Icon::SIZE_SMALL)->render(), $rec, $table) .
'</td>
432 <td nowrap="nowrap" class="col-control">
433 <div class="btn-group">
434 <a class="btn btn-default" href="#" onclick="' . htmlspecialchars((
'top.launchView(' .
GeneralUtility::quoteJSvalue($table) .
', \'' . (
int)$uid .
'\');
return false;
')) . '" title="' . $this->clLabel('info
', 'cm
') . '">' . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . '</a>' . '<a class="btn btn-
default" href="' . htmlspecialchars($this->removeUrl($table, $uid)) . '#clip_head
" title="' . $this->clLabel('removeItem
') . '">' . $this->iconFactory->getIcon('actions-selection-delete', Icon::SIZE_SMALL)->render() . '</a>
438 $localizationData = $this->getLocalizations($table, $rec, $bgColClass, $pad);
439 if ($localizationData) {
440 $lines[] = $localizationData;
443 unset($this->clipData[$pad]['el'][$k]);
450 $this->endClipboard();
459 public function hasElements()
461 foreach ($this->clipData as $data) {
462 if (isset($data['el']) && is_array($data['el']) && !empty($data['el'])) {
479 public function getLocalizations($table, $parentRec, $bgColClass, $pad)
482 $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
483 if ($table != 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) {
485 $where[] = $tcaCtrl['transOrigPointerField'] . '=' . (int)$parentRec['uid'];
486 $where[] = $tcaCtrl['languageField'] . '<>0';
487 if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) {
488 $where[] = $tcaCtrl['delete'] . '=0';
490 if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) {
491 $where[] = 't3ver_wsid=' . $parentRec['t3ver_wsid'];
493 $rows = $this->getDatabaseConnection()->exec_SELECTgetRows('*', $table, implode(' AND ', $where));
494 if (is_array($rows)) {
496 if ($pad == 'normal') {
497 $mode = $this->clipData['normal']['mode'] == 'copy' ? 'copy' : 'cut';
498 $modeData = ' <strong>(' . $this->clLabel($mode, 'cm') . ')</strong>';
500 foreach ($rows as $rec) {
503 <td nowrap="nowrap
" class="col-icon
">' . $this->iconFactory->getIconForRecord($table, $rec, Icon::SIZE_SMALL)->render() . '</td>
504 <td nowrap="nowrap
" width="95%
">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $this->getBackendUser()->uc['titleLen'])) . $modeData . '</td>
505 <td nowrap="nowrap
" class="col-control
"></td>
510 return implode('', $lines);
522 public function padTitleWrap($str, $pad, $active)
524 $el = count($this->elFromTable($this->fileMode ? '_FILE' : '', $pad));
526 $str .= ' (' . ($pad == 'normal' ? ($this->clipData['normal']['mode'] == 'copy' ? $this->clLabel('copy', 'cm') : $this->clLabel('cut', 'cm')) : htmlspecialchars($el)) . ')';
528 if ($active === true) {
529 return '<strong>' . $str . '</strong>';
531 return '<span class="text-muted
">' . $str . '</span>';
543 public function linkItemText($str, $rec, $table = '')
545 if (is_array($rec) && $table) {
546 if ($this->fileMode) {
547 $str = '<span class="text-muted
">' . $str . '</span>';
549 $str = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_list
', array('id' => $rec['pid
']))) . '">' . $str . '</a>';
551 } elseif (file_exists($rec)) {
552 if (!$this->fileMode) {
553 $str = '<span class="text-muted
">' . $str . '</span>';
555 if (ExtensionManagementUtility::isLoaded('filelist')) {
556 $str = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('file_list
', array('id' => dirname($rec)))) . '">' . $str . '</a>';
573 public function selUrlDB($table, $uid, $copy = 0, $deselect = 0, $baseArray = array())
575 $CB = array('el' => array(rawurlencode($table . '|' . $uid) => $deselect ? 0 : 1));
577 $CB['setCopyMode'] = 1;
579 $baseArray['CB'] = $CB;
580 return GeneralUtility::linkThisScript($baseArray);
592 public function selUrlFile($path, $copy = 0, $deselect = 0, $baseArray = array())
594 $CB = array('el' => array(rawurlencode('_FILE|' . GeneralUtility::shortmd5($path)) => $deselect ? '' : $path));
596 $CB['setCopyMode'] = 1;
598 $baseArray['CB'] = $CB;
599 return GeneralUtility::linkThisScript($baseArray);
613 public function pasteUrl($table, $uid, $setRedirect = true, array $update = null)
616 'vC' => $this->getBackendUser()->veriCode(),
619 'CB[paste]' => $table . '|' . $uid,
620 'CB[pad]' => $this->current
623 $urlParameters['redirect'] = GeneralUtility::linkThisScript(array('CB' => ''));
625 if (is_array($update)) {
626 $urlParameters['CB[update]'] = $update;
628 return BackendUtility::getModuleUrl($table === '_FILE' ? 'tce_file' : 'tce_db', $urlParameters);
638 public function deleteUrl($setRedirect = 1, $file = 0)
641 'vC' => $this->getBackendUser()->veriCode(),
645 'CB[pad]' => $this->current
648 $urlParameters['redirect'] = GeneralUtility::linkThisScript(array('CB' => ''));
650 return BackendUtility::getModuleUrl($file ? 'tce_file' : 'tce_db', $urlParameters);
660 public function editUrl()
662 $parameters = array();
664 $elements = $this->elFromTable('');
665 foreach ($elements as $tP => $value) {
666 list($table, $uid) = explode('|', $tP);
667 $parameters['edit[' . $table . '][' . $uid . ']'] = 'edit';
669 return BackendUtility::getModuleUrl('record_edit', $parameters);
680 public function removeUrl($table, $uid)
682 return GeneralUtility::linkThisScript(array('CB' => array('remove' => $table . '|' . $uid)));
695 public function confirmMsg($table, $rec, $type, $clElements, $columnLabel = '')
697 if ($this->getBackendUser()->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE)) {
698 $labelKey = 'LLL:EXT:lang/locallang_core.xlf:mess.' . ($this->currentMode() == 'copy' ? 'copy' : 'move') . ($this->current == 'normal' ? '' : 'cb') . '_' . $type;
699 $msg = $this->getLanguageService()->sL($labelKey . ($columnLabel ? '_colPos': ''));
700 if ($table == '_FILE') {
701 $thisRecTitle = basename($rec);
702 if ($this->current == 'normal') {
703 $selItem = reset($clElements);
704 $selRecTitle = basename($selItem);
706 $selRecTitle = count($clElements);
709 $thisRecTitle = $table == 'pages' && !is_array($rec) ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : BackendUtility::getRecordTitle($table, $rec);
710 if ($this->current == 'normal') {
711 $selItem = $this->getSelectedRecord();
712 $selRecTitle = $selItem['_RECORD_TITLE'];
714 $selRecTitle = count($clElements);
718 // This can get removed as soon as the "_colPos
" label is translated
719 // into all available locallang languages.
720 if (!$msg && $columnLabel) {
721 $thisRecTitle .= ' | ' . $columnLabel;
722 $msg = $this->getLanguageService()->sL($labelKey);
726 $conf = 'confirm(' . GeneralUtility::quoteJSvalue(sprintf($msg, GeneralUtility::fixed_lgd_cs($selRecTitle, 30), GeneralUtility::fixed_lgd_cs($thisRecTitle, 30), GeneralUtility::fixed_lgd_cs($columnLabel, 30))) . ')';
740 public function clLabel($key, $Akey = 'labels')
742 return htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:' . $Akey . '.' . $key));
750 protected function exportClipElementParameters()
753 $pad = $this->current;
755 $params['tx_impexp']['action'] = 'export';
757 if (is_array($this->clipData[$pad]['el'])) {
758 foreach ($this->clipData[$pad]['el'] as $k => $v) {
760 list($table, $uid) = explode('|', $k);
761 // Rendering files/directories on the clipboard
762 if ($table == '_FILE') {
763 if (file_exists($v) && GeneralUtility::isAllowedAbsPath($v)) {
764 $params['tx_impexp'][is_dir($v) ? 'dir' : 'file'][] = $v;
767 // Rendering records:
768 $rec = BackendUtility::getRecord($table, $uid);
769 if (is_array($rec)) {
770 $params['tx_impexp']['record'][] = $table . ':' . $uid;
779 /*****************************************
783 ****************************************/
790 public function removeElement($el)
792 unset($this->clipData[$this->current]['el'][$el]);
803 public function saveClipboard()
805 $this->getBackendUser()->pushModuleData('clipboard', $this->clipData);
813 public function currentMode()
815 return $this->clipData[$this->current]['mode'] == 'copy' ? 'copy' : 'cut';
824 public function cleanCurrent()
826 if (is_array($this->clipData[$this->current]['el'])) {
827 foreach ($this->clipData[$this->current]['el'] as $k => $v) {
828 list($table, $uid) = explode('|', $k);
829 if ($table != '_FILE') {
830 if (!$v || !is_array(BackendUtility::getRecord($table, $uid, 'uid'))) {
831 unset($this->clipData[$this->current]['el'][$k]);
836 unset($this->clipData[$this->current]['el'][$k]);
840 ResourceFactory::getInstance()->retrieveFileOrFolderObject($v);
841 } catch (\TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException $e) {
842 // The file has been deleted in the meantime, so just remove it silently
843 unset($this->clipData[$this->current]['el'][$k]);
858 public function elFromTable($matchTable = '', $pad = '')
860 $pad = $pad ? $pad : $this->current;
862 if (is_array($this->clipData[$pad]['el'])) {
863 foreach ($this->clipData[$pad]['el'] as $k => $v) {
865 list($table, $uid) = explode('|', $k);
866 if ($table != '_FILE') {
867 if ((!$matchTable || (string)$table == (string)$matchTable) && $GLOBALS['TCA'][$table]) {
868 $list[$k] = $pad == 'normal' ? $v : $uid;
871 if ((string)$table == (string)$matchTable) {
889 public function isSelected($table, $uid)
891 $k = $table . '|' . $uid;
892 return $this->clipData[$this->current]['el'][$k] ? ($this->current == 'normal' ? $this->currentMode() : 1) : '';
904 public function getSelectedRecord($table = '', $uid = '')
906 if (!$table && !$uid) {
907 $elArr = $this->elFromTable('');
909 list($table, $uid) = explode('|', key($elArr));
911 if ($this->isSelected($table, $uid)) {
912 $selRec = BackendUtility::getRecordWSOL($table, $uid);
913 $selRec['_RECORD_TITLE'] = BackendUtility::getRecordTitle($table, $selRec);
924 public function isElements()
926 return is_array($this->clipData[$this->current]['el']) && !empty($this->clipData[$this->current]['el']);
929 /*****************************************
931 * FOR USE IN tce_db.php:
933 ****************************************/
952 public function makePasteCmdArray($ref, $CMD, array $update = null)
954 list($pTable, $pUid) = explode('|', $ref);
956 // pUid must be set and if pTable is not set (that means paste ALL elements)
957 // the uid MUST be positive/zero (pointing to page id)
958 if ($pTable || $pUid >= 0) {
959 $elements = $this->elFromTable($pTable);
960 // So the order is preserved.
961 $elements = array_reverse($elements);
962 $mode = $this->currentMode() == 'copy' ? 'copy' : 'move';
963 // Traverse elements and make CMD array
964 foreach ($elements as $tP => $value) {
965 list($table, $uid) = explode('|', $tP);
966 if (!is_array($CMD[$table])) {
967 $CMD[$table] = array();
969 if (is_array($update)) {
970 $CMD[$table][$uid][$mode] = array(
976 $CMD[$table][$uid][$mode] = $pUid;
978 if ($mode == 'move') {
979 $this->removeElement($tP);
982 $this->endClipboard();
993 public function makeDeleteCmdArray($CMD)
996 $elements = $this->elFromTable('');
997 foreach ($elements as $tP => $value) {
998 list($table, $uid) = explode('|', $tP);
999 if (!is_array($CMD[$table])) {
1000 $CMD[$table] = array();
1002 $CMD[$table][$uid]['delete'] = 1;
1003 $this->removeElement($tP);
1005 $this->endClipboard();
1009 /*****************************************
1011 * FOR USE IN tce_file.php:
1013 ****************************************/
1022 public function makePasteCmdArray_file($ref, $FILE)
1024 list($pTable, $pUid) = explode('|', $ref);
1025 $elements = $this->elFromTable('_FILE');
1026 $mode = $this->currentMode() == 'copy' ? 'copy' : 'move';
1027 // Traverse elements and make CMD array
1028 foreach ($elements as $tP => $path) {
1029 $FILE[$mode][] = array('data' => $path, 'target' => $pUid);
1030 if ($mode == 'move') {
1031 $this->removeElement($tP);
1034 $this->endClipboard();
1044 public function makeDeleteCmdArray_file($FILE)
1046 $elements = $this->elFromTable('_FILE');
1047 // Traverse elements and make CMD array
1048 foreach ($elements as $tP => $path) {
1049 $FILE['delete'][] = array('data' => $path);
1050 $this->removeElement($tP);
1052 $this->endClipboard();
1061 protected function getLanguageService()
1063 return $GLOBALS['LANG'];
1071 protected function getBackendUser()
1073 return $GLOBALS['BE_USER'];
1081 protected function getDatabaseConnection()
1083 return $GLOBALS['TYPO3_DB'];