70668d8f4ca2104182dc96c7e174a09a16a63dc9
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Clipboard / Clipboard.php
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
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Backend\Utility\IconUtility;
19 use TYPO3\CMS\Core\Resource\ResourceFactory;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
22 use TYPO3\CMS\Core\Utility\MathUtility;
23
24 /**
25 * TYPO3 clipboard for records and files
26 *
27 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
28 */
29 class Clipboard {
30
31 /**
32 * @todo Define visibility
33 */
34 public $numberTabs = 3;
35
36 /**
37 * Clipboard data kept here
38 *
39 * Keys:
40 * 'normal'
41 * 'tab_[x]' where x is >=1 and denotes the pad-number
42 * 'mode' : 'copy' means copy-mode, default = moving ('cut')
43 * 'el' : Array of elements:
44 * DB: keys = '[tablename]|[uid]' eg. 'tt_content:123'
45 * DB: values = 1 (basically insignificant)
46 * FILE: keys = '_FILE|[shortmd5 of path]' eg. '_FILE|9ebc7e5c74'
47 * FILE: values = The full filepath, eg. '/www/htdocs/typo3/32/dummy/fileadmin/sem1_3_examples/alternative_index.php'
48 * or 'C:/www/htdocs/typo3/32/dummy/fileadmin/sem1_3_examples/alternative_index.php'
49 *
50 * 'current' pointer to current tab (among the above...)
51 * '_setThumb' boolean: If set, file thumbnails are shown.
52 *
53 * The virtual tablename '_FILE' will always indicate files/folders. When checking for elements from eg. 'all tables'
54 * (by using an empty string) '_FILE' entries are excluded (so in effect only DB elements are counted)
55 *
56 * @todo Define visibility
57 */
58 public $clipData = array();
59
60 /**
61 * @todo Define visibility
62 */
63 public $changed = 0;
64
65 /**
66 * @todo Define visibility
67 */
68 public $current = '';
69
70 /**
71 * @todo Define visibility
72 */
73 public $backPath = '';
74
75 /**
76 * @todo Define visibility
77 */
78 public $lockToNormal = 0;
79
80 // If set, clipboard is displaying files.
81 /**
82 * @todo Define visibility
83 */
84 public $fileMode = 0;
85
86 /*****************************************
87 *
88 * Initialize
89 *
90 ****************************************/
91 /**
92 * Initialize the clipboard from the be_user session
93 *
94 * @return void
95 * @todo Define visibility
96 */
97 public function initializeClipboard() {
98 $this->backPath = $GLOBALS['BACK_PATH'];
99 // Get data
100 $clipData = $GLOBALS['BE_USER']->getModuleData('clipboard', $GLOBALS['BE_USER']->getTSConfigVal('options.saveClipboard') ? '' : 'ses');
101 // NumberTabs
102 $clNP = $GLOBALS['BE_USER']->getTSConfigVal('options.clipboardNumberPads');
103 if (MathUtility::canBeInterpretedAsInteger($clNP) && $clNP >= 0) {
104 $this->numberTabs = MathUtility::forceIntegerInRange($clNP, 0, 20);
105 }
106 // Resets/reinstates the clipboard pads
107 $this->clipData['normal'] = is_array($clipData['normal']) ? $clipData['normal'] : array();
108 for ($a = 1; $a <= $this->numberTabs; $a++) {
109 $this->clipData['tab_' . $a] = is_array($clipData['tab_' . $a]) ? $clipData['tab_' . $a] : array();
110 }
111 // Setting the current pad pointer ($this->current) and _setThumb (which determines whether or not do show file thumbnails)
112 $this->clipData['current'] = ($this->current = isset($this->clipData[$clipData['current']]) ? $clipData['current'] : 'normal');
113 $this->clipData['_setThumb'] = $clipData['_setThumb'];
114 }
115
116 /**
117 * Call this method after initialization if you want to lock the clipboard to operate on the normal pad only.
118 * Trying to switch pad through ->setCmd will not work.
119 * This is used by the clickmenu since it only allows operation on single elements at a time (that is the "normal" pad)
120 *
121 * @return void
122 * @todo Define visibility
123 */
124 public function lockToNormal() {
125 $this->lockToNormal = 1;
126 $this->current = 'normal';
127 }
128
129 /**
130 * The array $cmd may hold various keys which notes some action to take.
131 * Normally perform only one action at a time.
132 * In scripts like db_list.php / filelist/mod1/index.php the GET-var CB is used to control the clipboard.
133 *
134 * Selecting / Deselecting elements
135 * Array $cmd['el'] has keys = element-ident, value = element value (see description of clipData array in header)
136 * Selecting elements for 'copy' should be done by simultaneously setting setCopyMode.
137 *
138 * @param array $cmd Array of actions, see function description
139 * @return void
140 * @todo Define visibility
141 */
142 public function setCmd($cmd) {
143 if (is_array($cmd['el'])) {
144 foreach ($cmd['el'] as $k => $v) {
145 if ($this->current == 'normal') {
146 unset($this->clipData['normal']);
147 }
148 if ($v) {
149 $this->clipData[$this->current]['el'][$k] = $v;
150 } else {
151 $this->removeElement($k);
152 }
153 $this->changed = 1;
154 }
155 }
156 // Change clipboard pad (if not locked to normal)
157 if ($cmd['setP']) {
158 $this->setCurrentPad($cmd['setP']);
159 }
160 // Remove element (value = item ident: DB; '[tablename]|[uid]' FILE: '_FILE|[shortmd5 hash of path]'
161 if ($cmd['remove']) {
162 $this->removeElement($cmd['remove']);
163 $this->changed = 1;
164 }
165 // Remove all on current pad (value = pad-ident)
166 if ($cmd['removeAll']) {
167 $this->clipData[$cmd['removeAll']] = array();
168 $this->changed = 1;
169 }
170 // Set copy mode of the tab
171 if (isset($cmd['setCopyMode'])) {
172 $this->clipData[$this->current]['mode'] = $this->isElements() ? ($cmd['setCopyMode'] ? 'copy' : '') : '';
173 $this->changed = 1;
174 }
175 // Toggle thumbnail display for files on/off
176 if (isset($cmd['setThumb'])) {
177 $this->clipData['_setThumb'] = $cmd['setThumb'];
178 $this->changed = 1;
179 }
180 }
181
182 /**
183 * Setting the current pad on clipboard
184 *
185 * @param string $padIdent Key in the array $this->clipData
186 * @return void
187 * @todo Define visibility
188 */
189 public function setCurrentPad($padIdent) {
190 // Change clipboard pad (if not locked to normal)
191 if (!$this->lockToNormal && $this->current != $padIdent) {
192 if (isset($this->clipData[$padIdent])) {
193 $this->clipData['current'] = ($this->current = $padIdent);
194 }
195 if ($this->current != 'normal' || !$this->isElements()) {
196 $this->clipData[$this->current]['mode'] = '';
197 }
198 // Setting mode to default (move) if no items on it or if not 'normal'
199 $this->changed = 1;
200 }
201 }
202
203 /**
204 * Call this after initialization and setCmd in order to save the clipboard to the user session.
205 * The function will check if the internal flag ->changed has been set and if so, save the clipboard. Else not.
206 *
207 * @return void
208 * @todo Define visibility
209 */
210 public function endClipboard() {
211 if ($this->changed) {
212 $this->saveClipboard();
213 }
214 $this->changed = 0;
215 }
216
217 /**
218 * Cleans up an incoming element array $CBarr (Array selecting/deselecting elements)
219 *
220 * @param array $CBarr Element array from outside ("key" => "selected/deselected")
221 * @param string $table The 'table which is allowed'. Must be set.
222 * @param boolean $removeDeselected Can be set in order to remove entries which are marked for deselection.
223 * @return array Processed input $CBarr
224 * @todo Define visibility
225 */
226 public function cleanUpCBC($CBarr, $table, $removeDeselected = 0) {
227 if (is_array($CBarr)) {
228 foreach ($CBarr as $k => $v) {
229 $p = explode('|', $k);
230 if ((string) $p[0] != (string) $table || $removeDeselected && !$v) {
231 unset($CBarr[$k]);
232 }
233 }
234 }
235 return $CBarr;
236 }
237
238 /*****************************************
239 *
240 * Clipboard HTML renderings
241 *
242 ****************************************/
243 /**
244 * Prints the clipboard
245 *
246 * @return string HTML output
247 * @todo Define visibility
248 */
249 public function printClipboard() {
250 $out = array();
251 $elCount = count($this->elFromTable($this->fileMode ? '_FILE' : ''));
252 // Upper header
253 $out[] = '
254 <tr class="t3-row-header">
255 <td colspan="3">' . BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_clipboard', $this->clLabel('clipboard', 'buttons')) . '</td>
256 </tr>';
257 // Button/menu header:
258 $thumb_url = GeneralUtility::linkThisScript(array('CB' => array('setThumb' => $this->clipData['_setThumb'] ? 0 : 1)));
259 $rmall_url = GeneralUtility::linkThisScript(array('CB' => array('removeAll' => $this->current)));
260 // Copymode Selector menu
261 $copymode_url = GeneralUtility::linkThisScript();
262 $moveLabel = htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xlf:moveElements'));
263 $copyLabel = htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_misc.xlf:copyElements'));
264 $opt = array();
265 $opt[] = '<option style="padding-left: 20px; background-image: url(\'' . IconUtility::skinImg($this->backPath, 'gfx/clip_cut.gif', '', 1) . '\'); background-repeat: no-repeat;" value="" ' . ($this->currentMode() == 'copy' ? '' : 'selected="selected"') . '>' . $moveLabel . '</option>';
266 $opt[] = '<option style="padding-left: 20px; background-image: url(\'' . IconUtility::skinImg($this->backPath, 'gfx/clip_copy.gif', '', 1) . '\'); background-repeat: no-repeat;" value="1" ' . ($this->currentMode() == 'copy' ? 'selected="selected"' : '') . '>' . $copyLabel . '</option>';
267 $copymode_selector = ' <select name="CB[setCopyMode]" onchange="this.form.method=\'POST\'; this.form.action=\'' . htmlspecialchars(($copymode_url . '&CB[setCopyMode]=')) . '\'+(this.options[this.selectedIndex].value); this.form.submit(); return true;" >' . implode('', $opt) . '</select>';
268 // Selector menu + clear button
269 $opt = array();
270 $opt[] = '<option value="" selected="selected">' . $this->clLabel('menu', 'rm') . '</option>';
271 // Import / Export link:
272 if ($elCount && ExtensionManagementUtility::isLoaded('impexp')) {
273 $url = BackendUtility::getModuleUrl('xMOD_tximpexp', $this->exportClipElementParameters());
274 $opt[] = '<option value="' . htmlspecialchars(('window.location.href=' . GeneralUtility::quoteJSvalue($url) . ';')) . '">' . $this->clLabel('export', 'rm') . '</option>';
275 }
276 // Edit:
277 if (!$this->fileMode && $elCount) {
278 $opt[] = '<option value="' . htmlspecialchars(('window.location.href=\'' . $this->editUrl() . '&returnUrl=\'+top.rawurlencode(window.location.href);')) . '">' . $this->clLabel('edit', 'rm') . '</option>';
279 }
280 // Delete:
281 if ($elCount) {
282 if ($GLOBALS['BE_USER']->jsConfirmation(4)) {
283 $js = '
284 if (confirm(' . GeneralUtility::quoteJSvalue(sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.deleteClip'), $elCount)) . ')){
285 window.location.href=\'' . $this->deleteUrl(0, ($this->fileMode ? 1 : 0)) . '&redirect=\'+top.rawurlencode(window.location.href);
286 }
287 ';
288 } else {
289 $js = ' window.location.href=\'' . $this->deleteUrl(0, ($this->fileMode ? 1 : 0)) . '&redirect=\'+top.rawurlencode(window.location.href); ';
290 }
291 $opt[] = '<option value="' . htmlspecialchars($js) . '">' . $this->clLabel('delete', 'rm') . '</option>';
292 }
293 $selector_menu = '<select name="_clipMenu" onchange="eval(this.options[this.selectedIndex].value);this.selectedIndex=0;">' . implode('', $opt) . '</select>';
294 $out[] = '
295 <tr class="typo3-clipboard-head">
296 <td nowrap="nowrap">' . '<a href="' . htmlspecialchars($thumb_url) . '#clip_head">' . '<img' . IconUtility::skinImg($this->backPath, ('gfx/thumb_' . ($this->clipData['_setThumb'] ? 's' : 'n') . '.gif'), 'width="21" height="16"') . ' vspace="2" border="0" title="' . $this->clLabel('thumbmode_clip') . '" alt="" />' . '</a>' . '</td>
297 <td width="95%" nowrap="nowrap">' . $copymode_selector . ' ' . $selector_menu . '</td>
298 <td>' . '<a href="' . htmlspecialchars($rmall_url) . '#clip_head">' . IconUtility::getSpriteIcon('actions-document-close', array('title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:buttons.clear', TRUE))) . '</a></td>
299 </tr>';
300 // Print header and content for the NORMAL tab:
301 $out[] = '
302 <tr class="bgColor5">
303 <td colspan="3"><a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('CB' => array('setP' => 'normal')))) . '#clip_head" title="' . $this->clLabel('normal-description') . '">'
304 . IconUtility::getSpriteIcon(('actions-view-table-' . ($this->current == 'normal' ? 'collapse' : 'expand')))
305 . $this->padTitleWrap($this->clLabel('normal'), 'normal')
306 . '</a></td>
307 </tr>';
308 if ($this->current == 'normal') {
309 $out = array_merge($out, $this->printContentFromTab('normal'));
310 }
311 // Print header and content for the NUMERIC tabs:
312 for ($a = 1; $a <= $this->numberTabs; $a++) {
313 $out[] = '
314 <tr class="bgColor5">
315 <td colspan="3"><a href="' . htmlspecialchars(GeneralUtility::linkThisScript(array('CB' => array('setP' => ('tab_' . $a))))) . '#clip_head" title="' . $this->clLabel('cliptabs-description') . '">'
316 . IconUtility::getSpriteIcon(('actions-view-table-' . ($this->current == 'tab_' . $a ? 'collapse' : 'expand')))
317 . $this->padTitleWrap(sprintf($this->clLabel('cliptabs-name'), $a), ('tab_' . $a))
318 . '</a></td>
319 </tr>';
320 if ($this->current == 'tab_' . $a) {
321 $out = array_merge($out, $this->printContentFromTab('tab_' . $a));
322 }
323 }
324 // Wrap accumulated rows in a table:
325 $output = '<a name="clip_head"></a>
326
327 <!--
328 TYPO3 Clipboard:
329 -->
330 <table cellpadding="0" cellspacing="1" border="0" width="290" id="typo3-clipboard">
331 ' . implode('', $out) . '
332 </table>';
333 // Wrap in form tag:
334 $output = '<form action="">' . $output . '</form>';
335 // Return the accumulated content:
336 return $output;
337 }
338
339 /**
340 * Print the content on a pad. Called from ->printClipboard()
341 *
342 * @access private
343 * @param string $pad Pad reference
344 * @return array Array with table rows for the clipboard.
345 * @todo Define visibility
346 */
347 public function printContentFromTab($pad) {
348 $lines = array();
349 if (is_array($this->clipData[$pad]['el'])) {
350 foreach ($this->clipData[$pad]['el'] as $k => $v) {
351 if ($v) {
352 list($table, $uid) = explode('|', $k);
353 $bgColClass = $table == '_FILE' && $this->fileMode || $table != '_FILE' && !$this->fileMode ? 'bgColor4-20' : 'bgColor4';
354 // Rendering files/directories on the clipboard
355 if ($table == '_FILE') {
356 $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($v);
357 if ($fileObject) {
358 $thumb = '';
359 $folder = $fileObject instanceof \TYPO3\CMS\Core\Resource\Folder;
360 $size = $folder ? '' : '(' . GeneralUtility::formatSize($fileObject->getSize()) . 'bytes)';
361 $icon = IconUtility::getSpriteIconForResource($fileObject, array('style' => 'margin: 0 20px;', 'title' => $fileObject->getName() . ' ' . $size));
362 if (!$folder && $this->clipData['_setThumb'] && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
363 $processedFile = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, array());
364 if ($processedFile) {
365 $thumbUrl = $processedFile->getPublicUrl(TRUE);
366 $thumb .= '<br /><img src="' . htmlspecialchars($thumbUrl) . '" title="' . htmlspecialchars($fileObject->getName()) . '" alt="" />';
367 }
368 }
369 $lines[] = '
370 <tr>
371 <td class="' . $bgColClass . '">' . $icon . '</td>
372 <td class="' . $bgColClass . '" nowrap="nowrap" width="95%">&nbsp;' . $this->linkItemText(htmlspecialchars(GeneralUtility::fixed_lgd_cs($fileObject->getName(), $GLOBALS['BE_USER']->uc['titleLen'])), $fileObject->getName()) . ($pad == 'normal' ? ' <strong>(' . ($this->clipData['normal']['mode'] == 'copy' ? $this->clLabel('copy', 'cm') : $this->clLabel('cut', 'cm')) . ')</strong>' : '') . '&nbsp;' . $thumb . '</td>
373 <td class="' . $bgColClass . '" align="center" nowrap="nowrap">' . '<a href="#" onclick="' . htmlspecialchars(('top.launchView(\'' . $table . '\', \'' . $v . '\'); return false;')) . '">' . IconUtility::getSpriteIcon('actions-document-info', array('title' => $this->clLabel('info', 'cm'))) . '</a>' . '<a href="' . htmlspecialchars($this->removeUrl('_FILE', GeneralUtility::shortmd5($v))) . '#clip_head">' . IconUtility::getSpriteIcon('actions-selection-delete', array('title' => $this->clLabel('removeItem'))) . '</a>' . '</td>
374 </tr>';
375 } else {
376 // If the file did not exist (or is illegal) then it is removed from the clipboard immediately:
377 unset($this->clipData[$pad]['el'][$k]);
378 $this->changed = 1;
379 }
380 } else {
381 // Rendering records:
382 $rec = BackendUtility::getRecordWSOL($table, $uid);
383 if (is_array($rec)) {
384 $lines[] = '
385 <tr>
386 <td class="' . $bgColClass . '">' . $this->linkItemText(IconUtility::getSpriteIconForRecord($table, $rec, array('style' => 'margin: 0 20px;', 'title' => htmlspecialchars(BackendUtility::getRecordIconAltText($rec, $table)))), $rec, $table) . '</td>
387 <td class="' . $bgColClass . '" nowrap="nowrap" width="95%">&nbsp;' . $this->linkItemText(htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $GLOBALS['BE_USER']->uc['titleLen'])), $rec, $table) . ($pad == 'normal' ? ' <strong>(' . ($this->clipData['normal']['mode'] == 'copy' ? $this->clLabel('copy', 'cm') : $this->clLabel('cut', 'cm')) . ')</strong>' : '') . '&nbsp;</td>
388 <td class="' . $bgColClass . '" align="center" nowrap="nowrap">' . '<a href="#" onclick="' . htmlspecialchars(('top.launchView(\'' . $table . '\', \'' . (int)$uid . '\'); return false;')) . '">' . IconUtility::getSpriteIcon('actions-document-info', array('title' => $this->clLabel('info', 'cm'))) . '</a>' . '<a href="' . htmlspecialchars($this->removeUrl($table, $uid)) . '#clip_head">' . IconUtility::getSpriteIcon('actions-selection-delete', array('title' => $this->clLabel('removeItem'))) . '</a>' . '</td>
389 </tr>';
390 $localizationData = $this->getLocalizations($table, $rec, $bgColClass, $pad);
391 if ($localizationData) {
392 $lines[] = $localizationData;
393 }
394 } else {
395 unset($this->clipData[$pad]['el'][$k]);
396 $this->changed = 1;
397 }
398 }
399 }
400 }
401 }
402 if (!count($lines)) {
403 $lines[] = '
404 <tr>
405 <td class="bgColor4"><img src="clear.gif" width="56" height="1" alt="" /></td>
406 <td colspan="2" class="bgColor4" nowrap="nowrap" width="95%">&nbsp;<em>(' . $this->clLabel('clipNoEl') . ')</em>&nbsp;</td>
407 </tr>';
408 }
409 $this->endClipboard();
410 return $lines;
411 }
412
413 /**
414 * Returns true if the clipboard contains elements
415 *
416 * @return boolean
417 */
418 public function hasElements() {
419 foreach ($this->clipData as $data) {
420 if (isset($data['el']) && is_array($data['el']) && !empty($data['el'])) {
421 return TRUE;
422 }
423 }
424
425 return FALSE;
426 }
427
428 /**
429 * Gets all localizations of the current record.
430 *
431 * @param string $table The table
432 * @param array $parentRec The current record
433 * @param string $bgColClass Class for the background color of a column
434 * @param string $pad Pad reference
435 * @return string HTML table rows
436 * @todo Define visibility
437 */
438 public function getLocalizations($table, $parentRec, $bgColClass, $pad) {
439 $lines = array();
440 $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
441 if ($table != 'pages' && BackendUtility::isTableLocalizable($table) && !$tcaCtrl['transOrigPointerTable']) {
442 $where = array();
443 $where[] = $tcaCtrl['transOrigPointerField'] . '=' . (int)$parentRec['uid'];
444 $where[] = $tcaCtrl['languageField'] . '<>0';
445 if (isset($tcaCtrl['delete']) && $tcaCtrl['delete']) {
446 $where[] = $tcaCtrl['delete'] . '=0';
447 }
448 if (isset($tcaCtrl['versioningWS']) && $tcaCtrl['versioningWS']) {
449 $where[] = 't3ver_wsid=' . $parentRec['t3ver_wsid'];
450 }
451 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*', $table, implode(' AND ', $where));
452 if (is_array($rows)) {
453 $modeData = '';
454 if ($pad == 'normal') {
455 $mode = $this->clipData['normal']['mode'] == 'copy' ? 'copy' : 'cut';
456 $modeData = ' <strong>(' . $this->clLabel($mode, 'cm') . ')</strong>';
457 }
458 foreach ($rows as $rec) {
459 $lines[] = '
460 <tr>
461 <td class="' . $bgColClass . '">' . IconUtility::getSpriteIconForRecord($table, $rec, array('style' => 'margin-left: 38px;')) . '</td>
462 <td class="' . $bgColClass . '" nowrap="nowrap" width="95%">&nbsp;' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $rec), $GLOBALS['BE_USER']->uc['titleLen'])) . $modeData . '&nbsp;</td>
463 <td class="' . $bgColClass . '" align="center" nowrap="nowrap">&nbsp;</td>
464 </tr>';
465 }
466 }
467 }
468 return implode('', $lines);
469 }
470
471 /**
472 * Wraps title of pad in bold-tags and maybe the number of elements if any.
473 *
474 * @param string $str String (already htmlspecialchars()'ed)
475 * @param string $pad Pad reference
476 * @return string HTML output (htmlspecialchar'ed content inside of tags.)
477 * @todo Define visibility
478 */
479 public function padTitleWrap($str, $pad) {
480 $el = count($this->elFromTable($this->fileMode ? '_FILE' : '', $pad));
481 if ($el) {
482 return '<strong>' . $str . '</strong> (' . ($pad == 'normal' ? ($this->clipData['normal']['mode'] == 'copy' ? $this->clLabel('copy', 'cm') : $this->clLabel('cut', 'cm')) : htmlspecialchars($el)) . ')';
483 } else {
484 return $GLOBALS['TBE_TEMPLATE']->dfw($str);
485 }
486 }
487
488 /**
489 * Wraps the title of the items listed in link-tags. The items will link to the page/folder where they originate from
490 *
491 * @param string $str Title of element - must be htmlspecialchar'ed on beforehand.
492 * @param mixed $rec If array, a record is expected. If string, its a path
493 * @param string $table Table name
494 * @return string
495 * @todo Define visibility
496 */
497 public function linkItemText($str, $rec, $table = '') {
498 if (is_array($rec) && $table) {
499 if ($this->fileMode) {
500 $str = $GLOBALS['TBE_TEMPLATE']->dfw($str);
501 } else {
502 $str = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_list', array('id' => $rec['pid']), $this->backPath)) . '">' . $str . '</a>';
503 }
504 } elseif (file_exists($rec)) {
505 if (!$this->fileMode) {
506 $str = $GLOBALS['TBE_TEMPLATE']->dfw($str);
507 } else {
508 if (ExtensionManagementUtility::isLoaded('filelist')) {
509 $str = '<a href="' . htmlspecialchars(($this->backPath . BackendUtility::getModuleUrl('file_list') . '&id=' . dirname($rec))) . '">' . $str . '</a>';
510 }
511 }
512 }
513 return $str;
514 }
515
516 /**
517 * Returns the select-url for database elements
518 *
519 * @param string $table Table name
520 * @param integer $uid Uid of record
521 * @param boolean $copy If set, copymode will be enabled
522 * @param boolean $deselect If set, the link will deselect, otherwise select.
523 * @param array $baseArray The base array of GET vars to be sent in addition. Notice that current GET vars WILL automatically be included.
524 * @return string URL linking to the current script but with the CB array set to select the element with table/uid
525 * @todo Define visibility
526 */
527 public function selUrlDB($table, $uid, $copy = 0, $deselect = 0, $baseArray = array()) {
528 $CB = array('el' => array(rawurlencode($table . '|' . $uid) => $deselect ? 0 : 1));
529 if ($copy) {
530 $CB['setCopyMode'] = 1;
531 }
532 $baseArray['CB'] = $CB;
533 return GeneralUtility::linkThisScript($baseArray);
534 }
535
536 /**
537 * Returns the select-url for files
538 *
539 * @param string $path Filepath
540 * @param boolean $copy If set, copymode will be enabled
541 * @param boolean $deselect If set, the link will deselect, otherwise select.
542 * @param array $baseArray The base array of GET vars to be sent in addition. Notice that current GET vars WILL automatically be included.
543 * @return string URL linking to the current script but with the CB array set to select the path
544 * @todo Define visibility
545 */
546 public function selUrlFile($path, $copy = 0, $deselect = 0, $baseArray = array()) {
547 $CB = array('el' => array(rawurlencode('_FILE|' . GeneralUtility::shortmd5($path)) => $deselect ? '' : $path));
548 if ($copy) {
549 $CB['setCopyMode'] = 1;
550 }
551 $baseArray['CB'] = $CB;
552 return GeneralUtility::linkThisScript($baseArray);
553 }
554
555 /**
556 * pasteUrl of the element (database and file)
557 * For the meaning of $table and $uid, please read from ->makePasteCmdArray!!!
558 * The URL will point to tce_file or tce_db depending in $table
559 *
560 * @param string $table Tablename (_FILE for files)
561 * @param mixed $uid "destination": can be positive or negative indicating how the paste is done (paste into / paste after)
562 * @param boolean $setRedirect If set, then the redirect URL will point back to the current script, but with CB reset.
563 * @param array|NULL $update Additional key/value pairs which should get set in the moved/copied record (via DataHandler)
564 * @return string
565 * @todo Define visibility
566 */
567 public function pasteUrl($table, $uid, $setRedirect = TRUE, array $update = NULL) {
568 $rU = $this->backPath . ($table == '_FILE' ? 'tce_file.php' : 'tce_db.php') . '?' .
569 ($setRedirect ? 'redirect=' . rawurlencode(GeneralUtility::linkThisScript(array('CB' => ''))) : '') .
570 '&vC=' . $GLOBALS['BE_USER']->veriCode() .
571 '&prErr=1&uPT=1' .
572 '&CB[paste]=' . rawurlencode($table . '|' . $uid) .
573 '&CB[pad]=' . $this->current .
574 (is_array($update) ? GeneralUtility::implodeArrayForUrl('CB[update]', $update) : '') .
575 BackendUtility::getUrlToken('tceAction');
576 return $rU;
577 }
578
579 /**
580 * deleteUrl for current pad
581 *
582 * @param boolean $setRedirect If set, then the redirect URL will point back to the current script, but with CB reset.
583 * @param boolean $file If set, then the URL will link to the tce_file.php script in the typo3/ dir.
584 * @return string
585 * @todo Define visibility
586 */
587 public function deleteUrl($setRedirect = 1, $file = 0) {
588 $rU = $this->backPath . ($file ? 'tce_file.php' : 'tce_db.php') . '?' . ($setRedirect ? 'redirect=' . rawurlencode(GeneralUtility::linkThisScript(array('CB' => ''))) : '') . '&vC=' . $GLOBALS['BE_USER']->veriCode() . '&prErr=1&uPT=1' . '&CB[delete]=1' . '&CB[pad]=' . $this->current . BackendUtility::getUrlToken('tceAction');
589 return $rU;
590 }
591
592 /**
593 * editUrl of all current elements
594 * ONLY database
595 * Links to alt_doc.php
596 *
597 * @return string The URL to alt_doc.php with parameters.
598 * @todo Define visibility
599 */
600 public function editUrl() {
601 // All records
602 $elements = $this->elFromTable('');
603 $editCMDArray = array();
604 foreach ($elements as $tP => $value) {
605 list($table, $uid) = explode('|', $tP);
606 $editCMDArray[] = '&edit[' . $table . '][' . $uid . ']=edit';
607 }
608 $rU = $this->backPath . 'alt_doc.php?' . implode('', $editCMDArray);
609 return $rU;
610 }
611
612 /**
613 * Returns the remove-url (file and db)
614 * for file $table='_FILE' and $uid = shortmd5 hash of path
615 *
616 * @param string $table Tablename
617 * @param string $uid Uid integer/shortmd5 hash
618 * @return string URL
619 * @todo Define visibility
620 */
621 public function removeUrl($table, $uid) {
622 return GeneralUtility::linkThisScript(array('CB' => array('remove' => $table . '|' . $uid)));
623 }
624
625 /**
626 * Returns confirm JavaScript message
627 *
628 * @param string $table Table name
629 * @param mixed $rec For records its an array, for files its a string (path)
630 * @param string $type Type-code
631 * @param array $clElements Array of selected elements
632 * @param string $columnLabel Name of the content column
633 * @return string JavaScript "confirm" message
634 * @todo Define visibility
635 */
636 public function confirmMsg($table, $rec, $type, $clElements, $columnLabel = '') {
637 if ($GLOBALS['BE_USER']->jsConfirmation(2)) {
638 $labelKey = 'LLL:EXT:lang/locallang_core.xlf:mess.' . ($this->currentMode() == 'copy' ? 'copy' : 'move') . ($this->current == 'normal' ? '' : 'cb') . '_' . $type;
639 $msg = $GLOBALS['LANG']->sL($labelKey . ($columnLabel ? '_colPos': ''));
640 if ($table == '_FILE') {
641 $thisRecTitle = basename($rec);
642 if ($this->current == 'normal') {
643 $selItem = reset($clElements);
644 $selRecTitle = basename($selItem);
645 } else {
646 $selRecTitle = count($clElements);
647 }
648 } else {
649 $thisRecTitle = $table == 'pages' && !is_array($rec) ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : BackendUtility::getRecordTitle($table, $rec);
650 if ($this->current == 'normal') {
651 $selItem = $this->getSelectedRecord();
652 $selRecTitle = $selItem['_RECORD_TITLE'];
653 } else {
654 $selRecTitle = count($clElements);
655 }
656 }
657 // @TODO
658 // This can get removed as soon as the "_colPos" label is translated
659 // into all available locallang languages.
660 if (!$msg && $columnLabel) {
661 $thisRecTitle .= ' | ' . $columnLabel;
662 $msg = $GLOBALS['LANG']->sL($labelKey);
663 }
664
665 // Message
666 $conf = 'confirm(' . GeneralUtility::quoteJSvalue(sprintf($msg, GeneralUtility::fixed_lgd_cs($selRecTitle, 30), GeneralUtility::fixed_lgd_cs($thisRecTitle, 30), GeneralUtility::fixed_lgd_cs($columnLabel, 30))) . ')';
667 } else {
668 $conf = '';
669 }
670 return $conf;
671 }
672
673 /**
674 * Clipboard label - getting from "EXT:lang/locallang_core.xlf:"
675 *
676 * @param string $key Label Key
677 * @param string $Akey Alternative key to "labels
678 * @return string
679 * @todo Define visibility
680 */
681 public function clLabel($key, $Akey = 'labels') {
682 return htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:' . $Akey . '.' . $key));
683 }
684
685 /**
686 * Creates GET parameters for linking to the export module.
687 *
688 * @return array GET parameters for current clipboard content to be exported
689 */
690 protected function exportClipElementParameters() {
691 // Init
692 $pad = $this->current;
693 $params = array();
694 $params['tx_impexp[action]'] = 'export';
695 // Traverse items:
696 if (is_array($this->clipData[$pad]['el'])) {
697 foreach ($this->clipData[$pad]['el'] as $k => $v) {
698 if ($v) {
699 list($table, $uid) = explode('|', $k);
700 // Rendering files/directories on the clipboard
701 if ($table == '_FILE') {
702 if (file_exists($v) && GeneralUtility::isAllowedAbsPath($v)) {
703 $params['tx_impexp[' . (is_dir($v) ? 'dir' : 'file') . '][]'] = $v;
704 }
705 } else {
706 // Rendering records:
707 $rec = BackendUtility::getRecord($table, $uid);
708 if (is_array($rec)) {
709 $params['tx_impexp[record][]'] = $table . ':' . $uid;
710 }
711 }
712 }
713 }
714 }
715 return $params;
716 }
717
718 /*****************************************
719 *
720 * Helper functions
721 *
722 ****************************************/
723 /**
724 * Removes element on clipboard
725 *
726 * @param string $el Key of element in ->clipData array
727 * @return void
728 * @todo Define visibility
729 */
730 public function removeElement($el) {
731 unset($this->clipData[$this->current]['el'][$el]);
732 $this->changed = 1;
733 }
734
735 /**
736 * Saves the clipboard, no questions asked.
737 * Use ->endClipboard normally (as it checks if changes has been done so saving is necessary)
738 *
739 * @access private
740 * @return void
741 * @todo Define visibility
742 */
743 public function saveClipboard() {
744 $GLOBALS['BE_USER']->pushModuleData('clipboard', $this->clipData);
745 }
746
747 /**
748 * Returns the current mode, 'copy' or 'cut'
749 *
750 * @return string "copy" or "cut
751 * @todo Define visibility
752 */
753 public function currentMode() {
754 return $this->clipData[$this->current]['mode'] == 'copy' ? 'copy' : 'cut';
755 }
756
757 /**
758 * This traverses the elements on the current clipboard pane
759 * and unsets elements which does not exist anymore or are disabled.
760 *
761 * @return void
762 * @todo Define visibility
763 */
764 public function cleanCurrent() {
765 if (is_array($this->clipData[$this->current]['el'])) {
766 foreach ($this->clipData[$this->current]['el'] as $k => $v) {
767 list($table, $uid) = explode('|', $k);
768 if ($table != '_FILE') {
769 if (!$v || !is_array(BackendUtility::getRecord($table, $uid, 'uid'))) {
770 unset($this->clipData[$this->current]['el'][$k]);
771 $this->changed = 1;
772 }
773 } else {
774 if (!$v) {
775 unset($this->clipData[$this->current]['el'][$k]);
776 $this->changed = 1;
777 } else {
778 try {
779 ResourceFactory::getInstance()->retrieveFileOrFolderObject($v);
780 } catch (\TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException $e) {
781 // The file has been deleted in the meantime, so just remove it silently
782 unset($this->clipData[$this->current]['el'][$k]);
783 }
784 }
785 }
786 }
787 }
788 }
789
790 /**
791 * Counts the number of elements from the table $matchTable. If $matchTable is blank, all tables (except '_FILE' of course) is counted.
792 *
793 * @param string $matchTable Table to match/count for.
794 * @param string $pad Can optionally be used to set another pad than the current.
795 * @return array Array with keys from the CB.
796 * @todo Define visibility
797 */
798 public function elFromTable($matchTable = '', $pad = '') {
799 $pad = $pad ? $pad : $this->current;
800 $list = array();
801 if (is_array($this->clipData[$pad]['el'])) {
802 foreach ($this->clipData[$pad]['el'] as $k => $v) {
803 if ($v) {
804 list($table, $uid) = explode('|', $k);
805 if ($table != '_FILE') {
806 if ((!$matchTable || (string) $table == (string) $matchTable) && $GLOBALS['TCA'][$table]) {
807 $list[$k] = $pad == 'normal' ? $v : $uid;
808 }
809 } else {
810 if ((string) $table == (string) $matchTable) {
811 $list[$k] = $v;
812 }
813 }
814 }
815 }
816 }
817 return $list;
818 }
819
820 /**
821 * Verifies if the item $table/$uid is on the current pad.
822 * If the pad is "normal", the mode value is returned if the element existed. Thus you'll know if the item was copy or cut moded...
823 *
824 * @param string $table Table name, (_FILE for files...)
825 * @param integer $uid Element uid (path for files)
826 * @return string
827 * @todo Define visibility
828 */
829 public function isSelected($table, $uid) {
830 $k = $table . '|' . $uid;
831 return $this->clipData[$this->current]['el'][$k] ? ($this->current == 'normal' ? $this->currentMode() : 1) : '';
832 }
833
834 /**
835 * Returns item record $table,$uid if selected on current clipboard
836 * If table and uid is blank, the first element is returned.
837 * Makes sense only for DB records - not files!
838 *
839 * @param string $table Table name
840 * @param integer $uid Element uid
841 * @return array Element record with extra field _RECORD_TITLE set to the title of the record
842 * @todo Define visibility
843 */
844 public function getSelectedRecord($table = '', $uid = '') {
845 if (!$table && !$uid) {
846 $elArr = $this->elFromTable('');
847 reset($elArr);
848 list($table, $uid) = explode('|', key($elArr));
849 }
850 if ($this->isSelected($table, $uid)) {
851 $selRec = BackendUtility::getRecordWSOL($table, $uid);
852 $selRec['_RECORD_TITLE'] = BackendUtility::getRecordTitle($table, $selRec);
853 return $selRec;
854 }
855 }
856
857 /**
858 * Reports if the current pad has elements (does not check file/DB type OR if file/DBrecord exists or not. Only counting array)
859 *
860 * @return boolean TRUE if elements exist.
861 * @todo Define visibility
862 */
863 public function isElements() {
864 return is_array($this->clipData[$this->current]['el']) && count($this->clipData[$this->current]['el']);
865 }
866
867 /*****************************************
868 *
869 * FOR USE IN tce_db.php:
870 *
871 ****************************************/
872 /**
873 * Applies the proper paste configuration in the $cmd array send to tce_db.php.
874 * $ref is the target, see description below.
875 * The current pad is pasted
876 *
877 * $ref: [tablename]:[paste-uid].
878 * Tablename is the name of the table from which elements *on the current clipboard* is pasted with the 'pid' paste-uid.
879 * No tablename means that all items on the clipboard (non-files) are pasted. This requires paste-uid to be positive though.
880 * so 'tt_content:-3' means 'paste tt_content elements on the clipboard to AFTER tt_content:3 record
881 * 'tt_content:30' means 'paste tt_content elements on the clipboard into page with id 30
882 * ':30' means 'paste ALL database elements on the clipboard into page with id 30
883 * ':-30' not valid.
884 *
885 * @param string $ref [tablename]:[paste-uid], see description
886 * @param array $CMD Command-array
887 * @param NULL|array If additional values should get set in the copied/moved record this will be an array containing key=>value pairs
888 * @return array Modified Command-array
889 * @todo Define visibility
890 */
891 public function makePasteCmdArray($ref, $CMD, array $update = NULL) {
892 list($pTable, $pUid) = explode('|', $ref);
893 $pUid = (int)$pUid;
894 // pUid must be set and if pTable is not set (that means paste ALL elements)
895 // the uid MUST be positive/zero (pointing to page id)
896 if ($pTable || $pUid >= 0) {
897 $elements = $this->elFromTable($pTable);
898 // So the order is preserved.
899 $elements = array_reverse($elements);
900 $mode = $this->currentMode() == 'copy' ? 'copy' : 'move';
901 // Traverse elements and make CMD array
902 foreach ($elements as $tP => $value) {
903 list($table, $uid) = explode('|', $tP);
904 if (!is_array($CMD[$table])) {
905 $CMD[$table] = array();
906 }
907 if (is_array($update)) {
908 $CMD[$table][$uid][$mode] = array(
909 'action' => 'paste',
910 'target' => $pUid,
911 'update' => $update,
912 );
913 } else {
914 $CMD[$table][$uid][$mode] = $pUid;
915 }
916 if ($mode == 'move') {
917 $this->removeElement($tP);
918 }
919 }
920 $this->endClipboard();
921 }
922 return $CMD;
923 }
924
925 /**
926 * Delete record entries in CMD array
927 *
928 * @param array $CMD Command-array
929 * @return array Modified Command-array
930 * @todo Define visibility
931 */
932 public function makeDeleteCmdArray($CMD) {
933 // all records
934 $elements = $this->elFromTable('');
935 foreach ($elements as $tP => $value) {
936 list($table, $uid) = explode('|', $tP);
937 if (!is_array($CMD[$table])) {
938 $CMD[$table] = array();
939 }
940 $CMD[$table][$uid]['delete'] = 1;
941 $this->removeElement($tP);
942 }
943 $this->endClipboard();
944 return $CMD;
945 }
946
947 /*****************************************
948 *
949 * FOR USE IN tce_file.php:
950 *
951 ****************************************/
952 /**
953 * Applies the proper paste configuration in the $file array send to tce_file.php.
954 * The current pad is pasted
955 *
956 * @param string $ref Reference to element (splitted by "|")
957 * @param array $FILE Command-array
958 * @return array Modified Command-array
959 * @todo Define visibility
960 */
961 public function makePasteCmdArray_file($ref, $FILE) {
962 list($pTable, $pUid) = explode('|', $ref);
963 $elements = $this->elFromTable('_FILE');
964 $mode = $this->currentMode() == 'copy' ? 'copy' : 'move';
965 // Traverse elements and make CMD array
966 foreach ($elements as $tP => $path) {
967 $FILE[$mode][] = array('data' => $path, 'target' => $pUid);
968 if ($mode == 'move') {
969 $this->removeElement($tP);
970 }
971 }
972 $this->endClipboard();
973 return $FILE;
974 }
975
976 /**
977 * Delete files in CMD array
978 *
979 * @param array $FILE Command-array
980 * @return array Modified Command-array
981 * @todo Define visibility
982 */
983 public function makeDeleteCmdArray_file($FILE) {
984 $elements = $this->elFromTable('_FILE');
985 // Traverse elements and make CMD array
986 foreach ($elements as $tP => $path) {
987 $FILE['delete'][] = array('data' => $path);
988 $this->removeElement($tP);
989 }
990 $this->endClipboard();
991 return $FILE;
992 }
993
994 }