TYPO3  7.6
Clipboard.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Clipboard;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
26 
30 class Clipboard
31 {
35  public $numberTabs = 3;
36 
58  public $clipData = array();
59 
63  public $changed = 0;
64 
68  public $current = '';
69 
73  public $lockToNormal = 0;
74 
80  public $fileMode = 0;
81 
85  protected $iconFactory;
86 
90  public function __construct()
91  {
92  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
93  }
94 
95  /*****************************************
96  *
97  * Initialize
98  *
99  ****************************************/
105  public function initializeClipboard()
106  {
107  // Get data
108  $clipData = $this->getBackendUser()->getModuleData('clipboard', $this->getBackendUser()->getTSConfigVal('options.saveClipboard') ? '' : 'ses');
109  // NumberTabs
110  $clNP = $this->getBackendUser()->getTSConfigVal('options.clipboardNumberPads');
111  if (MathUtility::canBeInterpretedAsInteger($clNP) && $clNP >= 0) {
112  $this->numberTabs = MathUtility::forceIntegerInRange($clNP, 0, 20);
113  }
114  // Resets/reinstates the clipboard pads
115  $this->clipData['normal'] = is_array($clipData['normal']) ? $clipData['normal'] : array();
116  for ($a = 1; $a <= $this->numberTabs; $a++) {
117  $this->clipData['tab_' . $a] = is_array($clipData['tab_' . $a]) ? $clipData['tab_' . $a] : array();
118  }
119  // Setting the current pad pointer ($this->current))
120  $this->clipData['current'] = ($this->current = isset($this->clipData[$clipData['current']]) ? $clipData['current'] : 'normal');
121  }
122 
130  public function lockToNormal()
131  {
132  $this->lockToNormal = 1;
133  $this->current = 'normal';
134  }
135 
148  public function setCmd($cmd)
149  {
150  if (is_array($cmd['el'])) {
151  foreach ($cmd['el'] as $k => $v) {
152  if ($this->current == 'normal') {
153  unset($this->clipData['normal']);
154  }
155  if ($v) {
156  $this->clipData[$this->current]['el'][$k] = $v;
157  } else {
158  $this->removeElement($k);
159  }
160  $this->changed = 1;
161  }
162  }
163  // Change clipboard pad (if not locked to normal)
164  if ($cmd['setP']) {
165  $this->setCurrentPad($cmd['setP']);
166  }
167  // Remove element (value = item ident: DB; '[tablename]|[uid]' FILE: '_FILE|[shortmd5 hash of path]'
168  if ($cmd['remove']) {
169  $this->removeElement($cmd['remove']);
170  $this->changed = 1;
171  }
172  // Remove all on current pad (value = pad-ident)
173  if ($cmd['removeAll']) {
174  $this->clipData[$cmd['removeAll']] = array();
175  $this->changed = 1;
176  }
177  // Set copy mode of the tab
178  if (isset($cmd['setCopyMode'])) {
179  $this->clipData[$this->current]['mode'] = $this->isElements() ? ($cmd['setCopyMode'] ? 'copy' : '') : '';
180  $this->changed = 1;
181  }
182  }
183 
190  public function setCurrentPad($padIdent)
191  {
192  // Change clipboard pad (if not locked to normal)
193  if (!$this->lockToNormal && $this->current != $padIdent) {
194  if (isset($this->clipData[$padIdent])) {
195  $this->clipData['current'] = ($this->current = $padIdent);
196  }
197  if ($this->current != 'normal' || !$this->isElements()) {
198  $this->clipData[$this->current]['mode'] = '';
199  }
200  // Setting mode to default (move) if no items on it or if not 'normal'
201  $this->changed = 1;
202  }
203  }
204 
211  public function endClipboard()
212  {
213  if ($this->changed) {
214  $this->saveClipboard();
215  }
216  $this->changed = 0;
217  }
218 
227  public function cleanUpCBC($CBarr, $table, $removeDeselected = 0)
228  {
229  if (is_array($CBarr)) {
230  foreach ($CBarr as $k => $v) {
231  $p = explode('|', $k);
232  if ((string)$p[0] != (string)$table || $removeDeselected && !$v) {
233  unset($CBarr[$k]);
234  }
235  }
236  }
237  return $CBarr;
238  }
239 
240  /*****************************************
241  *
242  * Clipboard HTML renderings
243  *
244  ****************************************/
250  public function printClipboard()
251  {
252  $out = array();
253  $elementCount = count($this->elFromTable($this->fileMode ? '_FILE' : ''));
254  // Copymode Selector menu
255  $copymodeUrl = GeneralUtility::linkThisScript();
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'));
258 
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>
264  </button>
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>
268  </ul>
269  </div>
270  ';
271 
272  $deleteLink = '';
273  $menuSelector = '';
274  if ($elementCount) {
275  $removeAllUrl = GeneralUtility::linkThisScript(array('CB' => array('removeAll' => $this->current)));
276 
277  // Selector menu + clear button
278  $optionArray = array();
279  // Import / Export link:
280  if (ExtensionManagementUtility::isLoaded('impexp')) {
281  $url = BackendUtility::getModuleUrl('xMOD_tximpexp', $this->exportClipElementParameters());
282  $optionArray[] = '<li><a href="#" onclick="' . htmlspecialchars(('window.location.href=' . GeneralUtility::quoteJSvalue($url) . ';')) . '">' . $this->clLabel('export', 'rm') . '</a></li>';
283  }
284  // Edit:
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>';
287  }
288 
289  // Delete referenced elements:
290  if ($this->getBackendUser()->jsConfirmation(JsConfirmation::DELETE)) {
291  $js = '
292  if (confirm(' . GeneralUtility::quoteJSvalue(sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:mess.deleteClip'), $elementCount)) . ')){
293  window.location.href=' . GeneralUtility::quoteJSvalue($this->deleteUrl(0, ($this->fileMode ? 1 : 0)) . '&redirect=') . '+top.rawurlencode(window.location.href);
294  }
295  ';
296  } else {
297  $js = ' window.location.href=' . GeneralUtility::quoteJSvalue($this->deleteUrl(0, ($this->fileMode ? 1 : 0)) . '&redirect=') . '+top.rawurlencode(window.location.href); ';
298  }
299  $optionArray[] = '<li><a href="#" onclick="' . htmlspecialchars($js) . '">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.clipboard.delete_elements', true) . '</a></li>';
300 
301  // Clear clipboard
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>';
303  $deleteLink = '<a class="btn btn-danger" href="' . htmlspecialchars($removeAllUrl) . '#clip_head" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:buttons.clear', true) . '">' . $this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL)->render(SvgIconProvider::MARKUP_IDENTIFIER_INLINE) . '</a>';
304 
305  // menuSelector
306  $menuSelector = '
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>
311  </button>
312  <ul class="dropdown-menu" aria-labelledby="menuSelector">
313  ' . implode('', $optionArray) . '
314  </ul>
315  </div>
316  ';
317  }
318 
319  $out[] = '
320  <tr>
321  <td colspan="2" nowrap="nowrap" width="95%">' . $copymodeSelector . ' ' . $menuSelector . '</td>
322  <td nowrap="nowrap" class="col-control">' . $deleteLink . '</td>
323  </tr>';
324 
325  // Print header and content for the NORMAL tab:
326  // check for current item so it can be wrapped in strong tag
327  $current = ($this->current == 'normal');
328  $out[] = '
329  <tr>
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>'
332  . $this->padTitleWrap($this->clLabel('normal'), 'normal', $current)
333  . '</a></td>
334  </tr>';
335  if ($this->current == 'normal') {
336  $out = array_merge($out, $this->printContentFromTab('normal'));
337  }
338  // Print header and content for the NUMERIC tabs:
339  for ($a = 1; $a <= $this->numberTabs; $a++) {
340  // check for current item so it can be wrapped in strong tag
341  $current = ($this->current == 'tab_' . $a);
342  $out[] = '
343  <tr>
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>'
346  . $this->padTitleWrap(sprintf($this->clLabel('cliptabs-name'), $a), ('tab_' . $a), $current)
347  . '</a></td>
348  </tr>';
349  if ($this->current == 'tab_' . $a) {
350  $out = array_merge($out, $this->printContentFromTab('tab_' . $a));
351  }
352  }
353  // Wrap accumulated rows in a table:
354  $output = '<a name="clip_head"></a>
355 
356  <!--
357  TYPO3 Clipboard:
358  -->
359  <div class="row">
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) . '
365  </table>
366  </div>
367  </div>
368  </div>
369  ';
370  // Wrap in form tag:
371  $output = '<form action="" id="clipboard_form">' . $output . '</form>';
372  // Return the accumulated content:
373  return $output;
374  }
375 
383  public function printContentFromTab($pad)
384  {
385  $lines = array();
386  if (is_array($this->clipData[$pad]['el'])) {
387  foreach ($this->clipData[$pad]['el'] as $k => $v) {
388  if ($v) {
389  list($table, $uid) = explode('|', $k);
390  $bgColClass = $table == '_FILE' && $this->fileMode || $table != '_FILE' && !$this->fileMode ? 'bgColor4-20' : 'bgColor4';
391  // Rendering files/directories on the clipboard
392  if ($table == '_FILE') {
393  $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($v);
394  if ($fileObject) {
395  $thumb = '';
396  $folder = $fileObject instanceof \TYPO3\CMS\Core\Resource\Folder;
397  $size = $folder ? '' : '(' . GeneralUtility::formatSize($fileObject->getSize()) . 'bytes)';
398  $icon = '<span title="' . htmlspecialchars($fileObject->getName() . ' ' . $size) . '">' . $this->iconFactory->getIconForResource($fileObject, Icon::SIZE_SMALL)->render() . '</span>';
399  if (!$folder && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
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="" />';
407  }
408  }
409  $lines[] = '
410  <tr>
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>' : '') . '&nbsp;' . $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>
416  </div>
417  </td>
418  </tr>';
419  } else {
420  // If the file did not exist (or is illegal) then it is removed from the clipboard immediately:
421  unset($this->clipData[$pad]['el'][$k]);
422  $this->changed = 1;
423  }
424  } else {
425  // Rendering records:
426  $rec = BackendUtility::getRecordWSOL($table, $uid);
427  if (is_array($rec)) {
428  $lines[] = '
429  <tr>
430  <td nowrap="nowrap" class="col-icon">' . $this->linkItemText($this->iconFactory->getIconForRecord($table, $rec, Icon::SIZE_SMALL)->render(), $rec, $table) . '</td>
431  <td nowrap="nowrap" width="95%">' . $this->linkItemText(htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $this->getBackendUser()->uc['titleLen'])), $rec, $table) . ($pad == 'normal' ? ' <strong>(' . ($this->clipData['normal']['mode'] == 'copy' ? $this->clLabel('copy', 'cm') : $this->clLabel('cut', 'cm')) . ')</strong>' : '') . '&nbsp;</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>
435  </div>
436  </td>
437  </tr>';
438  $localizationData = $this->getLocalizations($table, $rec, $bgColClass, $pad);
439  if ($localizationData) {
440  $lines[] = $localizationData;
441  }
442  } else {
443  unset($this->clipData[$pad]['el'][$k]);
444  $this->changed = 1;
445  }
446  }
447  }
448  }
449  }
450  $this->endClipboard();
451  return $lines;
452  }
453 
459  public function hasElements()
460  {
461  foreach ($this->clipData as $data) {
462  if (isset($data['el']) && is_array($data['el']) && !empty($data['el'])) {
463  return true;
464  }
465  }
466 
467  return false;
468  }
469 
479  public function getLocalizations($table, $parentRec, $bgColClass, $pad)
480  {
481  $lines = array();
482  $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
483  if ($table != 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) {
484  $where = array();
485  $where[] = $tcaCtrl['transOrigPointerField'] . '=' . (int)$parentRec['uid'];
486  $where[] = $tcaCtrl['languageField'] . '<>0';
487  if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) {
488  $where[] = $tcaCtrl['delete'] . '=0';
489  }
490  if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) {
491  $where[] = 't3ver_wsid=' . $parentRec['t3ver_wsid'];
492  }
493  $rows = $this->getDatabaseConnection()->exec_SELECTgetRows('*', $table, implode(' AND ', $where));
494  if (is_array($rows)) {
495  $modeData = '';
496  if ($pad == 'normal') {
497  $mode = $this->clipData['normal']['mode'] == 'copy' ? 'copy' : 'cut';
498  $modeData = ' <strong>(' . $this->clLabel($mode, 'cm') . ')</strong>';
499  }
500  foreach ($rows as $rec) {
501  $lines[] = '
502  <tr>
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>
506  </tr>';
507  }
508  }
509  }
510  return implode('', $lines);
511  }
512 
522  public function padTitleWrap($str, $pad, $active)
523  {
524  $el = count($this->elFromTable($this->fileMode ? '_FILE' : '', $pad));
525  if ($el) {
526  $str .= ' (' . ($pad == 'normal' ? ($this->clipData['normal']['mode'] == 'copy' ? $this->clLabel('copy', 'cm') : $this->clLabel('cut', 'cm')) : htmlspecialchars($el)) . ')';
527  }
528  if ($active === true) {
529  return '<strong>' . $str . '</strong>';
530  } else {
531  return '<span class="text-muted">' . $str . '</span>';
532  }
533  }
534 
543  public function linkItemText($str, $rec, $table = '')
544  {
545  if (is_array($rec) && $table) {
546  if ($this->fileMode) {
547  $str = '<span class="text-muted">' . $str . '</span>';
548  } else {
549  $str = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_list', array('id' => $rec['pid']))) . '">' . $str . '</a>';
550  }
551  } elseif (file_exists($rec)) {
552  if (!$this->fileMode) {
553  $str = '<span class="text-muted">' . $str . '</span>';
554  } else {
555  if (ExtensionManagementUtility::isLoaded('filelist')) {
556  $str = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('file_list', array('id' => dirname($rec)))) . '">' . $str . '</a>';
557  }
558  }
559  }
560  return $str;
561  }
562 
573  public function selUrlDB($table, $uid, $copy = 0, $deselect = 0, $baseArray = array())
574  {
575  $CB = array('el' => array(rawurlencode($table . '|' . $uid) => $deselect ? 0 : 1));
576  if ($copy) {
577  $CB['setCopyMode'] = 1;
578  }
579  $baseArray['CB'] = $CB;
580  return GeneralUtility::linkThisScript($baseArray);
581  }
582 
592  public function selUrlFile($path, $copy = 0, $deselect = 0, $baseArray = array())
593  {
594  $CB = array('el' => array(rawurlencode('_FILE|' . GeneralUtility::shortmd5($path)) => $deselect ? '' : $path));
595  if ($copy) {
596  $CB['setCopyMode'] = 1;
597  }
598  $baseArray['CB'] = $CB;
599  return GeneralUtility::linkThisScript($baseArray);
600  }
601 
613  public function pasteUrl($table, $uid, $setRedirect = true, array $update = null)
614  {
615  $urlParameters = [
616  'vC' => $this->getBackendUser()->veriCode(),
617  'prErr' => 1,
618  'uPT' => 1,
619  'CB[paste]' => $table . '|' . $uid,
620  'CB[pad]' => $this->current
621  ];
622  if ($setRedirect) {
623  $urlParameters['redirect'] = GeneralUtility::linkThisScript(array('CB' => ''));
624  }
625  if (is_array($update)) {
626  $urlParameters['CB[update]'] = $update;
627  }
628  return BackendUtility::getModuleUrl($table === '_FILE' ? 'tce_file' : 'tce_db', $urlParameters);
629  }
630 
638  public function deleteUrl($setRedirect = 1, $file = 0)
639  {
640  $urlParameters = [
641  'vC' => $this->getBackendUser()->veriCode(),
642  'prErr' => 1,
643  'uPT' => 1,
644  'CB[delete]' => 1,
645  'CB[pad]' => $this->current
646  ];
647  if ($setRedirect) {
648  $urlParameters['redirect'] = GeneralUtility::linkThisScript(array('CB' => ''));
649  }
650  return BackendUtility::getModuleUrl($file ? 'tce_file' : 'tce_db', $urlParameters);
651  }
652 
660  public function editUrl()
661  {
662  $parameters = array();
663  // All records
664  $elements = $this->elFromTable('');
665  foreach ($elements as $tP => $value) {
666  list($table, $uid) = explode('|', $tP);
667  $parameters['edit[' . $table . '][' . $uid . ']'] = 'edit';
668  }
669  return BackendUtility::getModuleUrl('record_edit', $parameters);
670  }
671 
680  public function removeUrl($table, $uid)
681  {
682  return GeneralUtility::linkThisScript(array('CB' => array('remove' => $table . '|' . $uid)));
683  }
684 
695  public function confirmMsg($table, $rec, $type, $clElements, $columnLabel = '')
696  {
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);
705  } else {
706  $selRecTitle = count($clElements);
707  }
708  } else {
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'];
713  } else {
714  $selRecTitle = count($clElements);
715  }
716  }
717  // @TODO
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);
723  }
724 
725  // Message
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))) . ')';
727  } else {
728  $conf = '';
729  }
730  return $conf;
731  }
732 
740  public function clLabel($key, $Akey = 'labels')
741  {
742  return htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:' . $Akey . '.' . $key));
743  }
744 
750  protected function exportClipElementParameters()
751  {
752  // Init
753  $pad = $this->current;
754  $params = array();
755  $params['tx_impexp']['action'] = 'export';
756  // Traverse items:
757  if (is_array($this->clipData[$pad]['el'])) {
758  foreach ($this->clipData[$pad]['el'] as $k => $v) {
759  if ($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;
765  }
766  } else {
767  // Rendering records:
768  $rec = BackendUtility::getRecord($table, $uid);
769  if (is_array($rec)) {
770  $params['tx_impexp']['record'][] = $table . ':' . $uid;
771  }
772  }
773  }
774  }
775  }
776  return $params;
777  }
778 
779  /*****************************************
780  *
781  * Helper functions
782  *
783  ****************************************/
790  public function removeElement($el)
791  {
792  unset($this->clipData[$this->current]['el'][$el]);
793  $this->changed = 1;
794  }
795 
803  public function saveClipboard()
804  {
805  $this->getBackendUser()->pushModuleData('clipboard', $this->clipData);
806  }
807 
813  public function currentMode()
814  {
815  return $this->clipData[$this->current]['mode'] == 'copy' ? 'copy' : 'cut';
816  }
817 
824  public function cleanCurrent()
825  {
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]);
832  $this->changed = 1;
833  }
834  } else {
835  if (!$v) {
836  unset($this->clipData[$this->current]['el'][$k]);
837  $this->changed = 1;
838  } else {
839  try {
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]);
844  }
845  }
846  }
847  }
848  }
849  }
850 
858  public function elFromTable($matchTable = '', $pad = '')
859  {
860  $pad = $pad ? $pad : $this->current;
861  $list = array();
862  if (is_array($this->clipData[$pad]['el'])) {
863  foreach ($this->clipData[$pad]['el'] as $k => $v) {
864  if ($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;
869  }
870  } else {
871  if ((string)$table == (string)$matchTable) {
872  $list[$k] = $v;
873  }
874  }
875  }
876  }
877  }
878  return $list;
879  }
880 
889  public function isSelected($table, $uid)
890  {
891  $k = $table . '|' . $uid;
892  return $this->clipData[$this->current]['el'][$k] ? ($this->current == 'normal' ? $this->currentMode() : 1) : '';
893  }
894 
904  public function getSelectedRecord($table = '', $uid = '')
905  {
906  if (!$table && !$uid) {
907  $elArr = $this->elFromTable('');
908  reset($elArr);
909  list($table, $uid) = explode('|', key($elArr));
910  }
911  if ($this->isSelected($table, $uid)) {
912  $selRec = BackendUtility::getRecordWSOL($table, $uid);
913  $selRec['_RECORD_TITLE'] = BackendUtility::getRecordTitle($table, $selRec);
914  return $selRec;
915  }
916  return array();
917  }
918 
924  public function isElements()
925  {
926  return is_array($this->clipData[$this->current]['el']) && !empty($this->clipData[$this->current]['el']);
927  }
928 
929  /*****************************************
930  *
931  * FOR USE IN tce_db.php:
932  *
933  ****************************************/
952  public function makePasteCmdArray($ref, $CMD, array $update = null)
953  {
954  list($pTable, $pUid) = explode('|', $ref);
955  $pUid = (int)$pUid;
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();
968  }
969  if (is_array($update)) {
970  $CMD[$table][$uid][$mode] = array(
971  'action' => 'paste',
972  'target' => $pUid,
973  'update' => $update,
974  );
975  } else {
976  $CMD[$table][$uid][$mode] = $pUid;
977  }
978  if ($mode == 'move') {
979  $this->removeElement($tP);
980  }
981  }
982  $this->endClipboard();
983  }
984  return $CMD;
985  }
986 
993  public function makeDeleteCmdArray($CMD)
994  {
995  // all records
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();
1001  }
1002  $CMD[$table][$uid]['delete'] = 1;
1003  $this->removeElement($tP);
1004  }
1005  $this->endClipboard();
1006  return $CMD;
1007  }
1008 
1009  /*****************************************
1010  *
1011  * FOR USE IN tce_file.php:
1012  *
1013  ****************************************/
1022  public function makePasteCmdArray_file($ref, $FILE)
1023  {
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);
1032  }
1033  }
1034  $this->endClipboard();
1035  return $FILE;
1036  }
1037 
1044  public function makeDeleteCmdArray_file($FILE)
1045  {
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);
1051  }
1052  $this->endClipboard();
1053  return $FILE;
1054  }
1055 
1061  protected function getLanguageService()
1062  {
1063  return $GLOBALS['LANG'];
1064  }
1065 
1071  protected function getBackendUser()
1072  {
1073  return $GLOBALS['BE_USER'];
1074  }
1075 
1081  protected function getDatabaseConnection()
1082  {
1083  return $GLOBALS['TYPO3_DB'];
1084  }
1085 }