[TASK] Make tce_db.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+\'' . BackendUtility::getModuleUrl('tce_db') . '&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+\'' . BackendUtility::getModuleUrl('tce_db') . '&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 $clickedFileOrFolder = ResourceFactory::getInstance()->retrieveFileOrFolderObject($combinedIdentifier);
921 $fileOrFolderInClipBoard = ResourceFactory::getInstance()->retrieveFileOrFolderObject($selItem);
922 if (!$fileOrFolderInClipBoard instanceof Folder || !$fileOrFolderInClipBoard->getStorage()->isWithinFolder($fileOrFolderInClipBoard, $clickedFileOrFolder)) {
923 $menuItems['pasteinto'] = $this->FILE_paste($identifier, $selItem, $elInfo);
924 }
925 }
926 $menuItems[] = 'spacer';
927 // Delete:
928 if (!in_array('delete', $this->disabledItems) && $fileObject->checkActionPermission('delete')) {
929 if ($isStorageRoot && $userMayEditStorage) {
930 $elInfo = array(GeneralUtility::fixed_lgd_cs($fileObject->getStorage()->getName(), $this->backendUser->uc['titleLen']));
931 $menuItems['delete'] = $this->DB_delete('sys_file_storage', $fileObject->getStorage()->getUid(), $elInfo);
932 } elseif (!$isStorageRoot) {
933 $menuItems['delete'] = $this->FILE_delete($identifier);
934 }
935 }
936 }
937 // Adding external elements to the menuItems array
938 $menuItems = $this->processingByExtClassArray($menuItems, $identifier, 0);
939 // Processing by external functions?
940 $menuItems = $this->externalProcessingOfFileMenuItems($menuItems);
941 // Return the printed elements:
942 return $this->printItems($menuItems, $icon . $fileObject->getName());
943 }
944
945 /**
946 * Processing the $menuItems array (for extension classes) (FILES)
947 *
948 * @param array $menuItems Array for manipulation.
949 * @return array Processed $menuItems array
950 */
951 public function externalProcessingOfFileMenuItems($menuItems) {
952 return $menuItems;
953 }
954
955 /**
956 * Multi-function for adding an entry to the $menuItems array
957 *
958 * @param string $path Path to the file/directory (target)
959 * @param string $moduleName Script (deprecated) or module name (e.g. file_edit) to pass &target= to
960 * @param string $type "type" is the code which fetches the correct label for the element from "cm.
961 * @param string $iconName
962 * @param bool $noReturnUrl If set, the return URL parameter will not be set in the link
963 * @return array Item array, element in $menuItems
964 * @internal
965 */
966 public function FILE_launch($path, $moduleName, $type, $iconName, $noReturnUrl = FALSE) {
967 $loc = 'top.content.list_frame';
968
969 if (strpos($moduleName, '.php') !== FALSE) {
970 GeneralUtility::deprecationLog(
971 'Using a php file directly in ClickMenu is deprecated since TYPO3 CMS 7, and will be removed in CMS 8.'
972 . ' Register the class as module and use BackendUtility::getModuleUrl() to get the right link.'
973 . ' For examples how to do this see ext_tables.php of EXT:backend.'
974 );
975 $scriptUrl = $moduleName;
976 } else {
977 $scriptUrl = BackendUtility::getModuleUrl($moduleName);
978 }
979
980 $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)') . ';}';
981 return $this->linkItem($this->label($type), IconUtility::getSpriteIcon($iconName), $editOnClick . 'top.nav.refresh();');
982 }
983
984 /**
985 * Returns element for copy or cut of files.
986 *
987 * @param string $path Path to the file/directory (target)
988 * @param string $type Type: "copy" or "cut
989 * @return array Item array, element in $menuItems
990 * @internal
991 */
992 public function FILE_copycut($path, $type) {
993 $isSel = '';
994 // Pseudo table name for use in the clipboard.
995 $table = '_FILE';
996 $uid = GeneralUtility::shortmd5($path);
997 if ($this->clipObj->current === 'normal') {
998 $isSel = $this->clipObj->isSelected($table, $uid);
999 }
1000 $addParam = array();
1001 if ($this->listFrame) {
1002 $addParam['reloadListFrame'] = $this->alwaysContentFrame ? 2 : 1;
1003 }
1004 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;');
1005 }
1006
1007 /**
1008 * Creates element for deleting of target
1009 *
1010 * @param string $path Path to the file/directory (target)
1011 * @return array Item array, element in $menuItems
1012 * @internal
1013 */
1014 public function FILE_delete($path) {
1015 $loc = 'top.content.list_frame';
1016 if ($this->backendUser->jsConfirmation(4)) {
1017 $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!)'))) . ')';
1018 } else {
1019 $conf = '1==1';
1020 }
1021 $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') . '\';};';
1022 return $this->linkItem($this->label('delete'), IconUtility::getSpriteIcon('actions-edit-delete'), $editOnClick . 'return false;');
1023 }
1024
1025 /**
1026 * Creates element for pasting files.
1027 *
1028 * @param string $path Path to the file/directory (target)
1029 * @param string $target target - NOT USED.
1030 * @param array $elInfo Various values for the labels.
1031 * @return array Item array, element in $menuItems
1032 * @internal
1033 */
1034 public function FILE_paste($path, $target, $elInfo) {
1035 $loc = 'top.content.list_frame';
1036 if ($this->backendUser->jsConfirmation(2)) {
1037 $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])) . ')';
1038 } else {
1039 $conf = $loc;
1040 }
1041 $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();';
1042 return $this->linkItem($this->label('pasteinto'), IconUtility::getSpriteIcon('actions-document-paste-into'), $editOnClick . 'return false;');
1043 }
1044
1045 /**
1046 * Adding ClickMenu element for file info
1047 *
1048 * @param string $identifier The combined identifier of the file.
1049 * @return array Item array, element in $menuItems
1050 */
1051 protected function fileInfo($identifier) {
1052 return $this->DB_info('_FILE', $identifier);
1053 }
1054
1055 /***************************************
1056 *
1057 * DRAG AND DROP
1058 *
1059 ***************************************/
1060 /**
1061 * Make 1st level clickmenu:
1062 *
1063 * @param string $table The absolute path
1064 * @param int $srcId UID for the current record.
1065 * @param int $dstId Destination ID
1066 * @return string HTML content
1067 */
1068 public function printDragDropClickMenu($table, $srcId, $dstId) {
1069 $menuItems = array();
1070 // If the drag and drop menu should apply to PAGES use this set of menu items
1071 if ($table === 'pages') {
1072 // Move Into:
1073 $menuItems['movePage_into'] = $this->dragDrop_copymovepage($srcId, $dstId, 'move', 'into');
1074 // Move After:
1075 $menuItems['movePage_after'] = $this->dragDrop_copymovepage($srcId, $dstId, 'move', 'after');
1076 // Copy Into:
1077 $menuItems['copyPage_into'] = $this->dragDrop_copymovepage($srcId, $dstId, 'copy', 'into');
1078 // Copy After:
1079 $menuItems['copyPage_after'] = $this->dragDrop_copymovepage($srcId, $dstId, 'copy', 'after');
1080 }
1081 // If the drag and drop menu should apply to FOLDERS use this set of menu items
1082 if ($table === 'folders') {
1083 // Move Into:
1084 $menuItems['moveFolder_into'] = $this->dragDrop_copymovefolder($srcId, $dstId, 'move');
1085 // Copy Into:
1086 $menuItems['copyFolder_into'] = $this->dragDrop_copymovefolder($srcId, $dstId, 'copy');
1087 }
1088 // Adding external elements to the menuItems array
1089 $menuItems = $this->processingByExtClassArray($menuItems, 'dragDrop_' . $table, $srcId);
1090 // to extend this, you need to apply a Context Menu to a "virtual" table called "dragDrop_pages" or similar
1091 // Processing by external functions?
1092 $menuItems = $this->externalProcessingOfDBMenuItems($menuItems);
1093 // Return the printed elements:
1094 return $this->printItems($menuItems, IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => BackendUtility::getRecordTitle($table, $this->rec, TRUE))));
1095 }
1096
1097 /**
1098 * Processing the $menuItems array (for extension classes) (DRAG'N DROP)
1099 *
1100 * @param array $menuItems Array for manipulation.
1101 * @return array Processed $menuItems array
1102 */
1103 public function externalProcessingOfDragDropMenuItems($menuItems) {
1104 return $menuItems;
1105 }
1106
1107 /**
1108 * Adding CM element for Copying/Moving a Page Into/After from a drag & drop action
1109 *
1110 * @param int $srcUid source UID code for the record to modify
1111 * @param int $dstUid destination UID code for the record to modify
1112 * @param string $action Action code: either "move" or "copy
1113 * @param string $into Parameter code: either "into" or "after
1114 * @return array Item array, element in $menuItems
1115 * @internal
1116 */
1117 public function dragDrop_copymovepage($srcUid, $dstUid, $action, $into) {
1118 $negativeSign = $into === 'into' ? '' : '-';
1119 $loc = 'top.content.list_frame';
1120 $editOnClick = 'if(' . $loc . '){' . $loc . '.document.location=top.TS.PATH_typo3+"' . BackendUtility::getModuleUrl('tce_db') . '&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();';
1121 return $this->linkItem($this->label($action . 'Page_' . $into), IconUtility::getSpriteIcon('actions-document-paste-' . $into), $editOnClick . 'return false;', 0);
1122 }
1123
1124 /**
1125 * Adding CM element for Copying/Moving a Folder Into from a drag & drop action
1126 *
1127 * @param string $srcPath source path for the record to modify
1128 * @param string $dstPath destination path for the records to modify
1129 * @param string $action Action code: either "move" or "copy
1130 * @return array Item array, element in $menuItems
1131 * @internal
1132 */
1133 public function dragDrop_copymovefolder($srcPath, $dstPath, $action) {
1134 $loc = 'top.content.list_frame';
1135 $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();';
1136 return $this->linkItem($this->label($action . 'Folder_into'), IconUtility::getSpriteIcon('apps-pagetree-drag-move-into'), $editOnClick . 'return false;', 0);
1137 }
1138
1139 /***************************************
1140 *
1141 * COMMON
1142 *
1143 **************************************/
1144 /**
1145 * Prints the items from input $menuItems array - as JS section for writing to the div-layers.
1146 *
1147 * @param array $menuItems Array
1148 * @return string HTML code
1149 */
1150 public function printItems($menuItems) {
1151 // Enable/Disable items
1152 $menuItems = $this->enableDisableItems($menuItems);
1153 // Clean up spacers
1154 $menuItems = $this->cleanUpSpacers($menuItems);
1155 // Adding JS part and return the content
1156 return $this->printLayerJScode($menuItems);
1157 }
1158
1159 /**
1160 * Create the JavaScript section
1161 *
1162 * @param array $menuItems The $menuItems array to print
1163 * @return string The JavaScript section which will print the content of the CM to the div-layer in the target frame.
1164 */
1165 public function printLayerJScode($menuItems) {
1166 // Clipboard must not be submitted - then it's probably a copy/cut situation.
1167 if ($this->isCMlayers()) {
1168 // Create the table displayed in the clickmenu layer:
1169 // Wrap the inner table in another table to create outer border:
1170 $CMtable = '
1171 <div class="typo3-CSM-wrapperCM">
1172 <table border="0" cellpadding="0" cellspacing="0" class="typo3-CSM">
1173 ' . implode('', $this->menuItemsForClickMenu($menuItems)) . '
1174 </table></div>';
1175 return '<data><clickmenu><htmltable><![CDATA[' . $CMtable . ']]></htmltable><cmlevel>' . $this->cmLevel . '</cmlevel></clickmenu></data>';
1176 }
1177 }
1178
1179 /**
1180 * Wrapping the input string in a table with background color 4 and a black border style.
1181 * For the pop-up menu
1182 *
1183 * @param string $str HTML content to wrap in table.
1184 * @return string
1185 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1186 */
1187 public function wrapColorTableCM($str) {
1188 GeneralUtility::logDeprecatedFunction();
1189 return '<div class="typo3-CSM-wrapperCM">
1190 ' . $str . '
1191 </div>';
1192 }
1193
1194 /**
1195 * Traverses the menuItems and generates an output array for implosion in the CM div-layers table.
1196 *
1197 * @param array $menuItems Array
1198 * @return array array for implosion in the CM div-layers table.
1199 */
1200 public function menuItemsForClickMenu($menuItems) {
1201 $out = array();
1202 foreach ($menuItems as $cc => $i) {
1203 // MAKE horizontal spacer
1204 if (is_string($i) && $i === 'spacer') {
1205 $out[] = '
1206 <tr style="height: 1px;" class="bgColor2">
1207 <td colspan="2"></td>
1208 </tr>';
1209 } else {
1210 // Just make normal element:
1211 $onClick = $i[3];
1212 $onClick = preg_replace('/return[[:space:]]+hideCM\\(\\)[[:space:]]*;/i', '', $onClick);
1213 $onClick = preg_replace('/return[[:space:]]+false[[:space:]]*;/i', '', $onClick);
1214 $onClick = preg_replace('/hideCM\\(\\);/i', '', $onClick);
1215 if (!$i[5]) {
1216 $onClick .= 'TYPO3.ClickMenu.hideAll();';
1217 }
1218 $CSM = ' oncontextmenu="this.click();return false;"';
1219 $out[] = '
1220 <tr class="typo3-CSM-itemRow" onclick="' . htmlspecialchars($onClick) . '"' . $CSM . '>
1221 ' . (!$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>') . '
1222 </tr>';
1223 }
1224 }
1225 return $out;
1226 }
1227
1228 /**
1229 * Adds or inserts a menu item
1230 * 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.
1231 *
1232 * @param array $menuItems Menu items array
1233 * @param array $newMenuItems Menu items array to insert
1234 * @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.
1235 * @return array Menu items array, processed.
1236 */
1237 public function addMenuItems($menuItems, $newMenuItems, $position = '') {
1238 if (is_array($newMenuItems)) {
1239 if ($position) {
1240 $posArr = GeneralUtility::trimExplode(',', $position, TRUE);
1241 foreach ($posArr as $pos) {
1242 list($place, $menuEntry) = GeneralUtility::trimExplode(':', $pos, TRUE);
1243 list($place, $placeExtra) = GeneralUtility::trimExplode('-', $place, TRUE);
1244 // Bottom
1245 $pointer = count($menuItems);
1246 $found = FALSE;
1247 if ($place) {
1248 switch (strtolower($place)) {
1249 case 'after':
1250 case 'before':
1251 if ($menuEntry) {
1252 $p = 1;
1253 reset($menuItems);
1254 while (TRUE) {
1255 if ((string)key($menuItems) === $menuEntry) {
1256 $pointer = $p;
1257 $found = TRUE;
1258 break;
1259 }
1260 if (!next($menuItems)) {
1261 break;
1262 }
1263 $p++;
1264 }
1265 if (!$found) {
1266 break;
1267 }
1268 if ($place === 'before') {
1269 $pointer--;
1270 if ($placeExtra === 'spacer' and prev($menuItems) === 'spacer') {
1271 $pointer--;
1272 }
1273 } elseif ($place === 'after') {
1274 if ($placeExtra === 'spacer' and next($menuItems) === 'spacer') {
1275 $pointer++;
1276 }
1277 }
1278 }
1279 break;
1280 default:
1281 if (strtolower($place) === 'top') {
1282 $pointer = 0;
1283 } else {
1284 $pointer = count($menuItems);
1285 }
1286 $found = TRUE;
1287 }
1288 }
1289 if ($found) {
1290 break;
1291 }
1292 }
1293 }
1294 $pointer = max(0, $pointer);
1295 $menuItemsBefore = array_slice($menuItems, 0, $pointer ?: 0);
1296 $menuItemsAfter = array_slice($menuItems, $pointer);
1297 $menuItems = $menuItemsBefore + $newMenuItems + $menuItemsAfter;
1298 }
1299 return $menuItems;
1300 }
1301
1302 /**
1303 * Creating an array with various elements for the clickmenu entry
1304 *
1305 * @param string $str The label, htmlspecialchar'ed already
1306 * @param string $icon <img>-tag for the icon
1307 * @param string $onClick JavaScript onclick event for label/icon
1308 * @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)
1309 * @param bool $dontHide If set, the clickmenu layer will not hide itself onclick - used for secondary menus to appear...
1310 * @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!
1311 */
1312 public function linkItem($str, $icon, $onClick, $onlyCM = 0, $dontHide = 0) {
1313 $onClick = str_replace('top.loadTopMenu', 'showClickmenu_raw', $onClick);
1314 return array(
1315 '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $str . $icon . '</a>',
1316 $str,
1317 $icon,
1318 $onClick,
1319 $onlyCM,
1320 $dontHide
1321 );
1322 }
1323
1324 /**
1325 * Returns the input string IF not a user setting has disabled display of icons.
1326 *
1327 * @param string $iconCode The icon-image tag
1328 * @return string The icon-image tag prefixed with space char IF the icon should be printed at all due to user settings
1329 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1330 */
1331 public function excludeIcon($iconCode) {
1332 GeneralUtility::logDeprecatedFunction();
1333 return $this->backendUser->uc['noMenuMode'] && $this->backendUser->uc['noMenuMode'] !== 'icons' ? '' : ' ' . $iconCode;
1334 }
1335
1336 /**
1337 * Enabling / Disabling items based on list provided from GET var ($this->iParts[3])
1338 *
1339 * @param array $menuItems Menu items array
1340 * @return array Menu items array, processed.
1341 */
1342 public function enableDisableItems($menuItems) {
1343 if ($this->iParts[3]) {
1344 // Detect "only" mode: (only showing listed items)
1345 if ($this->iParts[3][0] === '+') {
1346 $this->iParts[3] = substr($this->iParts[3], 1);
1347 $only = TRUE;
1348 } else {
1349 $only = FALSE;
1350 }
1351 // Do filtering:
1352 // Transfer ONLY elements which are mentioned (or are spacers)
1353 if ($only) {
1354 $newMenuArray = array();
1355 foreach ($menuItems as $key => $value) {
1356 if (GeneralUtility::inList($this->iParts[3], $key) || is_string($value) && $value === 'spacer') {
1357 $newMenuArray[$key] = $value;
1358 }
1359 }
1360 $menuItems = $newMenuArray;
1361 } else {
1362 // Traverse all elements except those listed (just unsetting them):
1363 $elements = GeneralUtility::trimExplode(',', $this->iParts[3], TRUE);
1364 foreach ($elements as $value) {
1365 unset($menuItems[$value]);
1366 }
1367 }
1368 }
1369 // Return processed menu items:
1370 return $menuItems;
1371 }
1372
1373 /**
1374 * Clean up spacers; Will remove any spacers in the start/end of menu items array plus any duplicates.
1375 *
1376 * @param array $menuItems Menu items array
1377 * @return array Menu items array, processed.
1378 */
1379 public function cleanUpSpacers($menuItems) {
1380 // Remove doubles:
1381 $prevItemWasSpacer = FALSE;
1382 foreach ($menuItems as $key => $value) {
1383 if (is_string($value) && $value === 'spacer') {
1384 if ($prevItemWasSpacer) {
1385 unset($menuItems[$key]);
1386 }
1387 $prevItemWasSpacer = TRUE;
1388 } else {
1389 $prevItemWasSpacer = FALSE;
1390 }
1391 }
1392 // Remove first:
1393 reset($menuItems);
1394 $key = key($menuItems);
1395 $value = current($menuItems);
1396 if (is_string($value) && $value === 'spacer') {
1397 unset($menuItems[$key]);
1398 }
1399 // Remove last:
1400 end($menuItems);
1401 $key = key($menuItems);
1402 $value = current($menuItems);
1403 if (is_string($value) && $value === 'spacer') {
1404 unset($menuItems[$key]);
1405 }
1406 // Return processed menu items:
1407 return $menuItems;
1408 }
1409
1410 /**
1411 * Get label from locallang_core.xlf:cm.*
1412 *
1413 * @param string $label The "cm."-suffix to get.
1414 * @return string
1415 */
1416 public function label($label) {
1417 return $this->languageService->makeEntities($this->languageService->sL('LLL:EXT:lang/locallang_core.xlf:cm.' . $label, TRUE));
1418 }
1419
1420 /**
1421 * Returns TRUE if there should be writing to the div-layers (commands sent to clipboard MUST NOT write to div-layers)
1422 *
1423 * @return bool
1424 */
1425 public function isCMlayers() {
1426 return !$this->CB;
1427 }
1428
1429 /**
1430 * Appends ".location" to input string
1431 *
1432 * @param string $str Input string, probably a JavaScript document reference
1433 * @return string
1434 */
1435 public function frameLocation($str) {
1436 return $str . '.location';
1437 }
1438
1439 }