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