8fcb109f80b48227d9ad3b3119bdbaafcf3e3026
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Page / NewMultiplePagesController.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Backend\Controller\Page;
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 Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Backend\Template\ModuleTemplate;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23 use TYPO3\CMS\Core\Database\ConnectionPool;
24 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
25 use TYPO3\CMS\Core\DataHandling\DataHandler;
26 use TYPO3\CMS\Core\Imaging\Icon;
27 use TYPO3\CMS\Core\Imaging\IconFactory;
28 use TYPO3\CMS\Core\Localization\LanguageService;
29 use TYPO3\CMS\Core\Type\Bitmask\Permission;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Core\Utility\MathUtility;
32 use TYPO3\CMS\Fluid\View\StandaloneView;
33
34 /**
35 * "Create multiple pages" controller
36 */
37 class NewMultiplePagesController
38 {
39 /**
40 * ModuleTemplate object
41 *
42 * @var ModuleTemplate
43 */
44 protected $moduleTemplate;
45
46 /**
47 * Constructor Method
48 *
49 * @var $moduleTemplate ModuleTemplate
50 */
51 public function __construct(ModuleTemplate $moduleTemplate = null)
52 {
53 $this->moduleTemplate = $moduleTemplate ?? GeneralUtility::makeInstance(ModuleTemplate::class);
54 }
55
56 /**
57 * Main function Handling input variables and rendering main view
58 *
59 * @param $request ServerRequestInterface
60 * @param $response ResponseInterface
61 * @return ResponseInterface Response
62 */
63 public function mainAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
64 {
65 $backendUser = $this->getBackendUser();
66 $pageUid = (int)$request->getQueryParams()['id'];
67
68 // Show only if there is a valid page and if this page may be viewed by the user
69 $pageRecord = BackendUtility::readPageAccess($pageUid, $backendUser->getPagePermsClause(Permission::PAGE_SHOW));
70 if (!is_array($pageRecord)) {
71 // User has no permission on parent page, should not happen, just render an empty page
72 $this->moduleTemplate->setContent('');
73 $response->getBody()->write($this->moduleTemplate->renderContent());
74 return $response;
75 }
76
77 // Doc header handling
78 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
79 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pageRecord);
80 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
81 $cshButton = $buttonBar->makeHelpButton()
82 ->setModuleName('pages_new')
83 ->setFieldName('pages_new');
84 $viewButton = $buttonBar->makeLinkButton()
85 ->setOnClick(BackendUtility::viewOnClick($pageUid, '', BackendUtility::BEgetRootLine($pageUid)))
86 ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
87 ->setIcon($iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
88 ->setHref('#');
89 $shortcutButton = $buttonBar->makeShortcutButton()
90 ->setModuleName('pages_new')
91 ->setGetVariables(['id']);
92 $buttonBar->addButton($cshButton)->addButton($viewButton)->addButton($shortcutButton);
93
94 // Main view setup
95 $view = GeneralUtility::makeInstance(StandaloneView::class);
96 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
97 'EXT:backend/Resources/Private/Templates/Page/NewPages.html'
98 ));
99
100 $calculatedPermissions = $backendUser->calcPerms($pageRecord);
101 $canCreateNew = $backendUser->isAdmin() || $calculatedPermissions & Permission::PAGE_NEW;
102
103 $view->assign('canCreateNew', $canCreateNew);
104 $view->assign('maxTitleLength', $backendUser->uc['titleLen'] ?? 20);
105 $view->assign('pageUid', $pageUid);
106
107 if ($canCreateNew) {
108 $newPagesData = (array)$request->getParsedBody()['pages'];
109 if (!empty($newPagesData)) {
110 $hasNewPagesData = true;
111 $afterExisting = isset($request->getParsedBody()['createInListEnd']);
112 $hidePages = isset($request->getParsedBody()['hidePages']);
113 $hidePagesInMenu = isset($request->getParsedBody()['hidePagesInMenus']);
114 $pagesCreated = $this->createPages($newPagesData, $pageUid, $afterExisting, $hidePages, $hidePagesInMenu);
115 $view->assign('pagesCreated', $pagesCreated);
116 $subPages = $this->getSubPagesOfPage($pageUid);
117 $visiblePages = [];
118 foreach ($subPages as $page) {
119 $calculatedPermissions = $backendUser->calcPerms($page);
120 if ($backendUser->isAdmin() || $calculatedPermissions & Permission::PAGE_SHOW) {
121 $visiblePages[] = $page;
122 }
123 }
124 $view->assign('visiblePages', $visiblePages);
125 } else {
126 $hasNewPagesData = false;
127 $view->assign('pageTypes', $this->getTypeSelectData($pageUid));
128 }
129 $view->assign('hasNewPagesData', $hasNewPagesData);
130 }
131
132 $this->moduleTemplate->setContent($view->render());
133 $response->getBody()->write($this->moduleTemplate->renderContent());
134 return $response;
135 }
136
137 /**
138 * Persist new pages in DB
139 *
140 * @param array $newPagesData Data array with title and page type
141 * @param int $pageUid Uid of page new pages should be added in
142 * @param bool $afterExisting True if new pages should be created after existing pages
143 * @param bool $hidePages True if new pages should be set to hidden
144 * @param bool $hidePagesInMenu True if new pages should be set to hidden in menu
145 * @return bool TRUE if at least on pages has been added
146 */
147 protected function createPages(array $newPagesData, int $pageUid, bool $afterExisting, bool $hidePages, bool $hidePagesInMenu): bool
148 {
149 $pagesCreated = false;
150
151 // Set first pid to "-1 * uid of last existing sub page" if pages should be created at end
152 $firstPid = $pageUid;
153 if ($afterExisting) {
154 $subPages = $this->getSubPagesOfPage($pageUid);
155 $lastPage = end($subPages);
156 if (isset($lastPage['uid']) && MathUtility::canBeInterpretedAsInteger($lastPage['uid'])) {
157 $firstPid = -(int)($lastPage['uid']);
158 }
159 }
160
161 $commandArray = [];
162 $firstRecord = true;
163 $previousIdentifier = '';
164 foreach ($newPagesData as $identifier => $data) {
165 if (!trim($data['title'])) {
166 continue;
167 } else {
168 $commandArray['pages'][$identifier]['hidden'] = $hidePages;
169 $commandArray['pages'][$identifier]['nav_hide'] = $hidePagesInMenu;
170 $commandArray['pages'][$identifier]['title'] = $data['title'];
171 $commandArray['pages'][$identifier]['doktype'] = $data['doktype'];
172 if ($firstRecord) {
173 $firstRecord = false;
174 $commandArray['pages'][$identifier]['pid'] = $firstPid;
175 } else {
176 $commandArray['pages'][$identifier]['pid'] = '-' . $previousIdentifier;
177 }
178 $previousIdentifier = $identifier;
179 }
180 }
181
182 if (!empty($commandArray)) {
183 $pagesCreated = true;
184 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
185 // Set default TCA values specific for the user
186 $backendUser = $this->getBackendUser();
187 $tcaDefaultOverride = $backendUser->getTSConfigProp('TCAdefaults');
188 if (is_array($tcaDefaultOverride)) {
189 $dataHandler->setDefaultsFromUserTS($tcaDefaultOverride);
190 }
191 $dataHandler->start($commandArray, []);
192 $dataHandler->process_datamap();
193 BackendUtility::setUpdateSignal('updatePageTree');
194 }
195
196 return $pagesCreated;
197 }
198
199 /**
200 * Page selector type data
201 *
202 * @param int $pageUid Page Uid
203 * @return array
204 */
205 protected function getTypeSelectData(int $pageUid)
206 {
207 $tsConfig = BackendUtility::getPagesTSconfig($pageUid);
208 $pagesTsConfig = $tsConfig['TCEFORM.']['pages.'] ?? [];
209
210 // Find all available doktypes for the current user
211 $types = $GLOBALS['PAGES_TYPES'];
212 unset($types['default']);
213 $types = array_keys($types);
214 $types[] = 1; // default
215 $types[] = 3; // link
216 $types[] = 4; // shortcut
217 $types[] = 7; // mount point
218 $types[] = 199; // spacer
219
220 if (!$this->getBackendUser()->isAdmin() && isset($this->getBackendUser()->groupData['pagetypes_select'])) {
221 $types = GeneralUtility::trimExplode(',', $this->getBackendUser()->groupData['pagetypes_select'], true);
222 }
223 $removeItems = isset($pagesTsConfig['doktype.']['removeItems']) ? GeneralUtility::trimExplode(',', $pagesTsConfig['doktype.']['removeItems'], true) : [];
224 $allowedDoktypes = array_diff($types, $removeItems);
225
226 // All doktypes in the TCA
227 $availableDoktypes = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
228
229 // Sort by group and allowedDoktypes
230 $groupedData = [];
231 $groupLabel = '';
232 foreach ($availableDoktypes as $doktypeData) {
233 // If it is a group, save the group label for the children underneath
234 if ($doktypeData[1] === '--div--') {
235 $groupLabel = $doktypeData[0];
236 } else {
237 if (in_array($doktypeData[1], $allowedDoktypes)) {
238 $groupedData[$groupLabel][] = $doktypeData;
239 }
240 }
241 }
242
243 return $groupedData;
244 }
245
246 /**
247 * Get a list of sub pages with some all fields from given page.
248 * Fetch all data fields for full page icon display
249 *
250 * @param int $pageUid Get sub pages from this pages
251 * @return array
252 */
253 protected function getSubPagesOfPage(int $pageUid): array
254 {
255 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
256 $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
257 return $queryBuilder->select('*')
258 ->from('pages')
259 ->where(
260 $queryBuilder->expr()->eq(
261 'pid',
262 $queryBuilder->createNamedParameter($pageUid, \PDO::PARAM_INT)
263 )
264 )
265 ->orderBy('sorting')
266 ->execute()
267 ->fetchAll();
268 }
269
270 /**
271 * Returns LanguageService
272 *
273 * @return LanguageService
274 */
275 protected function getLanguageService()
276 {
277 return $GLOBALS['LANG'];
278 }
279
280 /**
281 * Returns current BE user
282 *
283 * @return BackendUserAuthentication
284 */
285 protected function getBackendUser()
286 {
287 return $GLOBALS['BE_USER'];
288 }
289 }