[!!!][FEATURE] Introduce PSR-7-based Routing for Backend AJAX Requests
[Packages/TYPO3.CMS.git] / typo3 / sysext / beuser / Classes / Controller / PermissionAjaxController.php
1 <?php
2 namespace TYPO3\CMS\Beuser\Controller;
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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Core\DataHandling\DataHandler;
21 use TYPO3\CMS\Core\Imaging\Icon;
22 use TYPO3\CMS\Core\Imaging\IconFactory;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
25 use TYPO3\CMS\Fluid\View\StandaloneView;
26
27 /**
28 * This class extends the permissions module in the TYPO3 Backend to provide
29 * convenient methods of editing of page permissions (including page ownership
30 * (user and group)) via new AjaxRequestHandler facility
31 */
32 class PermissionAjaxController {
33
34 /**
35 * The local configuration array
36 *
37 * @var array
38 */
39 protected $conf = array();
40
41 /**
42 * @var IconFactory
43 */
44 protected $iconFactory;
45
46 /**
47 * The constructor of this class
48 */
49 public function __construct() {
50 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
51 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_mod_web_perm.xlf');
52 // Configuration, variable assignment
53 $this->conf['page'] = GeneralUtility::_POST('page');
54 $this->conf['who'] = GeneralUtility::_POST('who');
55 $this->conf['mode'] = GeneralUtility::_POST('mode');
56 $this->conf['bits'] = (int)GeneralUtility::_POST('bits');
57 $this->conf['permissions'] = (int)GeneralUtility::_POST('permissions');
58 $this->conf['action'] = GeneralUtility::_POST('action');
59 $this->conf['ownerUid'] = (int)GeneralUtility::_POST('ownerUid');
60 $this->conf['username'] = GeneralUtility::_POST('username');
61 $this->conf['groupUid'] = (int)GeneralUtility::_POST('groupUid');
62 $this->conf['groupname'] = GeneralUtility::_POST('groupname');
63 $this->conf['editLockState'] = (int)GeneralUtility::_POST('editLockState');
64 $this->conf['new_owner_uid'] = (int)GeneralUtility::_POST('newOwnerUid');
65 $this->conf['new_group_uid'] = (int)GeneralUtility::_POST('newGroupUid');
66 }
67
68 /**
69 * The main dispatcher function. Collect data and prepare HTML output.
70 *
71 * @param ServerRequestInterface $request
72 * @param ResponseInterface $response
73 * @return ResponseInterface
74 */
75 public function dispatch(ServerRequestInterface $request, ResponseInterface $response) {
76 $extPath = ExtensionManagementUtility::extPath('beuser');
77
78 $view = GeneralUtility::makeInstance(StandaloneView::class);
79 $view->setPartialRootPaths(array('default' => ExtensionManagementUtility::extPath('beuser') . 'Resources/Private/Partials'));
80 $view->assign('pageId', $this->conf['page']);
81
82 $content = '';
83 // Basic test for required value
84 if ($this->conf['page'] > 0) {
85 // Init TCE for execution of update
86 /** @var $tce DataHandler */
87 $tce = GeneralUtility::makeInstance(DataHandler::class);
88 $tce->stripslashes_values = FALSE;
89 // Determine the scripts to execute
90 switch ($this->conf['action']) {
91 case 'show_change_owner_selector':
92 $content = $this->renderUserSelector($this->conf['page'], $this->conf['ownerUid'], $this->conf['username']);
93 break;
94 case 'change_owner':
95 $userId = $this->conf['new_owner_uid'];
96 if (is_int($userId)) {
97 // Prepare data to change
98 $data = array();
99 $data['pages'][$this->conf['page']]['perms_userid'] = $userId;
100 // Execute TCE Update
101 $tce->start($data, array());
102 $tce->process_datamap();
103
104 $view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/PermissionAjax/ChangeOwner.html');
105 $view->assign('userId', $userId);
106 $usernameArray = BackendUtility::getUserNames('username', ' AND uid = ' . $userId);
107 $view->assign('username', $usernameArray[$userId]['username']);
108 $content = $view->render();
109 } else {
110 $response->getBody()->write('An error occurred: No page owner uid specified');
111 $response = $response->withStatus(500);
112 }
113 break;
114 case 'show_change_group_selector':
115 $content = $this->renderGroupSelector($this->conf['page'], $this->conf['groupUid'], $this->conf['groupname']);
116 break;
117 case 'change_group':
118 $groupId = $this->conf['new_group_uid'];
119 if (is_int($groupId)) {
120 // Prepare data to change
121 $data = array();
122 $data['pages'][$this->conf['page']]['perms_groupid'] = $groupId;
123 // Execute TCE Update
124 $tce->start($data, array());
125 $tce->process_datamap();
126
127 $view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/PermissionAjax/ChangeGroup.html');
128 $view->assign('groupId', $groupId);
129 $groupnameArray = BackendUtility::getGroupNames('title', ' AND uid = ' . $groupId);
130 $view->assign('groupname', $groupnameArray[$groupId]['title']);
131 $content = $view->render();
132 } else {
133 $response->getBody()->write('An error occurred: No page group uid specified');
134 $response = $response->withStatus(500);
135 }
136 break;
137 case 'toggle_edit_lock':
138 // Prepare data to change
139 $data = array();
140 $data['pages'][$this->conf['page']]['editlock'] = $this->conf['editLockState'] === 1 ? 0 : 1;
141 // Execute TCE Update
142 $tce->start($data, array());
143 $tce->process_datamap();
144 $content = $this->renderToggleEditLock($this->conf['page'], $data['pages'][$this->conf['page']]['editlock']);
145 break;
146 default:
147 if ($this->conf['mode'] === 'delete') {
148 $this->conf['permissions'] = (int)($this->conf['permissions'] - $this->conf['bits']);
149 } else {
150 $this->conf['permissions'] = (int)($this->conf['permissions'] + $this->conf['bits']);
151 }
152 // Prepare data to change
153 $data = array();
154 $data['pages'][$this->conf['page']]['perms_' . $this->conf['who']] = $this->conf['permissions'];
155 // Execute TCE Update
156 $tce->start($data, array());
157 $tce->process_datamap();
158
159 $view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/PermissionAjax/ChangePermission.html');
160 $view->assign('permission', $this->conf['permissions']);
161 $view->assign('scope', $this->conf['who']);
162 $content = $view->render();
163 }
164 } else {
165 $response->getBody()->write('This script cannot be called directly');
166 $response = $response->withStatus(500);
167 }
168 $response->getBody()->write($content);
169 $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
170 return $response;
171 }
172
173 /**
174 * Generate the user selector element
175 *
176 * @param int $page The page id to change the user for
177 * @param int $ownerUid The page owner uid
178 * @param string $username The username to display
179 * @return string The html select element
180 */
181 protected function renderUserSelector($page, $ownerUid, $username = '') {
182 $page = (int)$page;
183 $ownerUid = (int)$ownerUid;
184 // Get usernames
185 $beUsers = BackendUtility::getUserNames();
186 // Owner selector:
187 $options = '';
188 // Loop through the users
189 foreach ($beUsers as $uid => $row) {
190 $uid = (int)$uid;
191 $selected = $uid === $ownerUid ? ' selected="selected"' : '';
192 $options .= '<option value="' . $uid . '"' . $selected . '>' . htmlspecialchars($row['username']) . '</option>';
193 }
194 $elementId = 'o_' . $page;
195 $options = '<option value="0"></option>' . $options;
196 $selector = '<select name="new_page_owner" id="new_page_owner">' . $options . '</select>';
197 $saveButton = '<a class="saveowner" data-page="' . $page . '" data-owner="' . $ownerUid . '" data-element-id="' . $elementId . '" title="Change owner">' . $this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL) . '</a>';
198 $cancelButton = '<a class="restoreowner" data-page="' . $page . '" data-owner="' . $ownerUid . '" data-element-id="' . $elementId . '"' . (!empty($username) ? ' data-username="' . htmlspecialchars($username) . '"' : '') . ' title="Cancel">' . $this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL) . '</a>';
199 return '<span id="' . $elementId . '">' . $selector . $saveButton . $cancelButton . '</span>';
200 }
201
202 /**
203 * Generate the group selector element
204 *
205 * @param int $page The page id to change the user for
206 * @param int $groupUid The page group uid
207 * @param string $groupname The groupname to display
208 * @return string The html select element
209 */
210 protected function renderGroupSelector($page, $groupUid, $groupname = '') {
211 $page = (int)$page;
212 $groupUid = (int)$groupUid;
213
214 // Get usernames
215 $beGroupsO = $beGroups = BackendUtility::getGroupNames();
216 // Group selector:
217 $options = '';
218 // flag: is set if the page-groupid equals one from the group-list
219 $userset = 0;
220 // Loop through the groups
221 foreach ($beGroups as $uid => $row) {
222 $uid = (int)$uid;
223 if ($uid === $groupUid) {
224 $userset = 1;
225 $selected = ' selected="selected"';
226 } else {
227 $selected = '';
228 }
229 $options .= '<option value="' . $uid . '"' . $selected . '>' . htmlspecialchars($row['title']) . '</option>';
230 }
231 // If the group was not set AND there is a group for the page
232 if (!$userset && $groupUid) {
233 $options = '<option value="' . $groupUid . '" selected="selected">' .
234 htmlspecialchars($beGroupsO[$groupUid]['title']) . '</option>' . $options;
235 }
236 $elementId = 'g_' . $page;
237 $options = '<option value="0"></option>' . $options;
238 $selector = '<select name="new_page_group" id="new_page_group">' . $options . '</select>';
239 $saveButton = '<a class="savegroup" data-page="' . $page . '" data-group="' . $groupUid . '" data-element-id="' . $elementId . '" title="Change group">' . $this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL) . '</a>';
240 $cancelButton = '<a class="restoregroup" data-page="' . $page . '" data-group="' . $groupUid . '" data-element-id="' . $elementId . '"' . (!empty($groupname) ? ' data-groupname="' . htmlspecialchars($groupname) . '"' : '') . ' title="Cancel">' . $this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL) . '</a>';
241 return '<span id="' . $elementId . '">' . $selector . $saveButton . $cancelButton . '</span>';
242 }
243
244 /**
245 * Print the string with the new owner of a page record
246 *
247 * @param int $page The TYPO3 page id
248 * @param int $ownerUid The new page user uid
249 * @param string $username The TYPO3 BE username (used to display in the element)
250 * @param bool $validUser Must be set to FALSE, if the user has no name or is deleted
251 * @return string The new group wrapped in HTML
252 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. This is now solved with fluid.
253 */
254 static public function renderOwnername($page, $ownerUid, $username, $validUser = TRUE) {
255 GeneralUtility::logDeprecatedFunction();
256 $elementId = 'o_' . $page;
257 return '<span id="' . $elementId . '"><a class="ug_selector changeowner" data-page="' . $page . '" data-owner="' . $ownerUid . '" data-username="' . htmlspecialchars($username) . '">' . ($validUser ? ($username == '' ? '<span class=not_set>[' . $GLOBALS['LANG']->getLL('notSet') . ']</span>' : htmlspecialchars(GeneralUtility::fixed_lgd_cs($username, 20))) : '<span class=not_set title="' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($username, 20)) . '">[' . $GLOBALS['LANG']->getLL('deleted') . ']</span>') . '</a></span>';
258 }
259
260 /**
261 * Print the string with the new group of a page record
262 *
263 * @param int $page The TYPO3 page id
264 * @param int $groupUid The new page group uid
265 * @param string $groupname The TYPO3 BE groupname (used to display in the element)
266 * @param bool $validGroup Must be set to FALSE, if the group has no name or is deleted
267 * @return string The new group wrapped in HTML
268 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. This is now solved with fluid.
269 */
270 static public function renderGroupname($page, $groupUid, $groupname, $validGroup = TRUE) {
271 GeneralUtility::logDeprecatedFunction();
272 $elementId = 'g_' . $page;
273 return '<span id="' . $elementId . '"><a class="ug_selector changegroup" data-page="' . $page . '" data-group="' . $groupUid . '" data-groupname="' . htmlspecialchars($groupname) . '">' . ($validGroup ? ($groupname == '' ? '<span class=not_set>[' . $GLOBALS['LANG']->getLL('notSet') . ']</span>' : htmlspecialchars(GeneralUtility::fixed_lgd_cs($groupname, 20))) : '<span class=not_set title="' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($groupname, 20)) . '">[' . $GLOBALS['LANG']->getLL('deleted') . ']</span>') . '</a></span>';
274 }
275
276 /**
277 * Print the string with the new edit lock state of a page record
278 *
279 * @param int $page The TYPO3 page id
280 * @param string $editLockState The state of the TYPO3 page (locked, unlocked)
281 * @return string The new edit lock string wrapped in HTML
282 */
283 protected function renderToggleEditLock($page, $editLockState) {
284 $page = (int)$page;
285 if ($editLockState === 1) {
286 $ret = '<span id="el_' . $page . '"><a class="editlock" data-page="' . $page . '" data-lockstate="1" title="The page and all content is locked for editing by all non-Admin users.">' . $this->iconFactory->getIcon('status-warning-lock', Icon::SIZE_SMALL) . '</a></span>';
287 } else {
288 $ret = '<span id="el_' . $page . '"><a class="editlock" data-page="' . $page . '" data-lockstate="0" title="Enable the &raquo;Admin-only&laquo; edit lock for this page">[+]</a></span>';
289 }
290 return $ret;
291 }
292
293 /**
294 * Print a set of permissions. Also used in index.php
295 *
296 * @param int $int Permission integer (bits)
297 * @param int $pageId The TYPO3 page id
298 * @param string $who The scope (user, group or everybody)
299 * @return string HTML marked up x/* indications.
300 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. This is now solved with fluid.
301 */
302 static public function renderPermissions($int, $pageId = 0, $who = 'user') {
303 GeneralUtility::logDeprecatedFunction();
304 $str = '';
305 $permissions = array(1, 16, 2, 4, 8);
306 /** @var IconFactory $iconFactory */
307 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
308 foreach ($permissions as $permission) {
309 if ($int & $permission) {
310 $str .= '<span title="' . $GLOBALS['LANG']->getLL($permission, TRUE)
311 . ' class="change-permission text-success"'
312 . ' data-page="' . (int)$pageId . '"'
313 . ' data-permissions="' . (int)$int . '"'
314 . ' data-mode="delete"'
315 . ' data-who="' . htmlspecialchars($who) . '"'
316 . ' data-bits="' . $permission . '"'
317 . ' style="cursor:pointer">'
318 . $iconFactory->getIcon('status-status-permission-granted', Icon::SIZE_SMALL)
319 . '</span>';
320 } else {
321 $str .= '<span title="' . $GLOBALS['LANG']->getLL($permission, TRUE) . '"'
322 . ' class="change-permission text-danger"'
323 . ' data-page="' . (int)$pageId . '"'
324 . ' data-permissions="' . (int)$int . '"'
325 . ' data-mode="add"'
326 . ' data-who="' . htmlspecialchars($who) . '"'
327 . ' data-bits="' . $permission . '"'
328 . ' style="cursor:pointer">'
329 . $iconFactory->getIcon('status-status-permission-denied', Icon::SIZE_SMALL)
330 . '</span>';
331 }
332 }
333 return '<span id="' . $pageId . '_' . $who . '">' . $str . '</span>';
334 }
335
336 /**
337 * @return \TYPO3\CMS\Lang\LanguageService
338 */
339 protected function getLanguageService() {
340 return $GLOBALS['LANG'];
341 }
342
343 /**
344 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
345 */
346 protected function getBackendUser() {
347 return $GLOBALS['BE_USER'];
348 }
349
350 }