db051643819b36dfdd0e3c40411b4c2e780d749a
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Page / SortSubPagesController.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\Fluid\View\StandaloneView;
32
33 /**
34 * "Sort sub pages" controller - reachable from context menu "more" on page records
35 */
36 class SortSubPagesController
37 {
38 /**
39 * ModuleTemplate object
40 *
41 * @var ModuleTemplate
42 */
43 protected $moduleTemplate;
44
45 /**
46 * Constructor Method
47 *
48 * @var $moduleTemplate ModuleTemplate
49 */
50 public function __construct(ModuleTemplate $moduleTemplate = null)
51 {
52 $this->moduleTemplate = $moduleTemplate ?? GeneralUtility::makeInstance(ModuleTemplate::class);
53 }
54
55 /**
56 * Main function Handling input variables and rendering main view
57 *
58 * @param $request ServerRequestInterface
59 * @param $response ResponseInterface
60 * @return ResponseInterface Response
61 */
62 public function mainAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
63 {
64 $backendUser = $this->getBackendUser();
65 $parentPageUid = (int)$request->getQueryParams()['id'];
66
67 // Show only if there is a valid page and if this page may be viewed by the user
68 $pageInformation = BackendUtility::readPageAccess($parentPageUid, $backendUser->getPagePermsClause(Permission::PAGE_SHOW));
69 if (!is_array($pageInformation)) {
70 // User has no permission on parent page, should not happen, just render an empty page
71 $this->moduleTemplate->setContent('');
72 $response->getBody()->write($this->moduleTemplate->renderContent());
73 return $response;
74 }
75
76 // Doc header handling
77 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
78 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($pageInformation);
79 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
80 $cshButton = $buttonBar->makeHelpButton()
81 ->setModuleName('pages_sort')
82 ->setFieldName('pages_sort');
83 $viewButton = $buttonBar->makeLinkButton()
84 ->setOnClick(BackendUtility::viewOnClick($parentPageUid, '', BackendUtility::BEgetRootLine($parentPageUid)))
85 ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
86 ->setIcon($iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
87 ->setHref('#');
88 $shortcutButton = $buttonBar->makeShortcutButton()
89 ->setModuleName('pages_sort')
90 ->setGetVariables(['id']);
91 $buttonBar->addButton($cshButton)->addButton($viewButton)->addButton($shortcutButton);
92
93 // Main view setup
94 $view = GeneralUtility::makeInstance(StandaloneView::class);
95 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
96 'EXT:backend/Resources/Private/Templates/Page/SortSubPages.html'
97 ));
98
99 $isInWorkspace = $backendUser->workspace !== 0;
100 $view->assign('isInWorkspace', $isInWorkspace);
101 $view->assign('maxTitleLength', $backendUser->uc['titleLen'] ?? 20);
102 $view->assign('parentPageUid', $parentPageUid);
103 $view->assign('dateFormat', $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy']);
104 $view->assign('timeFormat', $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm']);
105
106 if (!$isInWorkspace) {
107 // Apply new sorting if given
108 $newSortBy = $request->getQueryParams()['newSortBy'] ?? null;
109 if ($newSortBy && in_array($newSortBy, ['title', 'subtitle', 'crdate', 'tstamp'], true)) {
110 $this->sortSubPagesByField($parentPageUid, (string)$newSortBy);
111 } elseif ($newSortBy && $newSortBy === 'reverseCurrentSorting') {
112 $this->reverseSortingOfPages($parentPageUid);
113 }
114
115 // Get sub pages, loop through them and add page/user specific permission details
116 $pageRecords = $this->getSubPagesOfPage($parentPageUid);
117 $hasInvisiblePage = false;
118 $subPages = [];
119 foreach ($pageRecords as $page) {
120 $pageWithPermissions = [];
121 $pageWithPermissions['record'] = $page;
122 $calculatedPermissions = $backendUser->calcPerms($page);
123 $pageWithPermissions['canEdit'] = $backendUser->isAdmin() || $calculatedPermissions & Permission::PAGE_EDIT;
124 $canSeePage = $backendUser->isAdmin() || $calculatedPermissions & Permission::PAGE_SHOW;
125 if ($canSeePage) {
126 $subPages[] = $pageWithPermissions;
127 } else {
128 $hasInvisiblePage = true;
129 }
130 }
131 $view->assign('subPages', $subPages);
132 $view->assign('hasInvisiblePage', $hasInvisiblePage);
133 }
134
135 $this->moduleTemplate->setContent($view->render());
136 $response->getBody()->write($this->moduleTemplate->renderContent());
137 return $response;
138 }
139
140 /**
141 * Sort sub pages of given uid by field name alphabetically
142 *
143 * @param int $parentPageUid Parent page uid
144 * @param string $newSortBy Field name to sort by
145 * @throws \RuntimeException If $newSortBy does not validate
146 */
147 protected function sortSubPagesByField(int $parentPageUid, string $newSortBy)
148 {
149 if (!in_array($newSortBy, ['title', 'subtitle', 'crdate', 'tstamp'], true)) {
150 throw new \RuntimeException(
151 'New sort by must be one of "title", "subtitle", "crdate" or tstamp',
152 1498924810
153 );
154 }
155 $subPages = $this->getSubPagesOfPage($parentPageUid, $newSortBy);
156 if (!empty($subPages)) {
157 $subPages = array_reverse($subPages);
158 $this->persistNewSubPageOrder($parentPageUid, $subPages);
159 }
160 }
161
162 /**
163 * Reverse current sorting of sub pages
164 *
165 * @param int $parentPageUid Parent page uid
166 */
167 protected function reverseSortingOfPages(int $parentPageUid)
168 {
169 $subPages = $this->getSubPagesOfPage($parentPageUid);
170 if (!empty($subPages)) {
171 $this->persistNewSubPageOrder($parentPageUid, $subPages);
172 }
173 }
174
175 /**
176 * Store new sub page order
177 *
178 * @param int $parentPageUid Parent page uid
179 * @param array $subPages List of sub pages in new order
180 */
181 protected function persistNewSubPageOrder(int $parentPageUid, array $subPages)
182 {
183 $commandArray = [];
184 foreach ($subPages as $subPage) {
185 $commandArray['pages'][$subPage['uid']]['move'] = $parentPageUid;
186 }
187 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
188 $dataHandler->start([], $commandArray);
189 $dataHandler->process_cmdmap();
190 BackendUtility::setUpdateSignal('updatePageTree');
191 }
192
193 /**
194 * Get a list of sub pages with some all fields from given page.
195 * Fetch all data fields for full page icon display
196 *
197 * @param int $parentPageUid Get sub pages from this pages
198 * @param string $orderBy Order pages by this field
199 * @return array
200 */
201 protected function getSubPagesOfPage(int $parentPageUid, string $orderBy = 'sorting'): array
202 {
203 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
204 $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
205 return $queryBuilder->select('*')
206 ->from('pages')
207 ->where(
208 $queryBuilder->expr()->eq(
209 'pid',
210 $queryBuilder->createNamedParameter($parentPageUid, \PDO::PARAM_INT)
211 )
212 )
213 ->orderBy($orderBy)
214 ->execute()
215 ->fetchAll();
216 }
217
218 /**
219 * Returns LanguageService
220 *
221 * @return LanguageService
222 */
223 protected function getLanguageService()
224 {
225 return $GLOBALS['LANG'];
226 }
227
228 /**
229 * Returns current BE user
230 *
231 * @return BackendUserAuthentication
232 */
233 protected function getBackendUser()
234 {
235 return $GLOBALS['BE_USER'];
236 }
237 }