[TASK] Apply quoteJSvalue or htmlspecialchars to getModuleUrl
[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+' .
731 GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&redirect=') . '+top.rawurlencode(' .
732 $this->frameLocation($loc . '.document') . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+' .
733 GeneralUtility::quoteJSvalue(
734 '&cmd[' . $table . '][' . $uid . '][delete]=1&prErr=1&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction')
735 ) . ';};';
736 if ($table === 'pages') {
737 $editOnClick .= 'top.nav.refresh.defer(500, top.nav);';
738 }
739 return $this->linkItem($this->label('delete'), IconUtility::getSpriteIcon('actions-edit-delete'), $editOnClick . 'return false;');
740 }
741
742 /**
743 * Adding CM element for View Page
744 *
745 * @param int $id Page uid (PID)
746 * @param string $anchor Anchor, if any
747 * @return array Item array, element in $menuItems
748 * @internal
749 */
750 public function DB_view($id, $anchor = '') {
751 return $this->linkItem($this->label('view'), IconUtility::getSpriteIcon('actions-document-view'), BackendUtility::viewOnClick($id, '', NULL, $anchor) . ';');
752 }
753
754 /**
755 * Adding element for setting temporary mount point.
756 *
757 * @param int $page_id Page uid (PID)
758 * @return array Item array, element in $menuItems
759 * @internal
760 */
761 public function DB_tempMountPoint($page_id) {
762 return $this->linkItem($this->label('tempMountPoint'), IconUtility::getSpriteIcon('apps-pagetree-page-mountpoint'), 'if (top.content.nav_frame) {
763 var node = top.TYPO3.Backend.NavigationContainer.PageTree.getSelected();
764 if (node === null) {
765 return false;
766 }
767
768 var useNode = {
769 attributes: {
770 nodeData: {
771 id: ' . (int)$page_id . '
772 }
773 }
774 };
775
776 node.ownerTree.commandProvider.mountAsTreeRoot(useNode, node.ownerTree);
777 }
778 ');
779 }
780
781 /**
782 * Adding CM element for hide/unhide of the input record
783 *
784 * @param string $table Table name
785 * @param array $rec Record array
786 * @param string $hideField Name of the hide field
787 * @return array Item array, element in $menuItems
788 * @internal
789 */
790 public function DB_hideUnhide($table, $rec, $hideField) {
791 return $this->DB_changeFlag($table, $rec, $hideField, $this->label(($rec[$hideField] ? 'un' : '') . 'hide'), 'hide');
792 }
793
794 /**
795 * Adding CM element for a flag field of the input record
796 *
797 * @param string $table Table name
798 * @param array $rec Record array
799 * @param string $flagField Name of the flag field
800 * @param string $title Menu item Title
801 * @return array Item array, element in $menuItems
802 */
803 public function DB_changeFlag($table, $rec, $flagField, $title) {
804 $uid = $rec['_ORIG_uid'] ?: $rec['uid'];
805 $loc = 'top.content.list_frame';
806 $editOnClick = 'if(' . $loc . '){' . $loc . '.location.href=top.TS.PATH_typo3+' .
807 GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&redirect=') . '+top.rawurlencode(' .
808 $this->frameLocation($loc . '.document') . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+\'' .
809 GeneralUtility::quoteJSvalue(
810 '&data[' . $table . '][' . $uid . '][' . $flagField . ']=' . ($rec[$flagField] ? 0 : 1) . '&prErr=1&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction')
811 ) . ';};';
812 if ($table === 'pages') {
813 $editOnClick .= 'top.nav.refresh.defer(500, top.nav);';
814 }
815 return $this->linkItem($title, IconUtility::getSpriteIcon('actions-edit-' . ($rec[$flagField] ? 'un' : '') . 'hide'), $editOnClick . 'return false;', 1);
816 }
817
818 /***************************************
819 *
820 * FILE
821 *
822 ***************************************/
823 /**
824 * Make 1st level clickmenu:
825 *
826 * @param string $combinedIdentifier The combined identifier
827 * @return string HTML content
828 * @see \TYPO3\CMS\Core\Resource\ResourceFactory::retrieveFileOrFolderObject()
829 */
830 public function printFileClickMenu($combinedIdentifier) {
831 $icon = '';
832 $identifier = '';
833 $menuItems = array();
834 $combinedIdentifier = rawurldecode($combinedIdentifier);
835 $fileObject = ResourceFactory::getInstance()
836 ->retrieveFileOrFolderObject($combinedIdentifier);
837 if ($fileObject) {
838 $folder = FALSE;
839 $isStorageRoot = FALSE;
840 $isOnline = TRUE;
841 $userMayViewStorage = FALSE;
842 $userMayEditStorage = FALSE;
843 $identifier = $fileObject->getCombinedIdentifier();
844 if ($fileObject instanceof Folder) {
845 $icon = IconUtility::getSpriteIconForResource($fileObject, array(
846 'class' => 'absmiddle',
847 'title' => htmlspecialchars($fileObject->getName())
848 ));
849 $folder = TRUE;
850 if ($fileObject->getIdentifier() === $fileObject->getStorage()->getRootLevelFolder()->getIdentifier()) {
851 $isStorageRoot = TRUE;
852 if ($this->backendUser->check('tables_select', 'sys_file_storage')) {
853 $userMayViewStorage = TRUE;
854 }
855 if ($this->backendUser->check('tables_modify', 'sys_file_storage')) {
856 $userMayEditStorage = TRUE;
857 }
858 }
859 if (!$fileObject->getStorage()->isOnline()) {
860 $isOnline = FALSE;
861 }
862 } else {
863 $icon = IconUtility::getSpriteIconForResource($fileObject, array(
864 'class' => 'absmiddle',
865 'title' => htmlspecialchars($fileObject->getName() . ' (' . GeneralUtility::formatSize($fileObject->getSize()) . ')')
866 ));
867 }
868 // Hide
869 if (!in_array('hide', $this->disabledItems) && $isStorageRoot && $userMayEditStorage) {
870 $record = BackendUtility::getRecord('sys_file_storage', $fileObject->getStorage()->getUid());
871 $menuItems['hide'] = $this->DB_changeFlag(
872 'sys_file_storage',
873 $record,
874 'is_online',
875 $this->label($record['is_online'] ? 'offline' : 'online'),
876 'hide'
877 );
878 }
879 // Edit
880 if (!in_array('edit', $this->disabledItems) && $fileObject->checkActionPermission('write')) {
881 if (!$folder && !$isStorageRoot && $fileObject->isIndexed() && $this->backendUser->check('tables_modify', 'sys_file_metadata')) {
882 $metaData = $fileObject->_getMetaData();
883 $menuItems['edit2'] = $this->DB_edit('sys_file_metadata', $metaData['uid']);
884 }
885 if (!$folder && GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'], $fileObject->getExtension()) && $fileObject->checkActionPermission('write')) {
886 $menuItems['edit'] = $this->FILE_launch($identifier, 'file_edit', 'editcontent', 'actions-page-open');
887 } elseif ($isStorageRoot && $userMayEditStorage) {
888 $menuItems['edit'] = $this->DB_edit('sys_file_storage', $fileObject->getStorage()->getUid());
889 }
890 }
891 // Rename
892 if (!in_array('rename', $this->disabledItems) && !$isStorageRoot && $fileObject->checkActionPermission('rename')) {
893 $menuItems['rename'] = $this->FILE_launch($identifier, 'file_rename', 'rename', 'actions-edit-rename');
894 }
895 // Upload
896 if (!in_array('upload', $this->disabledItems) && $folder && $isOnline && $fileObject->checkActionPermission('write')) {
897 $menuItems['upload'] = $this->FILE_launch($identifier, 'file_upload', 'upload', 'actions-edit-upload');
898 }
899 // New
900 if (!in_array('new', $this->disabledItems) && $folder && $isOnline && $fileObject->checkActionPermission('write')) {
901 $menuItems['new'] = $this->FILE_launch($identifier, 'file_newfolder', 'new', 'actions-document-new');
902 }
903 // Info
904 if (!in_array('info', $this->disabledItems) && $fileObject->checkActionPermission('read')) {
905 if ($isStorageRoot && $userMayViewStorage) {
906 $menuItems['info'] = $this->DB_info('sys_file_storage', $fileObject->getStorage()->getUid());
907 } elseif (!$folder) {
908 $menuItems['info'] = $this->fileInfo($identifier);
909 }
910 }
911 $menuItems[] = 'spacer';
912 // Copy:
913 if (!in_array('copy', $this->disabledItems) && !$isStorageRoot && $fileObject->checkActionPermission('read')) {
914 $menuItems['copy'] = $this->FILE_copycut($identifier, 'copy');
915 }
916 // Cut:
917 if (!in_array('cut', $this->disabledItems) && !$isStorageRoot && $fileObject->checkActionPermission('move')) {
918 $menuItems['cut'] = $this->FILE_copycut($identifier, 'cut');
919 }
920 // Paste:
921 $elFromAllTables = count($this->clipObj->elFromTable('_FILE'));
922 if (!in_array('paste', $this->disabledItems) && $elFromAllTables && $folder && $fileObject->checkActionPermission('write')) {
923 $elArr = $this->clipObj->elFromTable('_FILE');
924 $selItem = reset($elArr);
925 $elInfo = array(
926 basename($selItem),
927 basename($identifier),
928 $this->clipObj->currentMode()
929 );
930 $clickedFileOrFolder = ResourceFactory::getInstance()->retrieveFileOrFolderObject($combinedIdentifier);
931 $fileOrFolderInClipBoard = ResourceFactory::getInstance()->retrieveFileOrFolderObject($selItem);
932 if (!$fileOrFolderInClipBoard instanceof Folder || !$fileOrFolderInClipBoard->getStorage()->isWithinFolder($fileOrFolderInClipBoard, $clickedFileOrFolder)) {
933 $menuItems['pasteinto'] = $this->FILE_paste($identifier, $selItem, $elInfo);
934 }
935 }
936 $menuItems[] = 'spacer';
937 // Delete:
938 if (!in_array('delete', $this->disabledItems) && $fileObject->checkActionPermission('delete')) {
939 if ($isStorageRoot && $userMayEditStorage) {
940 $elInfo = array(GeneralUtility::fixed_lgd_cs($fileObject->getStorage()->getName(), $this->backendUser->uc['titleLen']));
941 $menuItems['delete'] = $this->DB_delete('sys_file_storage', $fileObject->getStorage()->getUid(), $elInfo);
942 } elseif (!$isStorageRoot) {
943 $menuItems['delete'] = $this->FILE_delete($identifier);
944 }
945 }
946 }
947 // Adding external elements to the menuItems array
948 $menuItems = $this->processingByExtClassArray($menuItems, $identifier, 0);
949 // Processing by external functions?
950 $menuItems = $this->externalProcessingOfFileMenuItems($menuItems);
951 // Return the printed elements:
952 return $this->printItems($menuItems, $icon . $fileObject->getName());
953 }
954
955 /**
956 * Processing the $menuItems array (for extension classes) (FILES)
957 *
958 * @param array $menuItems Array for manipulation.
959 * @return array Processed $menuItems array
960 */
961 public function externalProcessingOfFileMenuItems($menuItems) {
962 return $menuItems;
963 }
964
965 /**
966 * Multi-function for adding an entry to the $menuItems array
967 *
968 * @param string $path Path to the file/directory (target)
969 * @param string $moduleName Script (deprecated) or module name (e.g. file_edit) to pass &target= to
970 * @param string $type "type" is the code which fetches the correct label for the element from "cm.
971 * @param string $iconName
972 * @param bool $noReturnUrl If set, the return URL parameter will not be set in the link
973 * @return array Item array, element in $menuItems
974 * @internal
975 */
976 public function FILE_launch($path, $moduleName, $type, $iconName, $noReturnUrl = FALSE) {
977 $loc = 'top.content.list_frame';
978
979 if (strpos($moduleName, '.php') !== FALSE) {
980 GeneralUtility::deprecationLog(
981 'Using a php file directly in ClickMenu is deprecated since TYPO3 CMS 7, and will be removed in CMS 8.'
982 . ' Register the class as module and use BackendUtility::getModuleUrl() to get the right link.'
983 . ' For examples how to do this see ext_tables.php of EXT:backend.'
984 );
985 $scriptUrl = $moduleName;
986 } else {
987 $scriptUrl = BackendUtility::getModuleUrl($moduleName);
988 }
989
990 $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)') . ';}';
991 return $this->linkItem($this->label($type), IconUtility::getSpriteIcon($iconName), $editOnClick . 'top.nav.refresh();');
992 }
993
994 /**
995 * Returns element for copy or cut of files.
996 *
997 * @param string $path Path to the file/directory (target)
998 * @param string $type Type: "copy" or "cut
999 * @return array Item array, element in $menuItems
1000 * @internal
1001 */
1002 public function FILE_copycut($path, $type) {
1003 $isSel = '';
1004 // Pseudo table name for use in the clipboard.
1005 $table = '_FILE';
1006 $uid = GeneralUtility::shortmd5($path);
1007 if ($this->clipObj->current === 'normal') {
1008 $isSel = $this->clipObj->isSelected($table, $uid);
1009 }
1010 $addParam = array();
1011 if ($this->listFrame) {
1012 $addParam['reloadListFrame'] = $this->alwaysContentFrame ? 2 : 1;
1013 }
1014 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;');
1015 }
1016
1017 /**
1018 * Creates element for deleting of target
1019 *
1020 * @param string $path Path to the file/directory (target)
1021 * @return array Item array, element in $menuItems
1022 * @internal
1023 */
1024 public function FILE_delete($path) {
1025 $loc = 'top.content.list_frame';
1026 if ($this->backendUser->jsConfirmation(4)) {
1027 $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!)'))) . ')';
1028 } else {
1029 $conf = '1==1';
1030 }
1031 $editOnClick = 'if(' . $loc . ' && ' . $conf . ' ){' . $loc . '.location.href=top.TS.PATH_typo3+' .
1032 GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_file') . '&redirect=') . '+top.rawurlencode(' .
1033 $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+' .
1034 GeneralUtility::quoteJSvalue(
1035 '&file[delete][0][data]=' . rawurlencode($path) . '&vC=' . $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction')
1036 ) . ';};';
1037 return $this->linkItem($this->label('delete'), IconUtility::getSpriteIcon('actions-edit-delete'), $editOnClick . 'return false;');
1038 }
1039
1040 /**
1041 * Creates element for pasting files.
1042 *
1043 * @param string $path Path to the file/directory (target)
1044 * @param string $target target - NOT USED.
1045 * @param array $elInfo Various values for the labels.
1046 * @return array Item array, element in $menuItems
1047 * @internal
1048 */
1049 public function FILE_paste($path, $target, $elInfo) {
1050 $loc = 'top.content.list_frame';
1051 if ($this->backendUser->jsConfirmation(2)) {
1052 $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])) . ')';
1053 } else {
1054 $conf = $loc;
1055 }
1056 $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();';
1057 return $this->linkItem($this->label('pasteinto'), IconUtility::getSpriteIcon('actions-document-paste-into'), $editOnClick . 'return false;');
1058 }
1059
1060 /**
1061 * Adding ClickMenu element for file info
1062 *
1063 * @param string $identifier The combined identifier of the file.
1064 * @return array Item array, element in $menuItems
1065 */
1066 protected function fileInfo($identifier) {
1067 return $this->DB_info('_FILE', $identifier);
1068 }
1069
1070 /***************************************
1071 *
1072 * DRAG AND DROP
1073 *
1074 ***************************************/
1075 /**
1076 * Make 1st level clickmenu:
1077 *
1078 * @param string $table The absolute path
1079 * @param int $srcId UID for the current record.
1080 * @param int $dstId Destination ID
1081 * @return string HTML content
1082 */
1083 public function printDragDropClickMenu($table, $srcId, $dstId) {
1084 $menuItems = array();
1085 // If the drag and drop menu should apply to PAGES use this set of menu items
1086 if ($table === 'pages') {
1087 // Move Into:
1088 $menuItems['movePage_into'] = $this->dragDrop_copymovepage($srcId, $dstId, 'move', 'into');
1089 // Move After:
1090 $menuItems['movePage_after'] = $this->dragDrop_copymovepage($srcId, $dstId, 'move', 'after');
1091 // Copy Into:
1092 $menuItems['copyPage_into'] = $this->dragDrop_copymovepage($srcId, $dstId, 'copy', 'into');
1093 // Copy After:
1094 $menuItems['copyPage_after'] = $this->dragDrop_copymovepage($srcId, $dstId, 'copy', 'after');
1095 }
1096 // If the drag and drop menu should apply to FOLDERS use this set of menu items
1097 if ($table === 'folders') {
1098 // Move Into:
1099 $menuItems['moveFolder_into'] = $this->dragDrop_copymovefolder($srcId, $dstId, 'move');
1100 // Copy Into:
1101 $menuItems['copyFolder_into'] = $this->dragDrop_copymovefolder($srcId, $dstId, 'copy');
1102 }
1103 // Adding external elements to the menuItems array
1104 $menuItems = $this->processingByExtClassArray($menuItems, 'dragDrop_' . $table, $srcId);
1105 // to extend this, you need to apply a Context Menu to a "virtual" table called "dragDrop_pages" or similar
1106 // Processing by external functions?
1107 $menuItems = $this->externalProcessingOfDBMenuItems($menuItems);
1108 // Return the printed elements:
1109 return $this->printItems($menuItems, IconUtility::getSpriteIconForRecord($table, $this->rec, array('title' => BackendUtility::getRecordTitle($table, $this->rec, TRUE))));
1110 }
1111
1112 /**
1113 * Processing the $menuItems array (for extension classes) (DRAG'N DROP)
1114 *
1115 * @param array $menuItems Array for manipulation.
1116 * @return array Processed $menuItems array
1117 */
1118 public function externalProcessingOfDragDropMenuItems($menuItems) {
1119 return $menuItems;
1120 }
1121
1122 /**
1123 * Adding CM element for Copying/Moving a Page Into/After from a drag & drop action
1124 *
1125 * @param int $srcUid source UID code for the record to modify
1126 * @param int $dstUid destination UID code for the record to modify
1127 * @param string $action Action code: either "move" or "copy
1128 * @param string $into Parameter code: either "into" or "after
1129 * @return array Item array, element in $menuItems
1130 * @internal
1131 */
1132 public function dragDrop_copymovepage($srcUid, $dstUid, $action, $into) {
1133 $negativeSign = $into === 'into' ? '' : '-';
1134 $loc = 'top.content.list_frame';
1135 $editOnClick = 'if(' . $loc . '){' . $loc . '.document.location=top.TS.PATH_typo3+' .
1136 GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&redirect=') . '+top.rawurlencode(' .
1137 $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+' .
1138 GeneralUtility::quoteJSvalue(
1139 '&cmd[pages][' . $srcUid . '][' . $action . ']=' . $negativeSign . $dstUid . '&prErr=1&vC=' .
1140 $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction')
1141 ) . ';};top.nav.refresh();';
1142 return $this->linkItem($this->label($action . 'Page_' . $into), IconUtility::getSpriteIcon('actions-document-paste-' . $into), $editOnClick . 'return false;', 0);
1143 }
1144
1145 /**
1146 * Adding CM element for Copying/Moving a Folder Into from a drag & drop action
1147 *
1148 * @param string $srcPath source path for the record to modify
1149 * @param string $dstPath destination path for the records to modify
1150 * @param string $action Action code: either "move" or "copy
1151 * @return array Item array, element in $menuItems
1152 * @internal
1153 */
1154 public function dragDrop_copymovefolder($srcPath, $dstPath, $action) {
1155 $loc = 'top.content.list_frame';
1156 $editOnClick = 'if(' . $loc . '){' . $loc . '.document.location=top.TS.PATH_typo3+' .
1157 GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_file') . '&redirect=') . '+top.rawurlencode(' .
1158 $this->frameLocation(($loc . '.document')) . '.pathname+' . $this->frameLocation(($loc . '.document')) . '.search)+' .
1159 GeneralUtility::quoteJSvalue(
1160 '&file[' . $action . '][0][data]=' . $srcPath . '&file[' . $action . '][0][target]=' . $dstPath . '&prErr=1&vC=' .
1161 $this->backendUser->veriCode() . BackendUtility::getUrlToken('tceAction')
1162 ) . ';};top.nav.refresh();';
1163 return $this->linkItem($this->label($action . 'Folder_into'), IconUtility::getSpriteIcon('apps-pagetree-drag-move-into'), $editOnClick . 'return false;', 0);
1164 }
1165
1166 /***************************************
1167 *
1168 * COMMON
1169 *
1170 **************************************/
1171 /**
1172 * Prints the items from input $menuItems array - as JS section for writing to the div-layers.
1173 *
1174 * @param array $menuItems Array
1175 * @return string HTML code
1176 */
1177 public function printItems($menuItems) {
1178 // Enable/Disable items
1179 $menuItems = $this->enableDisableItems($menuItems);
1180 // Clean up spacers
1181 $menuItems = $this->cleanUpSpacers($menuItems);
1182 // Adding JS part and return the content
1183 return $this->printLayerJScode($menuItems);
1184 }
1185
1186 /**
1187 * Create the JavaScript section
1188 *
1189 * @param array $menuItems The $menuItems array to print
1190 * @return string The JavaScript section which will print the content of the CM to the div-layer in the target frame.
1191 */
1192 public function printLayerJScode($menuItems) {
1193 // Clipboard must not be submitted - then it's probably a copy/cut situation.
1194 if ($this->isCMlayers()) {
1195 // Create the table displayed in the clickmenu layer:
1196 // Wrap the inner table in another table to create outer border:
1197 $CMtable = '
1198 <div class="typo3-CSM-wrapperCM">
1199 <table border="0" cellpadding="0" cellspacing="0" class="typo3-CSM">
1200 ' . implode('', $this->menuItemsForClickMenu($menuItems)) . '
1201 </table></div>';
1202 return '<data><clickmenu><htmltable><![CDATA[' . $CMtable . ']]></htmltable><cmlevel>' . $this->cmLevel . '</cmlevel></clickmenu></data>';
1203 }
1204 }
1205
1206 /**
1207 * Wrapping the input string in a table with background color 4 and a black border style.
1208 * For the pop-up menu
1209 *
1210 * @param string $str HTML content to wrap in table.
1211 * @return string
1212 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1213 */
1214 public function wrapColorTableCM($str) {
1215 GeneralUtility::logDeprecatedFunction();
1216 return '<div class="typo3-CSM-wrapperCM">
1217 ' . $str . '
1218 </div>';
1219 }
1220
1221 /**
1222 * Traverses the menuItems and generates an output array for implosion in the CM div-layers table.
1223 *
1224 * @param array $menuItems Array
1225 * @return array array for implosion in the CM div-layers table.
1226 */
1227 public function menuItemsForClickMenu($menuItems) {
1228 $out = array();
1229 foreach ($menuItems as $cc => $i) {
1230 // MAKE horizontal spacer
1231 if (is_string($i) && $i === 'spacer') {
1232 $out[] = '
1233 <tr style="height: 1px;" class="bgColor2">
1234 <td colspan="2"></td>
1235 </tr>';
1236 } else {
1237 // Just make normal element:
1238 $onClick = $i[3];
1239 $onClick = preg_replace('/return[[:space:]]+hideCM\\(\\)[[:space:]]*;/i', '', $onClick);
1240 $onClick = preg_replace('/return[[:space:]]+false[[:space:]]*;/i', '', $onClick);
1241 $onClick = preg_replace('/hideCM\\(\\);/i', '', $onClick);
1242 if (!$i[5]) {
1243 $onClick .= 'TYPO3.ClickMenu.hideAll();';
1244 }
1245 $CSM = ' oncontextmenu="this.click();return false;"';
1246 $out[] = '
1247 <tr class="typo3-CSM-itemRow" onclick="' . htmlspecialchars($onClick) . '"' . $CSM . '>
1248 ' . (!$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>') . '
1249 </tr>';
1250 }
1251 }
1252 return $out;
1253 }
1254
1255 /**
1256 * Adds or inserts a menu item
1257 * 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.
1258 *
1259 * @param array $menuItems Menu items array
1260 * @param array $newMenuItems Menu items array to insert
1261 * @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.
1262 * @return array Menu items array, processed.
1263 */
1264 public function addMenuItems($menuItems, $newMenuItems, $position = '') {
1265 if (is_array($newMenuItems)) {
1266 if ($position) {
1267 $posArr = GeneralUtility::trimExplode(',', $position, TRUE);
1268 foreach ($posArr as $pos) {
1269 list($place, $menuEntry) = GeneralUtility::trimExplode(':', $pos, TRUE);
1270 list($place, $placeExtra) = GeneralUtility::trimExplode('-', $place, TRUE);
1271 // Bottom
1272 $pointer = count($menuItems);
1273 $found = FALSE;
1274 if ($place) {
1275 switch (strtolower($place)) {
1276 case 'after':
1277 case 'before':
1278 if ($menuEntry) {
1279 $p = 1;
1280 reset($menuItems);
1281 while (TRUE) {
1282 if ((string)key($menuItems) === $menuEntry) {
1283 $pointer = $p;
1284 $found = TRUE;
1285 break;
1286 }
1287 if (!next($menuItems)) {
1288 break;
1289 }
1290 $p++;
1291 }
1292 if (!$found) {
1293 break;
1294 }
1295 if ($place === 'before') {
1296 $pointer--;
1297 if ($placeExtra === 'spacer' and prev($menuItems) === 'spacer') {
1298 $pointer--;
1299 }
1300 } elseif ($place === 'after') {
1301 if ($placeExtra === 'spacer' and next($menuItems) === 'spacer') {
1302 $pointer++;
1303 }
1304 }
1305 }
1306 break;
1307 default:
1308 if (strtolower($place) === 'top') {
1309 $pointer = 0;
1310 } else {
1311 $pointer = count($menuItems);
1312 }
1313 $found = TRUE;
1314 }
1315 }
1316 if ($found) {
1317 break;
1318 }
1319 }
1320 }
1321 $pointer = max(0, $pointer);
1322 $menuItemsBefore = array_slice($menuItems, 0, $pointer ?: 0);
1323 $menuItemsAfter = array_slice($menuItems, $pointer);
1324 $menuItems = $menuItemsBefore + $newMenuItems + $menuItemsAfter;
1325 }
1326 return $menuItems;
1327 }
1328
1329 /**
1330 * Creating an array with various elements for the clickmenu entry
1331 *
1332 * @param string $str The label, htmlspecialchar'ed already
1333 * @param string $icon <img>-tag for the icon
1334 * @param string $onClick JavaScript onclick event for label/icon
1335 * @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)
1336 * @param bool $dontHide If set, the clickmenu layer will not hide itself onclick - used for secondary menus to appear...
1337 * @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!
1338 */
1339 public function linkItem($str, $icon, $onClick, $onlyCM = 0, $dontHide = 0) {
1340 $onClick = str_replace('top.loadTopMenu', 'showClickmenu_raw', $onClick);
1341 return array(
1342 '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' . $str . $icon . '</a>',
1343 $str,
1344 $icon,
1345 $onClick,
1346 $onlyCM,
1347 $dontHide
1348 );
1349 }
1350
1351 /**
1352 * Returns the input string IF not a user setting has disabled display of icons.
1353 *
1354 * @param string $iconCode The icon-image tag
1355 * @return string The icon-image tag prefixed with space char IF the icon should be printed at all due to user settings
1356 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1357 */
1358 public function excludeIcon($iconCode) {
1359 GeneralUtility::logDeprecatedFunction();
1360 return $this->backendUser->uc['noMenuMode'] && $this->backendUser->uc['noMenuMode'] !== 'icons' ? '' : ' ' . $iconCode;
1361 }
1362
1363 /**
1364 * Enabling / Disabling items based on list provided from GET var ($this->iParts[3])
1365 *
1366 * @param array $menuItems Menu items array
1367 * @return array Menu items array, processed.
1368 */
1369 public function enableDisableItems($menuItems) {
1370 if ($this->iParts[3]) {
1371 // Detect "only" mode: (only showing listed items)
1372 if ($this->iParts[3][0] === '+') {
1373 $this->iParts[3] = substr($this->iParts[3], 1);
1374 $only = TRUE;
1375 } else {
1376 $only = FALSE;
1377 }
1378 // Do filtering:
1379 // Transfer ONLY elements which are mentioned (or are spacers)
1380 if ($only) {
1381 $newMenuArray = array();
1382 foreach ($menuItems as $key => $value) {
1383 if (GeneralUtility::inList($this->iParts[3], $key) || is_string($value) && $value === 'spacer') {
1384 $newMenuArray[$key] = $value;
1385 }
1386 }
1387 $menuItems = $newMenuArray;
1388 } else {
1389 // Traverse all elements except those listed (just unsetting them):
1390 $elements = GeneralUtility::trimExplode(',', $this->iParts[3], TRUE);
1391 foreach ($elements as $value) {
1392 unset($menuItems[$value]);
1393 }
1394 }
1395 }
1396 // Return processed menu items:
1397 return $menuItems;
1398 }
1399
1400 /**
1401 * Clean up spacers; Will remove any spacers in the start/end of menu items array plus any duplicates.
1402 *
1403 * @param array $menuItems Menu items array
1404 * @return array Menu items array, processed.
1405 */
1406 public function cleanUpSpacers($menuItems) {
1407 // Remove doubles:
1408 $prevItemWasSpacer = FALSE;
1409 foreach ($menuItems as $key => $value) {
1410 if (is_string($value) && $value === 'spacer') {
1411 if ($prevItemWasSpacer) {
1412 unset($menuItems[$key]);
1413 }
1414 $prevItemWasSpacer = TRUE;
1415 } else {
1416 $prevItemWasSpacer = FALSE;
1417 }
1418 }
1419 // Remove first:
1420 reset($menuItems);
1421 $key = key($menuItems);
1422 $value = current($menuItems);
1423 if (is_string($value) && $value === 'spacer') {
1424 unset($menuItems[$key]);
1425 }
1426 // Remove last:
1427 end($menuItems);
1428 $key = key($menuItems);
1429 $value = current($menuItems);
1430 if (is_string($value) && $value === 'spacer') {
1431 unset($menuItems[$key]);
1432 }
1433 // Return processed menu items:
1434 return $menuItems;
1435 }
1436
1437 /**
1438 * Get label from locallang_core.xlf:cm.*
1439 *
1440 * @param string $label The "cm."-suffix to get.
1441 * @return string
1442 */
1443 public function label($label) {
1444 return $this->languageService->makeEntities($this->languageService->sL('LLL:EXT:lang/locallang_core.xlf:cm.' . $label, TRUE));
1445 }
1446
1447 /**
1448 * Returns TRUE if there should be writing to the div-layers (commands sent to clipboard MUST NOT write to div-layers)
1449 *
1450 * @return bool
1451 */
1452 public function isCMlayers() {
1453 return !$this->CB;
1454 }
1455
1456 /**
1457 * Appends ".location" to input string
1458 *
1459 * @param string $str Input string, probably a JavaScript document reference
1460 * @return string
1461 */
1462 public function frameLocation($str) {
1463 return $str . '.location';
1464 }
1465
1466 }