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