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