[TASK] Make move_el.php dispatched
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / ClickMenu / ClickMenu.php
1 <?php
2 namespace TYPO3\CMS\Backend\ClickMenu;
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\Folder;
20 use TYPO3\CMS\Core\Resource\ResourceFactory;
21 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Backend\Clipboard\Clipboard;
24 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
25 use TYPO3\CMS\Lang\LanguageService;
26
27 /**
28 * Class for generating the click menu
29 *
30 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
31 * @internal
32 */
33 class ClickMenu {
34
35 /**
36 * Defines if the click menu is first level or second.
37 * Second means the click menu is triggered from another menu.
38 *
39 * @var int
40 */
41 public $cmLevel = 0;
42
43 /**
44 * Clipboard array (submitted by eg. pressing the paste button)
45 *
46 * @var bool
47 */
48 public $CB;
49
50 /**
51 * If set, the calling document should be in the listframe of a frameset.
52 *
53 * @var bool
54 */
55 public $listFrame = FALSE;
56
57 /**
58 * If set, the menu is about database records, not files. (set if part 2 [1] of the item-var is NOT blank)
59 *
60 * @var bool
61 */
62 public $isDBmenu = FALSE;
63
64 /**
65 * If TRUE, the "content" frame is always used for reference (when condensed mode is enabled)
66 *
67 * @var bool
68 */
69 public $alwaysContentFrame = FALSE;
70
71 /**
72 * Stores the parts of the input $item string, splitted by "|":
73 * [0] = table/file, [1] = uid/blank, [2] = flag: If set, listFrame,
74 * If "2" then "content frame" is forced [3] = ("+" prefix = disable
75 * all by default, enable these. Default is to disable) Items key list
76 *
77 * @var array
78 */
79 public $iParts = array();
80
81 /**
82 * Contains list of keywords of items to disable in the menu
83 *
84 * @var array
85 */
86 public $disabledItems = array();
87
88 /**
89 * If TRUE, Show icons on the left.
90 *
91 * @var bool
92 */
93 public $leftIcons = FALSE;
94
95 /**
96 * Array of classes to be used for user processing of the menu content.
97 * This is for the API of adding items to the menu from outside.
98 *
99 * @var array
100 */
101 public $extClassArray = array();
102
103 /**
104 * Set, when edit icon is drawn.
105 *
106 * @var bool
107 */
108 public $editPageIconSet = FALSE;
109
110 /**
111 * Set to TRUE, if editing of the element is OK.
112 *
113 * @var bool
114 */
115 public $editOK = FALSE;
116
117 /**
118 * The current record
119 *
120 * @var array
121 */
122 public $rec = array();
123
124 /**
125 * Clipboard set from the outside
126 * Declared as public for now, should become protected
127 * soon-ish
128 * @var Clipboard;
129 */
130 public $clipObj;
131
132 /**
133 * The current page record
134 * @var array
135 */
136 protected $pageinfo;
137
138 /**
139 * Language Service property. Used to access localized labels
140 *
141 * @var LanguageService
142 */
143 protected $languageService;
144
145 /**
146 * @var BackendUserAuthentication
147 */
148 protected $backendUser;
149
150 /**
151 * @param LanguageService $languageService Language Service to inject
152 * @param BackendUserAuthentication $backendUser
153 */
154 public function __construct(LanguageService $languageService = NULL, BackendUserAuthentication $backendUser = NULL) {
155 $this->languageService = $languageService ?: $GLOBALS['LANG'];
156 $this->backendUser = $backendUser ?: $GLOBALS['BE_USER'];
157 }
158
159 /**
160 * Initialize click menu
161 *
162 * @return string The clickmenu HTML content
163 */
164 public function init() {
165 $CMcontent = '';
166 // Setting GPvars:
167 $this->cmLevel = (int)GeneralUtility::_GP('cmLevel');
168 $this->CB = GeneralUtility::_GP('CB');
169
170 // Deal with Drag&Drop context menus
171 if ((string)GeneralUtility::_GP('dragDrop') !== '') {
172 return $this->printDragDropClickMenu(GeneralUtility::_GP('dragDrop'), GeneralUtility::_GP('srcId'), GeneralUtility::_GP('dstId'));
173 }
174 // Can be set differently as well
175 $this->iParts[0] = GeneralUtility::_GP('table');
176 $this->iParts[1] = GeneralUtility::_GP('uid');
177 $this->iParts[2] = GeneralUtility::_GP('listFr');
178 $this->iParts[3] = GeneralUtility::_GP('enDisItems');
179 // Setting flags:
180 if ($this->iParts[2]) {
181 $this->listFrame = TRUE;
182 }
183 if ($this->iParts[2] == 2) {
184 $this->alwaysContentFrame = TRUE;
185 }
186 if (isset($this->iParts[1]) && $this->iParts[1] !== '') {
187 $this->isDBmenu = TRUE;
188 }
189 $TSkey = ($this->isDBmenu ? 'page' : 'folder') . ($this->listFrame ? 'List' : 'Tree');
190 $this->disabledItems = GeneralUtility::trimExplode(',', $this->backendUser->getTSConfigVal('options.contextMenu.' . $TSkey . '.disableItems'), TRUE);
191 $this->leftIcons = (bool)$this->backendUser->getTSConfigVal('options.contextMenu.options.leftIcons');
192 // &cmLevel flag detected (2nd level menu)
193 if (!$this->cmLevel) {
194 // Make 1st level clickmenu:
195 if ($this->isDBmenu) {
196 $CMcontent = $this->printDBClickMenu($this->iParts[0], $this->iParts[1]);
197 } else {
198 $CMcontent = $this->printFileClickMenu($this->iParts[0]);
199 }
200 } else {
201 // Make 2nd level clickmenu (only for DBmenus)
202 if ($this->isDBmenu) {
203 $CMcontent = $this->printNewDBLevel($this->iParts[0], $this->iParts[1]);
204 }
205 }
206 // Return clickmenu content:
207 return $CMcontent;
208 }
209
210 /***************************************
211 *
212 * DATABASE
213 *
214 ***************************************/
215 /**
216 * Make 1st level clickmenu:
217 *
218 * @param string $table Table name
219 * @param int $uid UID for the current record.
220 * @return string HTML content
221 */
222 public function printDBClickMenu($table, $uid) {
223 $uid = (int)$uid;
224 // Get record:
225 $this->rec = BackendUtility::getRecordWSOL($table, $uid);
226 $menuItems = array();
227 $root = 0;
228 $DBmount = FALSE;
229 // Rootlevel
230 if ($table === 'pages' && $uid === 0) {
231 $root = 1;
232 }
233 // DB mount
234 if ($table === 'pages' && in_array($uid, $this->backendUser->returnWebmounts())) {
235 $DBmount = TRUE;
236 }
237 // Used to hide cut,copy icons for l10n-records
238 $l10nOverlay = FALSE;
239 // Should only be performed for overlay-records within the same table
240 if (BackendUtility::isTableLocalizable($table) && !isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
241 $l10nOverlay = (int)$this->rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] != 0;
242 }
243 // If record found (or root), go ahead and fill the $menuItems array which will contain data for the elements to render.
244 if (is_array($this->rec) || $root) {
245 // Get permissions
246 $lCP = $this->backendUser->calcPerms(BackendUtility::getRecord('pages', $table === 'pages' ? $this->rec['uid'] : $this->rec['pid']));
247 // View
248 if (!in_array('view', $this->disabledItems)) {
249 if ($table === 'pages') {
250 $menuItems['view'] = $this->DB_view($uid);
251 }
252 if ($table === 'tt_content') {
253 $ws_rec = BackendUtility::getRecordWSOL($table, $this->rec['uid']);
254 $menuItems['view'] = $this->DB_view($ws_rec['pid']);
255 }
256 }
257 // Edit:
258 if (!$root && ($this->backendUser->isPSet($lCP, $table, 'edit') || $this->backendUser->isPSet($lCP, $table, 'editcontent'))) {
259 if (!in_array('edit', $this->disabledItems)) {
260 $menuItems['edit'] = $this->DB_edit($table, $uid);
261 }
262 $this->editOK = TRUE;
263 }
264 // New:
265 if (!in_array('new', $this->disabledItems) && $this->backendUser->isPSet($lCP, $table, 'new')) {
266 $menuItems['new'] = $this->DB_new($table, $uid);
267 }
268 // Info:
269 if (!in_array('info', $this->disabledItems) && !$root) {
270 $menuItems['info'] = $this->DB_info($table, $uid);
271 }
272 $menuItems['spacer1'] = 'spacer';
273 // Copy:
274 if (!in_array('copy', $this->disabledItems) && !$root && !$DBmount && !$l10nOverlay) {
275 $menuItems['copy'] = $this->DB_copycut($table, $uid, 'copy');
276 }
277 // Cut:
278 if (!in_array('cut', $this->disabledItems) && !$root && !$DBmount && !$l10nOverlay) {
279 $menuItems['cut'] = $this->DB_copycut($table, $uid, 'cut');
280 }
281 // Paste:
282 $elFromAllTables = count($this->clipObj->elFromTable(''));
283 if (!in_array('paste', $this->disabledItems) && $elFromAllTables) {
284 $selItem = $this->clipObj->getSelectedRecord();
285 $elInfo = array(
286 GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], $this->backendUser->uc['titleLen']),
287 $root ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] : GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $this->backendUser->uc['titleLen']),
288 $this->clipObj->currentMode()
289 );
290 if ($table === 'pages' && $lCP & 8) {
291 if ($elFromAllTables) {
292 $menuItems['pasteinto'] = $this->DB_paste('', $uid, 'into', $elInfo);
293 }
294 }
295 $elFromTable = count($this->clipObj->elFromTable($table));
296 if (!$root && !$DBmount && $elFromTable && $GLOBALS['TCA'][$table]['ctrl']['sortby']) {
297 $menuItems['pasteafter'] = $this->DB_paste($table, -$uid, 'after', $elInfo);
298 }
299 }
300 $localItems = array();
301 if (!$this->cmLevel && !in_array('moreoptions', $this->disabledItems, TRUE)) {
302 // Creating menu items here:
303 if ($this->editOK) {
304 $localItems[] = 'spacer';
305 $localItems['moreoptions'] = $this->linkItem(
306 $this->label('more'),
307 '',
308 'TYPO3.ClickMenu.fetch(' . GeneralUtility::quoteJSvalue(GeneralUtility::linkThisScript() . '&cmLevel=1&subname=moreoptions') . ');return false;',
309 FALSE,
310 TRUE
311 );
312 $menuItemHideUnhideAllowed = FALSE;
313 $hiddenField = '';
314 // Check if column for disabled is defined
315 if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])) {
316 $hiddenField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
317 if (
318 $hiddenField !== '' && !empty($GLOBALS['TCA'][$table]['columns'][$hiddenField]['exclude'])
319 && $this->backendUser->check('non_exclude_fields', $table . ':' . $hiddenField)
320 ) {
321 $menuItemHideUnhideAllowed = TRUE;
322 }
323 }
324 if ($menuItemHideUnhideAllowed && !in_array('hide', $this->disabledItems, TRUE)) {
325 $localItems['hide'] = $this->DB_hideUnhide($table, $this->rec, $hiddenField);
326 }
327 $anyEnableColumnsFieldAllowed = FALSE;
328 // Check if columns are defined
329 if (isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
330 $columnsToCheck = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
331 if ($table === 'pages' && !empty($columnsToCheck)) {
332 $columnsToCheck[] = 'extendToSubpages';
333 }
334 foreach ($columnsToCheck as $currentColumn) {
335 if (
336 !empty($GLOBALS['TCA'][$table]['columns'][$currentColumn]['exclude'])
337 && $this->backendUser->check('non_exclude_fields', $table . ':' . $currentColumn)
338 ) {
339 $anyEnableColumnsFieldAllowed = TRUE;
340 }
341 }
342 }
343 if ($anyEnableColumnsFieldAllowed && !in_array('edit_access', $this->disabledItems, TRUE)) {
344 $localItems['edit_access'] = $this->DB_editAccess($table, $uid);
345 }
346 if ($table === 'pages' && $this->editPageIconSet && !in_array('edit_pageproperties', $this->disabledItems, TRUE)) {
347 $localItems['edit_pageproperties'] = $this->DB_editPageProperties($uid);
348 }
349 }
350 // Find delete element among the input menu items and insert the local items just before that:
351 $c = 0;
352 $deleteFound = FALSE;
353 foreach ($menuItems as $key => $value) {
354 $c++;
355 if ($key === 'delete') {
356 $deleteFound = TRUE;
357 break;
358 }
359 }
360 if ($deleteFound) {
361 // .. subtract two... (delete item + its spacer element...)
362 $c -= 2;
363 // and insert the items just before the delete element.
364 array_splice($menuItems, $c, 0, $localItems);
365 } else {
366 $menuItems = array_merge($menuItems, $localItems);
367 }
368 }
369
370 // Delete:
371 $elInfo = array(GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $this->rec), $this->backendUser->uc['titleLen']));
372 if (!in_array('delete', $this->disabledItems) && !$root && !$DBmount && $this->backendUser->isPSet($lCP, $table, 'delete')) {
373 $menuItems['spacer2'] = 'spacer';
374 $menuItems['delete'] = $this->DB_delete($table, $uid, $elInfo);
375 }
376 if (!in_array('history', $this->disabledItems)) {
377 $menuItems['history'] = $this->DB_history($table, $uid, $elInfo);
378 }
379 }
380 // Adding external elements to the menuItems array
381 $menuItems = $this->processingByExtClassArray($menuItems, $table, $uid);
382 // Processing by external functions?
383 $menuItems = $this->externalProcessingOfDBMenuItems($menuItems);
384 if (!is_array($this->rec)) {
385 $this->rec = array();
386 }
387 // Return the printed elements:
388 return $this->printItems($menuItems, $root ? IconUtility::getSpriteIcon('apps-pagetree-root') . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) : IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => htmlspecialchars(BackendUtility::getRecordIconAltText($this->rec, $table)))) . BackendUtility::getRecordTitle($table, $this->rec, TRUE));
389 }
390
391 /**
392 * Make 2nd level clickmenu (only for DBmenus)
393 *
394 * @param string $table Table name
395 * @param int $uid UID for the current record.
396 * @return string HTML content
397 */
398 public function printNewDBLevel($table, $uid) {
399 $localItems = [];
400 $uid = (int)$uid;
401 // Setting internal record to the table/uid :
402 $this->rec = BackendUtility::getRecordWSOL($table, $uid);
403 $menuItems = array();
404 $root = 0;
405 // Rootlevel
406 if ($table === 'pages' && $uid === 0) {
407 $root = 1;
408 }
409 // If record was found, check permissions and get menu items.
410 if (is_array($this->rec) || $root) {
411 $lCP = $this->backendUser->calcPerms(BackendUtility::getRecord('pages', $table === 'pages' ? $this->rec['uid'] : $this->rec['pid']));
412 // Edit:
413 if (!$root && ($this->backendUser->isPSet($lCP, $table, 'edit') || $this->backendUser->isPSet($lCP, $table, 'editcontent'))) {
414 $this->editOK = TRUE;
415 }
416 $menuItems = $this->processingByExtClassArray($menuItems, $table, $uid);
417 }
418
419 $subname = GeneralUtility::_GP('subname');
420 if ($subname === 'moreoptions') {
421 // If the page can be edited, then show this:
422 if ($this->editOK) {
423 if (($table === 'pages' || $table === 'tt_content') && !in_array('move_wizard', $this->disabledItems, TRUE)) {
424 $localItems['move_wizard'] = $this->DB_moveWizard($table, $uid, $this->rec);
425 }
426 if (($table === 'pages' || $table === 'tt_content') && !in_array('new_wizard', $this->disabledItems, TRUE)) {
427 $localItems['new_wizard'] = $this->DB_newWizard($table, $uid, $this->rec);
428 }
429 if ($table === 'pages' && !in_array('perms', $this->disabledItems, TRUE) && $this->backendUser->check('modules', 'system_BeuserTxPermission')) {
430 $localItems['perms'] = $this->DB_perms($table, $uid, $this->rec);
431 }
432 if (!in_array('db_list', $this->disabledItems, TRUE) && $this->backendUser->check('modules', 'web_list')) {
433 $localItems['db_list'] = $this->DB_db_list($table, $uid, $this->rec);
434 }
435 }
436 // Temporary mount point item:
437 if ($table === 'pages') {
438 $localItems['temp_mount_point'] = $this->DB_tempMountPoint($uid);
439 }
440 // Merge the locally created items into the current menu items passed to this function.
441 $menuItems = array_merge($menuItems, $localItems);
442 }
443
444 // Return the printed elements:
445 if (!is_array($menuItems)) {
446 $menuItems = array();
447 }
448 return $this->printItems($menuItems, $root ? IconUtility::getSpriteIcon('apps-pagetree-root') . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) : IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => htmlspecialchars(BackendUtility::getRecordIconAltText($this->rec, $table)))) . BackendUtility::getRecordTitle($table, $this->rec, TRUE));
449 }
450
451 /**
452 * Processing the $menuItems array (for extension classes) (DATABASE RECORDS)
453 *
454 * @param array $menuItems Array for manipulation.
455 * @return array Processed $menuItems array
456 */
457 public function externalProcessingOfDBMenuItems($menuItems) {
458 return $menuItems;
459 }
460
461 /**
462 * Processing the $menuItems array by external classes (typ. adding items)
463 *
464 * @param array $menuItems Array for manipulation.
465 * @param string $table Table name
466 * @param int $uid UID for the current record.
467 * @return array Processed $menuItems array
468 */
469 public function processingByExtClassArray($menuItems, $table, $uid) {
470 if (is_array($this->extClassArray)) {
471 foreach ($this->extClassArray as $conf) {
472 $obj = GeneralUtility::makeInstance($conf['name']);
473 $menuItems = $obj->main($this, $menuItems, $table, $uid);
474 }
475 }
476 return $menuItems;
477 }
478
479 /**
480 * Returning JavaScript for the onClick event linking to the input URL.
481 *
482 * @param string $url The URL relative to TYPO3_mainDir
483 * @param string $retUrl The return_url-parameter
484 * @param bool $hideCM If set, the "hideCM()" will be called
485 * @param string $overrideLoc If set, gives alternative location to load in (for example top frame or somewhere else)
486 * @return string JavaScript for an onClick event.
487 */
488 public function urlRefForCM($url, $retUrl = '', $hideCM = TRUE, $overrideLoc = '') {
489 $loc = 'top.content.list_frame';
490 return ($overrideLoc ? 'var docRef=' . $overrideLoc : 'var docRef=(top.content.list_frame)?top.content.list_frame:' . $loc)
491 . '; docRef.location.href=top.TS.PATH_typo3+\'' . $url . '\'' . ($retUrl ? '+\'&' . $retUrl . '=\'+top.rawurlencode('
492 . $this->frameLocation('docRef.document') . '.pathname+' . $this->frameLocation('docRef.document') . '.search)' : '')
493 . ';';
494 }
495
496 /**
497 * Adding CM element for Clipboard "copy" and "cut"
498 *
499 * @param string $table Table name
500 * @param int $uid UID for the current record.
501 * @param string $type Type: "copy" or "cut
502 * @return array Item array, element in $menuItems
503 * @internal
504 */
505 public function DB_copycut($table, $uid, $type) {
506 $isSel = '';
507 if ($this->clipObj->current === 'normal') {
508 $isSel = $this->clipObj->isSelected($table, $uid);
509 }
510 $addParam = array();
511 if ($this->listFrame) {
512 $addParam['reloadListFrame'] = $this->alwaysContentFrame ? 2 : 1;
513 }
514 return $this->linkItem($this->label($type), IconUtility::getSpriteIcon('actions-edit-' . $type . ($isSel === $type ? '-release' : '')), 'TYPO3.ClickMenu.fetch(' . GeneralUtility::quoteJSvalue($this->clipObj->selUrlDB($table, $uid, ($type === 'copy' ? 1 : 0), ($isSel == $type), $addParam)) . ');return false;');
515 }
516
517 /**
518 * Adding CM element for Clipboard "paste into"/"paste after"
519 * NOTICE: $table and $uid should follow the special syntax for paste, see clipboard-class :: pasteUrl();
520 *
521 * @param string $table Table name
522 * @param int $uid UID for the current record. NOTICE: Special syntax!
523 * @param string $type Type: "into" or "after
524 * @param array $elInfo Contains instructions about whether to copy or cut an element.
525 * @return array Item array, element in $menuItems
526 * @see \TYPO3\CMS\Backend\Clipboard\Clipboard::pasteUrl()
527 * @internal
528 */
529 public function DB_paste($table, $uid, $type, $elInfo) {
530 $loc = 'top.content.list_frame';
531 if ($this->backendUser->jsConfirmation(2)) {
532 $conf = $loc . ' && confirm(' . GeneralUtility::quoteJSvalue(sprintf($this->languageService->sL(('LLL:EXT:lang/locallang_core.xlf:mess.' . ($elInfo[2] === 'copy' ? 'copy' : 'move') . '_' . $type)), $elInfo[0], $elInfo[1])) . ')';
533 } else {
534 $conf = $loc;
535 }
536 $editOnClick = 'if(' . $conf . '){' . $loc . '.location.href=top.TS.PATH_typo3+\'' . $this->clipObj->pasteUrl($table, $uid, 0) . '&redirect=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search);}';
537 return $this->linkItem($this->label('paste' . $type), IconUtility::getSpriteIcon('actions-document-paste-' . $type), $editOnClick . 'return false;');
538 }
539
540 /**
541 * Adding CM element for Info
542 *
543 * @param string $table Table name
544 * @param int $uid UID for the current record.
545 * @return array Item array, element in $menuItems
546 * @internal
547 */
548 public function DB_info($table, $uid) {
549 return $this->linkItem($this->label('info'), IconUtility::getSpriteIcon('actions-document-info'), 'top.launchView(\'' . $table . '\', \'' . $uid . '\');');
550 }
551
552 /**
553 * Adding CM element for History
554 *
555 * @param string $table Table name
556 * @param int $uid UID for the current record.
557 * @return array Item array, element in $menuItems
558 * @internal
559 */
560 public function DB_history($table, $uid) {
561 $url = BackendUtility::getModuleUrl('record_history', array('element' => $table . ':' . $uid));
562 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_history')), IconUtility::getSpriteIcon('actions-document-history-open'), $this->urlRefForCM($url, 'returnUrl'), 0);
563 }
564
565 /**
566 * Adding CM element for Permission setting
567 *
568 * @param string $table Table name
569 * @param int $uid UID for the current record.
570 * @param array $rec The "pages" record with "perms_*" fields inside.
571 * @return array Item array, element in $menuItems
572 * @internal
573 */
574 public function DB_perms($table, $uid, $rec) {
575 if (!ExtensionManagementUtility::isLoaded('beuser')) {
576 return '';
577 }
578
579 $parameters = array(
580 'id' => $uid,
581 );
582
583 if ($rec['perms_userid'] == $this->backendUser->user['uid'] || $this->backendUser->isAdmin()) {
584 $parameters['return_id'] = $uid;
585 $parameters['edit'] = '1';
586 }
587
588 $url = BackendUtility::getModuleUrl('system_BeuserTxPermission', $parameters);
589 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_perms')), IconUtility::getSpriteIcon('status-status-locked'), $this->urlRefForCM($url), 0);
590 }
591
592 /**
593 * Adding CM element for DBlist
594 *
595 * @param string $table Table name
596 * @param int $uid UID for the current record.
597 * @param array $rec Record of the element (needs "pid" field if not pages-record)
598 * @return array Item array, element in $menuItems
599 * @internal
600 */
601 public function DB_db_list($table, $uid, $rec) {
602 $urlParams = array();
603 $urlParams['id'] = $table === 'pages' ? $uid : $rec['pid'];
604 $urlParams['table'] = $table === 'pages' ? '' : $table;
605 $url = BackendUtility::getModuleUrl('web_list', $urlParams, '', TRUE);
606 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_db_list')), IconUtility::getSpriteIcon('actions-system-list-open'), 'top.nextLoadModuleUrl=\'' . $url . '\';top.goToModule(\'web_list\', 1);', 0);
607 }
608
609 /**
610 * Adding CM element for Moving wizard
611 *
612 * @param string $table Table name
613 * @param int $uid UID for the current record.
614 * @param array $rec Record. Needed for tt-content elements which will have the sys_language_uid sent
615 * @return array Item array, element in $menuItems
616 * @internal
617 */
618 public function DB_moveWizard($table, $uid, $rec) {
619 // Hardcoded field for tt_content elements.
620 $url = BackendUtility::getModuleUrl('move_element') . '&table=' . $table . '&uid=' . $uid;
621 $url .= ($table === 'tt_content' ? '&sys_language_uid=' . (int)$rec['sys_language_uid'] : '');
622 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_moveWizard' . ($table === 'pages' ? '_page' : ''))), IconUtility::getSpriteIcon('actions-' . ($table === 'pages' ? 'page' : 'document') . '-move'), $this->urlRefForCM($url, 'returnUrl'), 0);
623 }
624
625 /**
626 * Adding CM element for Create new wizard (either db_new.php or BackendUtility::getModuleUrl('new_content_element') or custom wizard)
627 *
628 * @param string $table Table name
629 * @param int $uid UID for the current record.
630 * @param array $rec Record.
631 * @return array Item array, element in $menuItems
632 * @internal
633 */
634 public function DB_newWizard($table, $uid, $rec) {
635 // If mod.web_list.newContentWiz.overrideWithExtension is set, use that extension's create new content wizard instead:
636 $tmpTSc = BackendUtility::getModTSconfig($this->pageinfo['uid'], 'mod.web_list');
637 $tmpTSc = $tmpTSc['properties']['newContentWiz.']['overrideWithExtension'];
638
639 $newContentWizScriptPath = ExtensionManagementUtility::isLoaded($tmpTSc) ? ExtensionManagementUtility::extRelPath($tmpTSc) . 'mod1/db_new_content_el.php?' : BackendUtility::getModuleUrl('new_content_element') . '&';
640 $url = $table === 'pages' ? 'db_new.php?id=' . $uid . '&pagesOnly=1' : $newContentWizScriptPath . 'id=' . $rec['pid'] . '&sys_language_uid=' . (int)$rec['sys_language_uid'];
641 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_newWizard')), IconUtility::getSpriteIcon('actions-' . ($table === 'pages' ? 'page' : 'document') . '-new'), $this->urlRefForCM($url, 'returnUrl'), 0);
642 }
643
644 /**
645 * Adding CM element for Editing of the access related fields of a table (disable, starttime, endtime, fe_groups)
646 *
647 * @param string $table Table name
648 * @param int $uid UID for the current record.
649 * @return array Item array, element in $menuItems
650 * @internal
651 */
652 public function DB_editAccess($table, $uid) {
653 $addParam = '&columnsOnly=' . rawurlencode((implode(',', $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']) . ($table === 'pages' ? ',extendToSubpages' : '')));
654 $url = 'alt_doc.php?edit[' . $table . '][' . $uid . ']=edit' . $addParam;
655 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_editAccess')), IconUtility::getSpriteIcon('actions-document-edit-access'), $this->urlRefForCM($url, 'returnUrl'), 1);
656 }
657
658 /**
659 * Adding CM element for edit page properties
660 *
661 * @param int $uid page uid to edit (PID)
662 * @return array Item array, element in $menuItems
663 * @internal
664 */
665 public function DB_editPageProperties($uid) {
666 $url = 'alt_doc.php?edit[pages][' . $uid . ']=edit';
667 return $this->linkItem($this->languageService->makeEntities($this->languageService->getLL('CM_editPageProperties')), IconUtility::getSpriteIcon('actions-page-open'), $this->urlRefForCM($url, 'returnUrl'), 1);
668 }
669
670 /**
671 * Adding CM element for regular editing of the element!
672 *
673 * @param string $table Table name
674 * @param int $uid UID for the current record.
675 * @return array Item array, element in $menuItems
676 * @internal
677 */
678 public function DB_edit($table, $uid) {
679 // If another module was specified, replace the default Page module with the new one
680 $newPageModule = trim($this->backendUser->getTSConfigVal('options.overridePageModule'));
681 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
682 $editOnClick = '';
683 $loc = 'top.content.list_frame';
684 $addParam = '';
685 $theIcon = 'actions-document-open';
686 if ($this->iParts[0] === 'pages' && $this->iParts[1] && $this->backendUser->check('modules', $pageModule)) {
687 $this->editPageIconSet = TRUE;
688 if ($this->backendUser->uc['classicPageEditMode']) {
689 $addParam = '&editRegularContentFromId=' . (int)$this->iParts[1];
690 } else {
691 $editOnClick = 'if(' . $loc . '){' . $loc . '.location.href=top.TS.PATH_typo3+\'alt_doc.php?returnUrl=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'&edit[' . $table . '][' . $uid . ']=edit' . $addParam . '\';}';
692 }
693 }
694 if (!$editOnClick) {
695 $editOnClick = 'if(' . $loc . '){' . $loc . '.location.href=top.TS.PATH_typo3+\'alt_doc.php?returnUrl=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'&edit[' . $table . '][' . $uid . ']=edit' . $addParam . '\';}';
696 }
697 return $this->linkItem($this->label('edit'), IconUtility::getSpriteIcon($theIcon), $editOnClick . ';');
698 }
699
700 /**
701 * Adding CM element for regular Create new element
702 *
703 * @param string $table Table name
704 * @param int $uid UID for the current record.
705 * @return array Item array, element in $menuItems
706 * @internal
707 */
708 public function DB_new($table, $uid) {
709 $loc = 'top.content.list_frame';
710 $editOnClick = 'if(' . $loc . '){' . $loc . '.location.href=top.TS.PATH_typo3+\'' . ($this->listFrame ? 'alt_doc.php?returnUrl=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'&edit[' . $table . '][-' . $uid . ']=new\'' : 'db_new.php?id=' . (int)$uid . '\'') . ';}';
711 return $this->linkItem($this->label('new'), IconUtility::getSpriteIcon('actions-' . ($table === 'pages' ? 'page' : 'document') . '-new'), $editOnClick . ';');
712 }
713
714 /**
715 * Adding CM element for Delete
716 *
717 * @param string $table Table name
718 * @param int $uid UID for the current record.
719 * @param array $elInfo Label for including in the confirmation message, EXT:lang/locallang_core.xlf:mess.delete
720 * @return array Item array, element in $menuItems
721 * @internal
722 */
723 public function DB_delete($table, $uid, $elInfo) {
724 $loc = 'top.content.list_frame';
725 if ($this->backendUser->jsConfirmation(4)) {
726 $conf = 'confirm(' . GeneralUtility::quoteJSvalue((sprintf($this->languageService->sL('LLL:EXT:lang/locallang_core.xlf:mess.delete'), $elInfo[0]) . BackendUtility::referenceCount($table, $uid, ' (There are %s reference(s) to this record!)') . BackendUtility::translationCount($table, $uid, (' ' . $this->languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.translationsOfRecord'))))) . ')';
727 } else {
728 $conf = '1==1';
729 }
730 $editOnClick = 'if(' . $loc . ' && ' . $conf . ' ){' . $loc . '.location.href=top.TS.PATH_typo3+\'tce_db.php?redirect=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'' . '&cmd[' . $table . '][' . $uid . '][delete]=1&prErr=1&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction') . '\';};';
731 if ($table === 'pages') {
732 $editOnClick .= 'top.nav.refresh.defer(500, top.nav);';
733 }
734 return $this->linkItem($this->label('delete'), IconUtility::getSpriteIcon('actions-edit-delete'), $editOnClick . 'return false;');
735 }
736
737 /**
738 * Adding CM element for View Page
739 *
740 * @param int $id Page uid (PID)
741 * @param string $anchor Anchor, if any
742 * @return array Item array, element in $menuItems
743 * @internal
744 */
745 public function DB_view($id, $anchor = '') {
746 return $this->linkItem($this->label('view'), IconUtility::getSpriteIcon('actions-document-view'), BackendUtility::viewOnClick($id, '', NULL, $anchor) . ';');
747 }
748
749 /**
750 * Adding element for setting temporary mount point.
751 *
752 * @param int $page_id Page uid (PID)
753 * @return array Item array, element in $menuItems
754 * @internal
755 */
756 public function DB_tempMountPoint($page_id) {
757 return $this->linkItem($this->label('tempMountPoint'), IconUtility::getSpriteIcon('apps-pagetree-page-mountpoint'), 'if (top.content.nav_frame) {
758 var node = top.TYPO3.Backend.NavigationContainer.PageTree.getSelected();
759 if (node === null) {
760 return false;
761 }
762
763 var useNode = {
764 attributes: {
765 nodeData: {
766 id: ' . (int)$page_id . '
767 }
768 }
769 };
770
771 node.ownerTree.commandProvider.mountAsTreeRoot(useNode, node.ownerTree);
772 }
773 ');
774 }
775
776 /**
777 * Adding CM element for hide/unhide of the input record
778 *
779 * @param string $table Table name
780 * @param array $rec Record array
781 * @param string $hideField Name of the hide field
782 * @return array Item array, element in $menuItems
783 * @internal
784 */
785 public function DB_hideUnhide($table, $rec, $hideField) {
786 return $this->DB_changeFlag($table, $rec, $hideField, $this->label(($rec[$hideField] ? 'un' : '') . 'hide'), 'hide');
787 }
788
789 /**
790 * Adding CM element for a flag field of the input record
791 *
792 * @param string $table Table name
793 * @param array $rec Record array
794 * @param string $flagField Name of the flag field
795 * @param string $title Menu item Title
796 * @return array Item array, element in $menuItems
797 */
798 public function DB_changeFlag($table, $rec, $flagField, $title) {
799 $uid = $rec['_ORIG_uid'] ?: $rec['uid'];
800 $loc = 'top.content.list_frame';
801 $editOnClick = 'if(' . $loc . '){' . $loc . '.location.href=top.TS.PATH_typo3+\'tce_db.php?redirect=\'' . '+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'' . '&data[' . $table . '][' . $uid . '][' . $flagField . ']=' . ($rec[$flagField] ? 0 : 1) . '&prErr=1&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction') . '\';};';
802 if ($table === 'pages') {
803 $editOnClick .= 'top.nav.refresh.defer(500, top.nav);';
804 }
805 return $this->linkItem($title, IconUtility::getSpriteIcon('actions-edit-' . ($rec[$flagField] ? 'un' : '') . 'hide'), $editOnClick . 'return false;', 1);
806 }
807
808 /***************************************
809 *
810 * FILE
811 *
812 ***************************************/
813 /**
814 * Make 1st level clickmenu:
815 *
816 * @param string $combinedIdentifier The combined identifier
817 * @return string HTML content
818 * @see \TYPO3\CMS\Core\Resource\ResourceFactory::retrieveFileOrFolderObject()
819 */
820 public function printFileClickMenu($combinedIdentifier) {
821 $icon = '';
822 $identifier = '';
823 $menuItems = array();
824 $combinedIdentifier = rawurldecode($combinedIdentifier);
825 $fileObject = ResourceFactory::getInstance()
826 ->retrieveFileOrFolderObject($combinedIdentifier);
827 if ($fileObject) {
828 $folder = FALSE;
829 $isStorageRoot = FALSE;
830 $isOnline = TRUE;
831 $userMayViewStorage = FALSE;
832 $userMayEditStorage = FALSE;
833 $identifier = $fileObject->getCombinedIdentifier();
834 if ($fileObject instanceof Folder) {
835 $icon = IconUtility::getSpriteIconForResource($fileObject, array(
836 'class' => 'absmiddle',
837 'title' => htmlspecialchars($fileObject->getName())
838 ));
839 $folder = TRUE;
840 if ($fileObject->getIdentifier() === $fileObject->getStorage()->getRootLevelFolder()->getIdentifier()) {
841 $isStorageRoot = TRUE;
842 if ($this->backendUser->check('tables_select', 'sys_file_storage')) {
843 $userMayViewStorage = TRUE;
844 }
845 if ($this->backendUser->check('tables_modify', 'sys_file_storage')) {
846 $userMayEditStorage = TRUE;
847 }
848 }
849 if (!$fileObject->getStorage()->isOnline()) {
850 $isOnline = FALSE;
851 }
852 } else {
853 $icon = IconUtility::getSpriteIconForResource($fileObject, array(
854 'class' => 'absmiddle',
855 'title' => htmlspecialchars($fileObject->getName() . ' (' . GeneralUtility::formatSize($fileObject->getSize()) . ')')
856 ));
857 }
858 // Hide
859 if (!in_array('hide', $this->disabledItems) && $isStorageRoot && $userMayEditStorage) {
860 $record = BackendUtility::getRecord('sys_file_storage', $fileObject->getStorage()->getUid());
861 $menuItems['hide'] = $this->DB_changeFlag(
862 'sys_file_storage',
863 $record,
864 'is_online',
865 $this->label($record['is_online'] ? 'offline' : 'online'),
866 'hide'
867 );
868 }
869 // Edit
870 if (!in_array('edit', $this->disabledItems) && $fileObject->checkActionPermission('write')) {
871 if (!$folder && !$isStorageRoot && $fileObject->isIndexed() && $this->backendUser->check('tables_modify', 'sys_file_metadata')) {
872 $metaData = $fileObject->_getMetaData();
873 $menuItems['edit2'] = $this->DB_edit('sys_file_metadata', $metaData['uid']);
874 }
875 if (!$folder && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileObject->getExtension()) && $fileObject->checkActionPermission('write')) {
876 $menuItems['edit'] = $this->FILE_launch($identifier, 'file_edit', 'editcontent', 'actions-page-open');
877 } elseif ($isStorageRoot && $userMayEditStorage) {
878 $menuItems['edit'] = $this->DB_edit('sys_file_storage', $fileObject->getStorage()->getUid());
879 }
880 }
881 // Rename
882 if (!in_array('rename', $this->disabledItems) && !$isStorageRoot && $fileObject->checkActionPermission('rename')) {
883 $menuItems['rename'] = $this->FILE_launch($identifier, 'file_rename', 'rename', 'actions-edit-rename');
884 }
885 // Upload
886 if (!in_array('upload', $this->disabledItems) && $folder && $isOnline && $fileObject->checkActionPermission('write')) {
887 $menuItems['upload'] = $this->FILE_launch($identifier, 'file_upload', 'upload', 'actions-edit-upload');
888 }
889 // New
890 if (!in_array('new', $this->disabledItems) && $folder && $isOnline && $fileObject->checkActionPermission('write')) {
891 $menuItems['new'] = $this->FILE_launch($identifier, 'file_newfolder', 'new', 'actions-document-new');
892 }
893 // Info
894 if (!in_array('info', $this->disabledItems) && $fileObject->checkActionPermission('read')) {
895 if ($isStorageRoot && $userMayViewStorage) {
896 $menuItems['info'] = $this->DB_info('sys_file_storage', $fileObject->getStorage()->getUid());
897 } elseif (!$folder) {
898 $menuItems['info'] = $this->fileInfo($identifier);
899 }
900 }
901 $menuItems[] = 'spacer';
902 // Copy:
903 if (!in_array('copy', $this->disabledItems) && !$isStorageRoot && $fileObject->checkActionPermission('read')) {
904 $menuItems['copy'] = $this->FILE_copycut($identifier, 'copy');
905 }
906 // Cut:
907 if (!in_array('cut', $this->disabledItems) && !$isStorageRoot && $fileObject->checkActionPermission('move')) {
908 $menuItems['cut'] = $this->FILE_copycut($identifier, 'cut');
909 }
910 // Paste:
911 $elFromAllTables = count($this->clipObj->elFromTable('_FILE'));
912 if (!in_array('paste', $this->disabledItems) && $elFromAllTables && $folder && $fileObject->checkActionPermission('write')) {
913 $elArr = $this->clipObj->elFromTable('_FILE');
914 $selItem = reset($elArr);
915 $elInfo = array(
916 basename($selItem),
917 basename($identifier),
918 $this->clipObj->currentMode()
919 );
920 $menuItems['pasteinto'] = $this->FILE_paste($identifier, $selItem, $elInfo);
921 }
922 $menuItems[] = 'spacer';
923 // Delete:
924 if (!in_array('delete', $this->disabledItems) && $fileObject->checkActionPermission('delete')) {
925 if ($isStorageRoot && $userMayEditStorage) {
926 $elInfo = array(GeneralUtility::fixed_lgd_cs($fileObject->getStorage()->getName(), $this->backendUser->uc['titleLen']));
927 $menuItems['delete'] = $this->DB_delete('sys_file_storage', $fileObject->getStorage()->getUid(), $elInfo);
928 } elseif (!$isStorageRoot) {
929 $menuItems['delete'] = $this->FILE_delete($identifier);
930 }
931 }
932 }
933 // Adding external elements to the menuItems array
934 $menuItems = $this->processingByExtClassArray($menuItems, $identifier, 0);
935 // Processing by external functions?
936 $menuItems = $this->externalProcessingOfFileMenuItems($menuItems);
937 // Return the printed elements:
938 return $this->printItems($menuItems, $icon . $fileObject->getName());
939 }
940
941 /**
942 * Processing the $menuItems array (for extension classes) (FILES)
943 *
944 * @param array $menuItems Array for manipulation.
945 * @return array Processed $menuItems array
946 */
947 public function externalProcessingOfFileMenuItems($menuItems) {
948 return $menuItems;
949 }
950
951 /**
952 * Multi-function for adding an entry to the $menuItems array
953 *
954 * @param string $path Path to the file/directory (target)
955 * @param string $moduleName Script (deprecated) or module name (e.g. file_edit) to pass &target= to
956 * @param string $type "type" is the code which fetches the correct label for the element from "cm.
957 * @param string $iconName
958 * @param bool $noReturnUrl If set, the return URL parameter will not be set in the link
959 * @return array Item array, element in $menuItems
960 * @internal
961 */
962 public function FILE_launch($path, $moduleName, $type, $iconName, $noReturnUrl = FALSE) {
963 $loc = 'top.content.list_frame';
964
965 if (strpos($moduleName, '.php') !== FALSE) {
966 GeneralUtility::deprecationLog(
967 'Using a php file directly in ClickMenu is deprecated since TYPO3 CMS 7, and will be removed in CMS 8.'
968 . ' Register the class as module and use BackendUtility::getModuleUrl() to get the right link.'
969 . ' For examples how to do this see ext_tables.php of EXT:backend.'
970 );
971 $scriptUrl = $moduleName;
972 } else {
973 $scriptUrl = BackendUtility::getModuleUrl($moduleName);
974 }
975
976 $editOnClick = 'if(' . $loc . '){' . $loc . '.location.href=top.TS.PATH_typo3+' . GeneralUtility::quoteJSvalue($scriptUrl . '&target=' . rawurlencode($path)) . ($noReturnUrl ? '' : '+\'&returnUrl=\'+top.rawurlencode(' . $this->frameLocation($loc . '.document') . '.pathname+' . $this->frameLocation($loc . '.document') . '.search)') . ';}';
977 return $this->linkItem($this->label($type), IconUtility::getSpriteIcon($iconName), $editOnClick . 'top.nav.refresh();');
978 }
979
980 /**
981 * Returns element for copy or cut of files.
982 *
983 * @param string $path Path to the file/directory (target)
984 * @param string $type Type: "copy" or "cut
985 * @return array Item array, element in $menuItems
986 * @internal
987 */
988 public function FILE_copycut($path, $type) {
989 $isSel = '';
990 // Pseudo table name for use in the clipboard.
991 $table = '_FILE';
992 $uid = GeneralUtility::shortmd5($path);
993 if ($this->clipObj->current === 'normal') {
994 $isSel = $this->clipObj->isSelected($table, $uid);
995 }
996 $addParam = array();
997 if ($this->listFrame) {
998 $addParam['reloadListFrame'] = $this->alwaysContentFrame ? 2 : 1;
999 }
1000 return $this->linkItem($this->label($type), IconUtility::getSpriteIcon('actions-edit-' . $type . ($isSel === $type ? '-release' : '')), 'TYPO3.ClickMenu.fetch(' . GeneralUtility::quoteJSvalue($this->clipObj->selUrlFile($path, ($type === 'copy' ? 1 : 0), ($isSel == $type), $addParam)) . ');return false;');
1001 }
1002
1003 /**
1004 * Creates element for deleting of target
1005 *
1006 * @param string $path Path to the file/directory (target)
1007 * @return array Item array, element in $menuItems
1008 * @internal
1009 */
1010 public function FILE_delete($path) {
1011 $loc = 'top.content.list_frame';
1012 if ($this->backendUser->jsConfirmation(4)) {
1013 $conf = 'confirm(' . GeneralUtility::quoteJSvalue((sprintf($this->languageService->sL('LLL:EXT:lang/locallang_core.xlf:mess.delete'), basename($path)) . BackendUtility::referenceCount('_FILE', $path, ' (There are %s reference(s) to this file!)'))) . ')';
1014 } else {
1015 $conf = '1==1';
1016 }
1017 $editOnClick = 'if(' . $loc . ' && ' . $conf . ' ){' . $loc . '.location.href=top.TS.PATH_typo3+\'tce_file.php?redirect=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'' . '&file[delete][0][data]=' . rawurlencode($path) . '&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction') . '\';};';
1018 return $this->linkItem($this->label('delete'), IconUtility::getSpriteIcon('actions-edit-delete'), $editOnClick . 'return false;');
1019 }
1020
1021 /**
1022 * Creates element for pasting files.
1023 *
1024 * @param string $path Path to the file/directory (target)
1025 * @param string $target target - NOT USED.
1026 * @param array $elInfo Various values for the labels.
1027 * @return array Item array, element in $menuItems
1028 * @internal
1029 */
1030 public function FILE_paste($path, $target, $elInfo) {
1031 $loc = 'top.content.list_frame';
1032 if ($this->backendUser->jsConfirmation(2)) {
1033 $conf = $loc . ' && confirm(' . GeneralUtility::quoteJSvalue(sprintf($this->languageService->sL(('LLL:EXT:lang/locallang_core.xlf:mess.' . ($elInfo[2] === 'copy' ? 'copy' : 'move') . '_into')), $elInfo[0], $elInfo[1])) . ')';
1034 } else {
1035 $conf = $loc;
1036 }
1037 $editOnClick = 'if(' . $conf . '){' . $loc . '.location.href=top.TS.PATH_typo3+\'' . $this->clipObj->pasteUrl('_FILE', $path, 0) . '&redirect=\'+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search); };top.nav.refresh();';
1038 return $this->linkItem($this->label('pasteinto'), IconUtility::getSpriteIcon('actions-document-paste-into'), $editOnClick . 'return false;');
1039 }
1040
1041 /**
1042 * Adding ClickMenu element for file info
1043 *
1044 * @param string $identifier The combined identifier of the file.
1045 * @return array Item array, element in $menuItems
1046 */
1047 protected function fileInfo($identifier) {
1048 return $this->DB_info('_FILE', $identifier);
1049 }
1050
1051 /***************************************
1052 *
1053 * DRAG AND DROP
1054 *
1055 ***************************************/
1056 /**
1057 * Make 1st level clickmenu:
1058 *
1059 * @param string $table The absolute path
1060 * @param int $srcId UID for the current record.
1061 * @param int $dstId Destination ID
1062 * @return string HTML content
1063 */
1064 public function printDragDropClickMenu($table, $srcId, $dstId) {
1065 $menuItems = array();
1066 // If the drag and drop menu should apply to PAGES use this set of menu items
1067 if ($table === 'pages') {
1068 // Move Into:
1069 $menuItems['movePage_into'] = $this->dragDrop_copymovepage($srcId, $dstId, 'move', 'into');
1070 // Move After:
1071 $menuItems['movePage_after'] = $this->dragDrop_copymovepage($srcId, $dstId, 'move', 'after');
1072 // Copy Into:
1073 $menuItems['copyPage_into'] = $this->dragDrop_copymovepage($srcId, $dstId, 'copy', 'into');
1074 // Copy After:
1075 $menuItems['copyPage_after'] = $this->dragDrop_copymovepage($srcId, $dstId, 'copy', 'after');
1076 }
1077 // If the drag and drop menu should apply to FOLDERS use this set of menu items
1078 if ($table === 'folders') {
1079 // Move Into:
1080 $menuItems['moveFolder_into'] = $this->dragDrop_copymovefolder($srcId, $dstId, 'move');
1081 // Copy Into:
1082 $menuItems['copyFolder_into'] = $this->dragDrop_copymovefolder($srcId, $dstId, 'copy');
1083 }
1084 // Adding external elements to the menuItems array
1085 $menuItems = $this->processingByExtClassArray($menuItems, 'dragDrop_' . $table, $srcId);
1086 // to extend this, you need to apply a Context Menu to a "virtual" table called "dragDrop_pages" or similar
1087 // Processing by external functions?
1088 $menuItems = $this->externalProcessingOfDBMenuItems($menuItems);
1089 // Return the printed elements:
1090 return $this->printItems($menuItems, IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => BackendUtility::getRecordTitle($table, $this->rec, TRUE))));
1091 }
1092
1093 /**
1094 * Processing the $menuItems array (for extension classes) (DRAG'N DROP)
1095 *
1096 * @param array $menuItems Array for manipulation.
1097 * @return array Processed $menuItems array
1098 */
1099 public function externalProcessingOfDragDropMenuItems($menuItems) {
1100 return $menuItems;
1101 }
1102
1103 /**
1104 * Adding CM element for Copying/Moving a Page Into/After from a drag & drop action
1105 *
1106 * @param int $srcUid source UID code for the record to modify
1107 * @param int $dstUid destination UID code for the record to modify
1108 * @param string $action Action code: either "move" or "copy
1109 * @param string $into Parameter code: either "into" or "after
1110 * @return array Item array, element in $menuItems
1111 * @internal
1112 */
1113 public function dragDrop_copymovepage($srcUid, $dstUid, $action, $into) {
1114 $negativeSign = $into === 'into' ? '' : '-';
1115 $loc = 'top.content.list_frame';
1116 $editOnClick = 'if(' . $loc . '){' . $loc . '.document.location=top.TS.PATH_typo3+"tce_db.php?redirect="+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+"' . '&cmd[pages][' . $srcUid . '][' . $action . ']=' . $negativeSign . $dstUid . '&prErr=1&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction') . '";};top.nav.refresh();';
1117 return $this->linkItem($this->label($action . 'Page_' . $into), IconUtility::getSpriteIcon('actions-document-paste-' . $into), $editOnClick . 'return false;', 0);
1118 }
1119
1120 /**
1121 * Adding CM element for Copying/Moving a Folder Into from a drag & drop action
1122 *
1123 * @param string $srcPath source path for the record to modify
1124 * @param string $dstPath destination path for the records to modify
1125 * @param string $action Action code: either "move" or "copy
1126 * @return array Item array, element in $menuItems
1127 * @internal
1128 */
1129 public function dragDrop_copymovefolder($srcPath, $dstPath, $action) {
1130 $loc = 'top.content.list_frame';
1131 $editOnClick = 'if(' . $loc . '){' . $loc . '.document.location=top.TS.PATH_typo3+"tce_file.php?redirect="+top.rawurlencode(' . $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+"' . '&file[' . $action . '][0][data]=' . $srcPath . '&file[' . $action . '][0][target]=' . $dstPath . '&prErr=1&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction') . '";};top.nav.refresh();';
1132 return $this->linkItem($this->label($action . 'Folder_into'), IconUtility::getSpriteIcon('apps-pagetree-drag-move-into'), $editOnClick . 'return false;', 0);
1133 }
1134
1135 /***************************************
1136 *
1137 * COMMON
1138 *
1139 **************************************/
1140 /**
1141 * Prints the items from input $menuItems array - as JS section for writing to the div-layers.
1142 *
1143 * @param array $menuItems Array
1144 * @return string HTML code
1145 */
1146 public function printItems($menuItems) {
1147 // Enable/Disable items
1148 $menuItems = $this->enableDisableItems($menuItems);
1149 // Clean up spacers
1150 $menuItems = $this->cleanUpSpacers($menuItems);
1151 // Adding JS part and return the content
1152 return $this->printLayerJScode($menuItems);
1153 }
1154
1155 /**
1156 * Create the JavaScript section
1157 *
1158 * @param array $menuItems The $menuItems array to print
1159 * @return string The JavaScript section which will print the content of the CM to the div-layer in the target frame.
1160 */
1161 public function printLayerJScode($menuItems) {
1162 // Clipboard must not be submitted - then it's probably a copy/cut situation.
1163 if ($this->isCMlayers()) {
1164 // Create the table displayed in the clickmenu layer:
1165 // Wrap the inner table in another table to create outer border:
1166 $CMtable = '
1167 <div class="typo3-CSM-wrapperCM">
1168 <table border="0" cellpadding="0" cellspacing="0" class="typo3-CSM">
1169 ' . implode('', $this->menuItemsForClickMenu($menuItems)) . '
1170 </table></div>';
1171 return '<data><clickmenu><htmltable><![CDATA[' . $CMtable . ']]></htmltable><cmlevel>' . $this->cmLevel . '</cmlevel></clickmenu></data>';
1172 }
1173 }
1174
1175 /**
1176 * Wrapping the input string in a table with background color 4 and a black border style.
1177 * For the pop-up menu
1178 *
1179 * @param string $str HTML content to wrap in table.
1180 * @return string
1181 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1182 */
1183 public function wrapColorTableCM($str) {
1184 GeneralUtility::logDeprecatedFunction();
1185 return '<div class="typo3-CSM-wrapperCM">
1186 ' . $str . '
1187 </div>';
1188 }
1189
1190 /**
1191 * Traverses the menuItems and generates an output array for implosion in the CM div-layers table.
1192 *
1193 * @param array $menuItems Array
1194 * @return array array for implosion in the CM div-layers table.
1195 */
1196 public function menuItemsForClickMenu($menuItems) {
1197 $out = array();
1198 foreach ($menuItems as $cc => $i) {
1199 // MAKE horizontal spacer
1200 if (is_string($i) && $i === 'spacer') {
1201 $out[] = '
1202 <tr style="height: 1px;" class="bgColor2">
1203 <td colspan="2"></td>
1204 </tr>';
1205 } else {
1206 // Just make normal element:
1207 $onClick = $i[3];
1208 $onClick = preg_replace('/return[[:space:]]+hideCM\\(\\)[[:space:]]*;/i', '', $onClick);
1209 $onClick = preg_replace('/return[[:space:]]+false[[:space:]]*;/i', '', $onClick);
1210 $onClick = preg_replace('/hideCM\\(\\);/i', '', $onClick);
1211 if (!$i[5]) {
1212 $onClick .= 'TYPO3.ClickMenu.hideAll();';
1213 }
1214 $CSM = ' oncontextmenu="this.click();return false;"';
1215 $out[] = '
1216 <tr class="typo3-CSM-itemRow" onclick="' . htmlspecialchars($onClick) . '"' . $CSM . '>
1217 ' . (!$this->leftIcons ? '<td class="typo3-CSM-item">' . $i[1] . '</td><td align="center">' . $i[2] . '</td>' : '<td align="center">' . $i[2] . '</td><td class="typo3-CSM-item">' . $i[1] . '</td>') . '
1218 </tr>';
1219 }
1220 }
1221 return $out;
1222 }
1223
1224 /**
1225 * Adds or inserts a menu item
1226 * Can be used to set the position of new menu entries within the list of existing menu entries. Has this syntax: [cmd]:[menu entry key],[cmd].... cmd can be "after", "before" or "top" (or blank/"bottom" which is default). If "after"/"before" then menu items will be inserted after/before the existing entry with [menu entry key] if found. "after-spacer" and "before-spacer" do the same, but inserts before or after an item and a spacer. If not found, the bottom of list. If "top" the items are inserted in the top of the list.
1227 *
1228 * @param array $menuItems Menu items array
1229 * @param array $newMenuItems Menu items array to insert
1230 * @param string $position Position command string. Has this syntax: [cmd]:[menu entry key],[cmd].... cmd can be "after", "before" or "top" (or blank/"bottom" which is default). If "after"/"before" then menu items will be inserted after/before the existing entry with [menu entry key] if found. "after-spacer" and "before-spacer" do the same, but inserts before or after an item and a spacer. If not found, the bottom of list. If "top" the items are inserted in the top of the list.
1231 * @return array Menu items array, processed.
1232 */
1233 public function addMenuItems($menuItems, $newMenuItems, $position = '') {
1234 if (is_array($newMenuItems)) {
1235 if ($position) {
1236 $posArr = GeneralUtility::trimExplode(',', $position, TRUE);
1237 foreach ($posArr as $pos) {
1238 list($place, $menuEntry) = GeneralUtility::trimExplode(':', $pos, TRUE);
1239 list($place, $placeExtra) = GeneralUtility::trimExplode('-', $place, TRUE);
1240 // Bottom
1241 $pointer = count($menuItems);
1242 $found = FALSE;
1243 if ($place) {
1244 switch (strtolower($place)) {
1245 case 'after':
1246 case 'before':
1247 if ($menuEntry) {
1248 $p = 1;
1249 reset($menuItems);
1250 while (TRUE) {
1251 if ((string)key($menuItems) === $menuEntry) {
1252 $pointer = $p;
1253 $found = TRUE;
1254 break;
1255 }
1256 if (!next($menuItems)) {
1257 break;
1258 }
1259 $p++;
1260 }
1261 if (!$found) {
1262 break;
1263 }
1264 if ($place === 'before') {
1265 $pointer--;
1266 if ($placeExtra === 'spacer' and prev($menuItems) === 'spacer') {
1267 $pointer--;
1268 }
1269 } elseif ($place === 'after') {
1270 if ($placeExtra === 'spacer' and next($menuItems) === 'spacer') {
1271 $pointer++;
1272 }
1273 }
1274 }
1275 break;
1276 default:
1277 if (strtolower($place) === 'top') {
1278 $pointer = 0;
1279 } else {
1280 $pointer = count($menuItems);
1281 }
1282 $found = TRUE;
1283 }
1284 }
1285 if ($found) {
1286 break;
1287 }
1288 }
1289 }
1290 $pointer = max(0, $pointer);
1291 $menuItemsBefore = array_slice($menuItems, 0, $pointer ?: 0);
1292 $menuItemsAfter = array_slice($menuItems, $pointer);
1293 $menuItems = $menuItemsBefore + $newMenuItems + $menuItemsAfter;
1294 }
1295 return $menuItems;
1296 }
1297
1298 /**
1299 * Creating an array with various elements for the clickmenu entry
1300 *
1301 * @param string $str The label, htmlspecialchar'ed already
1302 * @param string $icon <img>-tag for the icon
1303 * @param string $onClick JavaScript onclick event for label/icon
1304 * @param int $onlyCM ==1 and the element will NOT appear in clickmenus in the topframe (unless clickmenu is totally unavailable)! ==2 and the item will NEVER appear in top frame. (This is mostly for "less important" options since the top frame is not capable of holding so many elements horizontally)
1305 * @param bool $dontHide If set, the clickmenu layer will not hide itself onclick - used for secondary menus to appear...
1306 * @return array $menuItem entry with 6 numerical entries: [0] is the HTML for display of the element with link and icon an mouseover etc., [1]-[5] is simply the input params passed through!
1307 */
1308 public function linkItem($str, $icon, $onClick, $onlyCM = 0, $dontHide = 0) {
1309 $onClick = str_replace('top.loadTopMenu', 'showClickmenu_raw', $onClick);
1310 return array(
1311 '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $str . $icon . '</a>',
1312 $str,
1313 $icon,
1314 $onClick,
1315 $onlyCM,
1316 $dontHide
1317 );
1318 }
1319
1320 /**
1321 * Returns the input string IF not a user setting has disabled display of icons.
1322 *
1323 * @param string $iconCode The icon-image tag
1324 * @return string The icon-image tag prefixed with space char IF the icon should be printed at all due to user settings
1325 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1326 */
1327 public function excludeIcon($iconCode) {
1328 GeneralUtility::logDeprecatedFunction();
1329 return $this->backendUser->uc['noMenuMode'] && $this->backendUser->uc['noMenuMode'] !== 'icons' ? '' : ' ' . $iconCode;
1330 }
1331
1332 /**
1333 * Enabling / Disabling items based on list provided from GET var ($this->iParts[3])
1334 *
1335 * @param array $menuItems Menu items array
1336 * @return array Menu items array, processed.
1337 */
1338 public function enableDisableItems($menuItems) {
1339 if ($this->iParts[3]) {
1340 // Detect "only" mode: (only showing listed items)
1341 if ($this->iParts[3][0] === '+') {
1342 $this->iParts[3] = substr($this->iParts[3], 1);
1343 $only = TRUE;
1344 } else {
1345 $only = FALSE;
1346 }
1347 // Do filtering:
1348 // Transfer ONLY elements which are mentioned (or are spacers)
1349 if ($only) {
1350 $newMenuArray = array();
1351 foreach ($menuItems as $key => $value) {
1352 if (GeneralUtility::inList($this->iParts[3], $key) || is_string($value) && $value === 'spacer') {
1353 $newMenuArray[$key] = $value;
1354 }
1355 }
1356 $menuItems = $newMenuArray;
1357 } else {
1358 // Traverse all elements except those listed (just unsetting them):
1359 $elements = GeneralUtility::trimExplode(',', $this->iParts[3], TRUE);
1360 foreach ($elements as $value) {
1361 unset($menuItems[$value]);
1362 }
1363 }
1364 }
1365 // Return processed menu items:
1366 return $menuItems;
1367 }
1368
1369 /**
1370 * Clean up spacers; Will remove any spacers in the start/end of menu items array plus any duplicates.
1371 *
1372 * @param array $menuItems Menu items array
1373 * @return array Menu items array, processed.
1374 */
1375 public function cleanUpSpacers($menuItems) {
1376 // Remove doubles:
1377 $prevItemWasSpacer = FALSE;
1378 foreach ($menuItems as $key => $value) {
1379 if (is_string($value) && $value === 'spacer') {
1380 if ($prevItemWasSpacer) {
1381 unset($menuItems[$key]);
1382 }
1383 $prevItemWasSpacer = TRUE;
1384 } else {
1385 $prevItemWasSpacer = FALSE;
1386 }
1387 }
1388 // Remove first:
1389 reset($menuItems);
1390 $key = key($menuItems);
1391 $value = current($menuItems);
1392 if (is_string($value) && $value === 'spacer') {
1393 unset($menuItems[$key]);
1394 }
1395 // Remove last:
1396 end($menuItems);
1397 $key = key($menuItems);
1398 $value = current($menuItems);
1399 if (is_string($value) && $value === 'spacer') {
1400 unset($menuItems[$key]);
1401 }
1402 // Return processed menu items:
1403 return $menuItems;
1404 }
1405
1406 /**
1407 * Get label from locallang_core.xlf:cm.*
1408 *
1409 * @param string $label The "cm."-suffix to get.
1410 * @return string
1411 */
1412 public function label($label) {
1413 return $this->languageService->makeEntities($this->languageService->sL('LLL:EXT:lang/locallang_core.xlf:cm.' . $label, TRUE));
1414 }
1415
1416 /**
1417 * Returns TRUE if there should be writing to the div-layers (commands sent to clipboard MUST NOT write to div-layers)
1418 *
1419 * @return bool
1420 */
1421 public function isCMlayers() {
1422 return !$this->CB;
1423 }
1424
1425 /**
1426 * Appends ".location" to input string
1427 *
1428 * @param string $str Input string, probably a JavaScript document reference
1429 * @return string
1430 */
1431 public function frameLocation($str) {
1432 return $str . '.location';
1433 }
1434
1435 }