[BUGFIX] Correct label for cut release in clickmenu
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / ContextMenu / ItemProviders / PageProvider.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Backend\ContextMenu\ItemProviders;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Type\Bitmask\Permission;
19
20 /**
21 * Context menu item provider for pages table
22 */
23 class PageProvider extends RecordProvider
24 {
25 /**
26 * @var string
27 */
28 protected $table = 'pages';
29
30 /**
31 * @var array
32 */
33 protected $itemsConfiguration = [
34 'view' => [
35 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.view',
36 'iconIdentifier' => 'actions-document-view',
37 'callbackAction' => 'viewRecord'
38 ],
39 'edit' => [
40 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.edit',
41 'iconIdentifier' => 'actions-page-open',
42 'callbackAction' => 'editRecord'
43 ],
44 'new' => [
45 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.new',
46 'iconIdentifier' => 'actions-document-new',
47 'callbackAction' => 'newRecord'
48 ],
49 'info' => [
50 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.info',
51 'iconIdentifier' => 'actions-document-info',
52 'callbackAction' => 'openInfoPopUp'
53 ],
54 'divider1' => [
55 'type' => 'divider'
56 ],
57 'copy' => [
58 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.copy',
59 'iconIdentifier' => 'actions-edit-copy',
60 'callbackAction' => 'copy'
61 ],
62 'copyRelease' => [
63 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.copy',
64 'iconIdentifier' => 'actions-edit-copy-release',
65 'callbackAction' => 'clipboardRelease'
66 ],
67 'cut' => [
68 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.cut',
69 'iconIdentifier' => 'actions-edit-cut',
70 'callbackAction' => 'cut'
71 ],
72 'cutRelease' => [
73 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.cutrelease',
74 'iconIdentifier' => 'actions-edit-cut-release',
75 'callbackAction' => 'clipboardRelease'
76 ],
77 'pasteAfter' => [
78 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.pasteafter',
79 'iconIdentifier' => 'actions-document-paste-after',
80 'callbackAction' => 'pasteAfter'
81 ],
82 'pasteInto' => [
83 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.pasteinto',
84 'iconIdentifier' => 'actions-document-paste-into',
85 'callbackAction' => 'pasteInto'
86 ],
87 'divider2' => [
88 'type' => 'divider'
89 ],
90 'more' => [
91 'type' => 'submenu',
92 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.more',
93 'iconIdentifier' => '',
94 'callbackAction' => 'openSubmenu',
95 'childItems' => [
96 'newWizard' => [
97 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_misc.xlf:CM_newWizard',
98 'iconIdentifier' => 'actions-page-new',
99 'callbackAction' => 'newPageWizard',
100 ],
101 'openListModule' => [
102 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_misc.xlf:CM_db_list',
103 'iconIdentifier' => 'actions-system-list-open',
104 'callbackAction' => 'openListModule',
105 ],
106 'mountAsTreeRoot' => [
107 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.tempMountPoint',
108 'iconIdentifier' => 'actions-pagetree-mountroot',
109 'callbackAction' => 'mountAsTreeRoot',
110 ],
111 ],
112 ],
113 'divider3' => [
114 'type' => 'divider'
115 ],
116 'enable' => [
117 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:enable',
118 'iconIdentifier' => 'actions-edit-unhide',
119 'callbackAction' => 'enableRecord',
120 ],
121 'disable' => [
122 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:disable',
123 'iconIdentifier' => 'actions-edit-hide',
124 'callbackAction' => 'disableRecord',
125 ],
126 'delete' => [
127 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:cm.delete',
128 'iconIdentifier' => 'actions-edit-delete',
129 'callbackAction' => 'deleteRecord',
130 ],
131 'history' => [
132 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_misc.xlf:CM_history',
133 'iconIdentifier' => 'actions-document-history-open',
134 'callbackAction' => 'openHistoryPopUp',
135 ],
136 'clearCache' => [
137 'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clear_cache',
138 'iconIdentifier' => 'actions-system-cache-clear',
139 'callbackAction' => 'clearCache',
140 ],
141 ];
142
143 /**
144 * Checks if the provider can add items to the menu
145 *
146 * @return bool
147 */
148 public function canHandle(): bool
149 {
150 return $this->table === 'pages';
151 }
152
153 /**
154 * @return int
155 */
156 public function getPriority(): int
157 {
158 return 100;
159 }
160
161 /**
162 * @param string $itemName
163 * @param string $type
164 * @return bool
165 */
166 protected function canRender(string $itemName, string $type): bool
167 {
168 if (in_array($type, ['divider', 'submenu'], true)) {
169 return true;
170 }
171 if (in_array($itemName, $this->disabledItems, true)) {
172 return false;
173 }
174 $canRender = false;
175 switch ($itemName) {
176 case 'view':
177 $canRender = $this->canBeViewed();
178 break;
179 case 'edit':
180 $canRender = $this->canBeEdited();
181 break;
182 case 'new':
183 case 'newWizard':
184 $canRender = $this->canBeCreated();
185 break;
186 case 'info':
187 $canRender = $this->canShowInfo();
188 break;
189 case 'enable':
190 $canRender = $this->canBeEnabled();
191 break;
192 case 'disable':
193 $canRender = $this->canBeDisabled();
194 break;
195 case 'delete':
196 $canRender = $this->canBeDeleted();
197 break;
198 case 'history':
199 $canRender = $this->canShowHistory();
200 break;
201 case 'openListModule':
202 $canRender = $this->canOpenListModule();
203 break;
204 case 'mountAsTreeRoot':
205 $canRender = !$this->isRoot();
206 break;
207 case 'copy':
208 $canRender = $this->canBeCopied();
209 break;
210 case 'copyRelease':
211 $canRender = $this->isRecordInClipboard('copy');
212 break;
213 case 'cut':
214 $canRender = $this->canBeCut();
215 break;
216 case 'cutRelease':
217 $canRender = $this->isRecordInClipboard('cut');
218 break;
219 case 'pasteAfter':
220 $canRender = $this->canBePastedAfter();
221 break;
222 case 'pasteInto':
223 $canRender = $this->canBePastedInto();
224 break;
225 case 'clearCache':
226 $canRender = $this->canClearCache();
227 break;
228 }
229 return $canRender;
230 }
231
232 /**
233 * Saves calculated permissions for a page to speed things up
234 */
235 protected function initPermissions()
236 {
237 $this->pagePermissions = $this->backendUser->calcPerms($this->record);
238 }
239
240 /**
241 * Checks if the user may create pages below the given page
242 *
243 * @return bool
244 */
245 protected function canBeCreated(): bool
246 {
247 return $this->hasPagePermission(Permission::PAGE_NEW);
248 }
249
250 /**
251 * Checks if the user has editing rights
252 *
253 * @return bool
254 */
255 protected function canBeEdited(): bool
256 {
257 if ($this->isRoot()) {
258 return false;
259 }
260 if (isset($GLOBALS['TCA'][$this->table]['ctrl']['readOnly']) && $GLOBALS['TCA'][$this->table]['ctrl']['readOnly']) {
261 return false;
262 }
263 if ($this->backendUser->isAdmin()) {
264 return true;
265 }
266 if (isset($GLOBALS['TCA'][$this->table]['ctrl']['adminOnly']) && $GLOBALS['TCA'][$this->table]['ctrl']['adminOnly']) {
267 return false;
268 }
269 return !$this->isRecordLocked() && $this->hasPagePermission(Permission::PAGE_EDIT);
270 }
271
272 /**
273 * Check if a page is locked
274 *
275 * @return bool
276 */
277 protected function isRecordLocked(): bool
278 {
279 return (bool)$this->record['editlock'];
280 }
281
282 /**
283 * Checks if the page is allowed to can be cut
284 *
285 * @return bool
286 */
287 protected function canBeCut(): bool
288 {
289 return !$this->isWebMount()
290 && $this->canBeEdited()
291 && !$this->isDeletePlaceholder();
292 }
293
294 /**
295 * Checks if the page is allowed to be copied
296 *
297 * @return bool
298 */
299 protected function canBeCopied(): bool
300 {
301 return !$this->isRoot()
302 && !$this->isWebMount()
303 && !$this->isRecordInClipboard('copy')
304 && $this->hasPagePermission(Permission::PAGE_SHOW)
305 && !$this->isDeletePlaceholder();
306 }
307
308 /**
309 * Checks if something can be pasted into the node
310 *
311 * @return bool
312 */
313 protected function canBePastedInto(): bool
314 {
315 $clipboardElementCount = count($this->clipboard->elFromTable($this->table));
316
317 return $clipboardElementCount
318 && $this->canBeCreated()
319 && !$this->isDeletePlaceholder();
320 }
321
322 /**
323 * Checks if something can be pasted after the node
324 *
325 * @return bool
326 */
327 protected function canBePastedAfter(): bool
328 {
329 $clipboardElementCount = count($this->clipboard->elFromTable($this->table));
330 return $clipboardElementCount
331 && $this->canBeCreated()
332 && !$this->isDeletePlaceholder();
333 }
334
335 /**
336 * Checks if the page is allowed to be removed
337 *
338 * @return bool
339 */
340 protected function canBeRemoved(): bool
341 {
342 return !$this->isDeletePlaceholder()
343 && !$this->isRecordLocked()
344 && $this->hasPagePermission(Permission::PAGE_DELETE);
345 }
346
347 /**
348 * Checks if the page is allowed to be viewed in frontend
349 *
350 * @return bool
351 */
352 protected function canBeViewed(): bool
353 {
354 return !$this->isRoot() && !$this->isDeleted();
355 }
356
357 /**
358 * Checks if the page is allowed to show info
359 *
360 * @return bool
361 */
362 protected function canShowInfo(): bool
363 {
364 return !$this->isRoot();
365 }
366
367 /**
368 * Checks if the user has clear cache rights
369 *
370 * @return bool
371 */
372 protected function canClearCache(): bool
373 {
374 return !$this->isRoot()
375 && ($this->backendUser->isAdmin() || $this->backendUser->getTSConfigVal('options.clearCache.pages'));
376 }
377
378 /**
379 * Determines whether this node is deleted.
380 *
381 * @return bool
382 */
383 protected function isDeleted(): bool
384 {
385 return !empty($this->record['deleted']) || $this->isDeletePlaceholder();
386 }
387
388 /**
389 * Returns true if current record is a root page
390 *
391 * @return bool
392 */
393 protected function isRoot()
394 {
395 return (int)$this->identifier === 0;
396 }
397
398 /**
399 * Returns true if current record is a web mount
400 *
401 * @return bool
402 */
403 protected function isWebMount()
404 {
405 return in_array($this->identifier, $this->backendUser->returnWebmounts());
406 }
407
408 /**
409 * @param string $itemName
410 * @return array
411 */
412 protected function getAdditionalAttributes(string $itemName): array
413 {
414 $attributes = [];
415 if ($itemName === 'view') {
416 $attributes += $this->getViewAdditionalAttributes();
417 }
418 if ($itemName === 'delete') {
419 $attributes += $this->getDeleteAdditionalAttributes();
420 }
421 if ($itemName === 'pasteInto') {
422 $attributes += $this->getPasteAdditionalAttributes('into');
423 }
424 if ($itemName === 'pasteAfter') {
425 $attributes += $this->getPasteAdditionalAttributes('after');
426 }
427 return $attributes;
428 }
429
430 /**
431 * @return int
432 */
433 protected function getPreviewPid(): int
434 {
435 return (int)$this->record['uid'];
436 }
437 }