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