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