Commit 6d956e81 authored by Oliver Bartsch's avatar Oliver Bartsch Committed by Benni Mack
Browse files

[TASK] Clean up Permission controllers

The PermissionController was previously handled
via extbase, even though no pure Extbase-related
feature (validation, type-converting, persistence)
was used. Furthermore a combination of non-extbase
arguments and extbase-prefixed arguments was used,
leading to a couple of GeneralUtility::_GP() usages.

To clean up the controller, it is now not longer
registered as extbase module, while keeping
the module name "system_BeuserTxPermission"
for backwards-compatibility reasons.

Furthermore, is the PermissionAjaxController,
used for inline updates in the tree view,
moved into the PermissionController. This
allowed to streamline and clean up its only
endpoint.

Besides the clean up, some things got improved,
e.g. the shortcut button does now also take the
current action into account and all used labels
can now be translated.

Resolves: #94564
Releases: master
Change-Id: Ic03e341df5b69aa154be1a5b737df2eecc433e55
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69893

Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Jochen's avatarJochen <rothjochen@gmail.com>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Jochen's avatarJochen <rothjochen@gmail.com>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 0de5ac09
......@@ -48,7 +48,7 @@ class Permissions {
for (let permissionCheckbox of checkedPermissionCheckboxes) {
permissionValue |= parseInt(permissionCheckbox.value, 10);
}
document.forms.namedItem('editform')[varname].value = permissionValue | (checknames === 'tx_beuser_system_beusertxpermission[check][perms_user]' ? 1 : 0);
document.forms.namedItem('editform')[varname].value = permissionValue | (checknames === 'check[perms_user]' ? 1 : 0);
}
constructor() {
......@@ -63,6 +63,9 @@ class Permissions {
let page = element.dataset.page;
let who = element.dataset.who;
// Hide all Tooltips to avoid permanent visible/never hidden Tooltips
Tooltip.hide(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
(new AjaxRequest(this.ajaxUrl)).post({
page: page,
who: who,
......
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Beuser\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* This class extends the permissions module in the TYPO3 Backend to provide
* convenient methods of editing of page permissions (including page ownership
* (user and group)) via new AjaxRequestHandler facility
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
*/
class PermissionAjaxController
{
/**
* The local configuration array
*
* @var array
*/
protected $conf;
/**
* @var IconFactory
*/
protected $iconFactory;
/**
* The constructor of this class
*/
public function __construct()
{
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$this->getLanguageService()->includeLLFile('EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf');
}
/**
* The main dispatcher function. Collect data and prepare HTML output.
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function dispatch(ServerRequestInterface $request): ResponseInterface
{
$parsedBody = $request->getParsedBody();
$this->conf = [
'page' => $parsedBody['page'] ?? null,
'who' => $parsedBody['who'] ?? null,
'mode' => $parsedBody['mode'] ?? null,
'bits' => (int)($parsedBody['bits'] ?? 0),
'permissions' => (int)($parsedBody['permissions'] ?? 0),
'action' => $parsedBody['action'] ?? null,
'ownerUid' => (int)($parsedBody['ownerUid'] ?? 0),
'username' => $parsedBody['username'] ?? null,
'groupUid' => (int)($parsedBody['groupUid'] ?? 0),
'groupname' => $parsedBody['groupname'] ?? '',
'editLockState' => (int)($parsedBody['editLockState'] ?? 0),
'new_owner_uid' => (int)($parsedBody['newOwnerUid'] ?? 0),
'new_group_uid' => (int)($parsedBody['newGroupUid'] ?? 0),
];
$extPath = ExtensionManagementUtility::extPath('beuser');
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setPartialRootPaths(['default' => ExtensionManagementUtility::extPath('beuser') . 'Resources/Private/Partials']);
$view->assign('pageId', $this->conf['page']);
$response = new HtmlResponse('');
// Basic test for required value
if ($this->conf['page'] <= 0) {
$response->getBody()->write('This script cannot be called directly');
return $response->withStatus(500);
}
$content = '';
// Init TCE for execution of update
$tce = GeneralUtility::makeInstance(DataHandler::class);
// Determine the scripts to execute
switch ($this->conf['action']) {
case 'show_change_owner_selector':
$content = $this->renderUserSelector($this->conf['page'], $this->conf['ownerUid'], $this->conf['username']);
break;
case 'change_owner':
$userId = $this->conf['new_owner_uid'];
if (is_int($userId)) {
// Prepare data to change
$data = [];
$data['pages'][$this->conf['page']]['perms_userid'] = $userId;
// Execute TCE Update
$tce->start($data, []);
$tce->process_datamap();
$view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/PermissionAjax/ChangeOwner.html');
$view->assign('userId', $userId);
$usernameArray = BackendUtility::getUserNames('username', ' AND uid = ' . $userId);
$view->assign('username', $usernameArray[$userId]['username']);
$content = $view->render();
} else {
$response->getBody()->write('An error occurred: No page owner uid specified');
$response = $response->withStatus(500);
}
break;
case 'show_change_group_selector':
$content = $this->renderGroupSelector($this->conf['page'], $this->conf['groupUid'], $this->conf['groupname']);
break;
case 'change_group':
$groupId = $this->conf['new_group_uid'];
if (is_int($groupId)) {
// Prepare data to change
$data = [];
$data['pages'][$this->conf['page']]['perms_groupid'] = $groupId;
// Execute TCE Update
$tce->start($data, []);
$tce->process_datamap();
$view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/PermissionAjax/ChangeGroup.html');
$view->assign('groupId', $groupId);
$groupnameArray = BackendUtility::getGroupNames('title', ' AND uid = ' . $groupId);
$view->assign('groupname', $groupnameArray[$groupId]['title']);
$content = $view->render();
} else {
$response->getBody()->write('An error occurred: No page group uid specified');
$response = $response->withStatus(500);
}
break;
case 'toggle_edit_lock':
// Prepare data to change
$data = [];
$data['pages'][$this->conf['page']]['editlock'] = $this->conf['editLockState'] === 1 ? 0 : 1;
// Execute TCE Update
$tce->start($data, []);
$tce->process_datamap();
$content = $this->renderToggleEditLock($this->conf['page'], $data['pages'][$this->conf['page']]['editlock']);
break;
default:
if ($this->conf['mode'] === 'delete') {
$this->conf['permissions'] = (int)($this->conf['permissions'] - $this->conf['bits']);
} else {
$this->conf['permissions'] = (int)($this->conf['permissions'] + $this->conf['bits']);
}
// Prepare data to change
$data = [];
$data['pages'][$this->conf['page']]['perms_' . $this->conf['who']] = $this->conf['permissions'];
// Execute TCE Update
$tce->start($data, []);
$tce->process_datamap();
$view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/PermissionAjax/ChangePermission.html');
$view->assign('permission', $this->conf['permissions']);
$view->assign('scope', $this->conf['who']);
$content = $view->render();
}
$response->getBody()->write($content);
return $response;
}
/**
* Generate the user selector element
*
* @param int $page The page id to change the user for
* @param int $ownerUid The page owner uid
* @param string $username The username to display
* @return string The html select element
*/
protected function renderUserSelector($page, $ownerUid, $username = '')
{
$page = (int)$page;
$ownerUid = (int)$ownerUid;
// Get usernames
$beUsers = BackendUtility::getUserNames();
// Owner selector:
$options = '';
// Loop through the users
foreach ($beUsers as $uid => $row) {
$uid = (int)$uid;
$selected = $uid === $ownerUid ? ' selected="selected"' : '';
$options .= '<option value="' . $uid . '"' . $selected . '>' . htmlspecialchars($row['username']) . '</option>';
}
$elementId = 'o_' . $page;
$options = '<option value="0"></option>' . $options;
$selector = '<select name="new_page_owner" id="new_page_owner">' . $options . '</select>';
$saveButton = '<button class="saveowner btn btn-default" data-page="' . $page . '" data-owner="' . $ownerUid
. '" data-element-id="' . $elementId . '" title="Change owner" type="button">'
. $this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL)->render()
. '</button>';
$cancelButton = '<button class="restoreowner btn btn-default" data-page="' . $page . '" data-owner="' . $ownerUid
. '" data-element-id="' . $elementId . '" data-if-not-set="[not set]"'
. (!empty($username) ? ' data-username="' . htmlspecialchars($username) . '"' : '')
. ' title="Cancel" type="button">'
. $this->iconFactory->getIcon('actions-close', Icon::SIZE_SMALL)->render()
. '</button>';
return '<span id="' . $elementId . '">'
. $selector
. '<span class="btn-group">'
. $saveButton
. $cancelButton
. '</span>'
. '</span>';
}
/**
* Generate the group selector element
*
* @param int $page The page id to change the user for
* @param int $groupUid The page group uid
* @param string $groupname The groupname to display
* @return string The html select element
*/
protected function renderGroupSelector($page, $groupUid, $groupname = '')
{
$page = (int)$page;
$groupUid = (int)$groupUid;
// Get group names
$beGroupsO = $beGroups = BackendUtility::getGroupNames();
// Group selector:
$options = '';
// flag: is set if the page-groupid equals one from the group-list
$userset = 0;
// Loop through the groups
foreach ($beGroups as $uid => $row) {
$uid = (int)$uid;
if ($uid === $groupUid) {
$userset = 1;
$selected = ' selected="selected"';
} else {
$selected = '';
}
$options .= '<option value="' . $uid . '"' . $selected . '>' . htmlspecialchars($row['title']) . '</option>';
}
// If the group was not set AND there is a group for the page
if (!$userset && $groupUid) {
$options = '<option value="' . $groupUid . '" selected="selected">' .
htmlspecialchars($beGroupsO[$groupUid]['title']) . '</option>' . $options;
}
$elementId = 'g_' . $page;
$options = '<option value="0"></option>' . $options;
$selector = '<select name="new_page_group" id="new_page_group">' . $options . '</select>';
$saveButton = '<button class="savegroup btn btn-default" data-page="' . $page . '" data-group-id="' . $groupUid
. '" data-element-id="' . $elementId . '" title="Change group" type="button">'
. $this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL)->render()
. '</button>';
$cancelButton = '<button class="restoregroup btn btn-default" data-page="' . $page . '" data-group-id="' . $groupUid
. '" data-element-id="' . $elementId . '" data-if-not-set="[not set]"'
. (!empty($groupname) ? ' data-groupname="' . htmlspecialchars($groupname) . '"' : '')
. ' title="Cancel" type="button">'
. $this->iconFactory->getIcon('actions-close', Icon::SIZE_SMALL)->render()
. '</button>';
return '<span id="' . $elementId . '">'
. $selector
. '<span class="btn-group">'
. $saveButton
. $cancelButton
. '</span>'
. '</span>';
}
/**
* Print the string with the new edit lock state of a page record
*
* @param int $page The TYPO3 page id
* @param int $editLockState The state of the TYPO3 page (locked, unlocked)
* @return string The new edit lock string wrapped in HTML
*/
protected function renderToggleEditLock($page, $editLockState)
{
$page = (int)$page;
if ($editLockState === 1) {
$ret = '<span id="el_' . $page . '"><button type="button" class="editlock btn btn-default" data-page="' . $page
. '" data-lockstate="1" title="The page and all content is locked for editing by all non-Admin users.">'
. $this->iconFactory->getIcon('actions-lock', Icon::SIZE_SMALL)->render() . '</button></span>';
} else {
$ret = '<span id="el_' . $page . '"><button type="button" class="editlock btn btn-default" data-page="' . $page .
'" data-lockstate="0" title="Enable the &raquo;Admin-only&laquo; edit lock for this page">'
. $this->iconFactory->getIcon('actions-unlock', Icon::SIZE_SMALL)->render() . '</button></span>';
}
return $ret;
}
/**
* @return \TYPO3\CMS\Core\Localization\LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'];
}
}
......@@ -7,6 +7,6 @@ return [
// Dispatch the permissions actions
'user_access_permissions' => [
'path' => '/users/access/permissions',
'target' => \TYPO3\CMS\Beuser\Controller\PermissionAjaxController::class . '::dispatch'
'target' => \TYPO3\CMS\Beuser\Controller\PermissionController::class . '::handleAjaxRequest'
]
];
......@@ -6,3 +6,6 @@ services:
TYPO3\CMS\Beuser\:
resource: '../Classes/*'
TYPO3\CMS\Beuser\Controller\PermissionController:
tags: ['backend.controller']
......@@ -66,6 +66,12 @@
<trans-unit id="User" resname="User">
<source>User</source>
</trans-unit>
<trans-unit id="changeOwner" resname="changeOwner">
<source>Change owner</source>
</trans-unit>
<trans-unit id="changeGroup" resname="changeGroup">
<source>Change group</source>
</trans-unit>
<trans-unit id="def" resname="def">
<source>Definition: 'content' is records from all tables on a page - except from records from the table 'pages' (Pages).</source>
</trans-unit>
......
<f:render partial="Groupname" arguments="{pageId: pageId, groupId: groupId, groupname: groupname}" />
<span id="{elementId}">
<select name="new_page_group" id="new_page_group">
<f:if condition="{addCurrentGroup}">
<f:then><option value="{groupUid}" selected>{groupname}</option></f:then>
<f:else><option value="0"></option></f:else>
</f:if>
<f:for each="{groups}" key="uid" as="group">
<option value="{uid}" {f:if(condition: '{uid} == {groupUid}', then: 'selected')}>{group.title}</option>
</f:for>
</select>
<span class="btn-group">
<button type="button"
class="savegroup btn btn-default"
title="{f:translate(key:'LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:changeGroup')}"
data-page="{pageId}"
data-group-id="{groupUid}"
data-element-id="{elementId}"
>
<core:icon identifier="actions-document-save" size="small"/>
</button>
<button type="button"
class="restoregroup btn btn-default"
title="{f:translate(key:'LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel')}"
data-page="{pageId}"
data-group-id="{groupUid}"
data-element-id="{elementId}"
data-if-not-set="[not set]"
{f:if(condition: groupname, then: 'data-groupname="{groupname}"')}
>
<core:icon identifier="actions-close" size="small"/>
</button>
</span>
</span>
<f:render partial="Ownername" arguments="{pageId: pageId, userId: userId, username: username}" />
<span id="{elementId}">
<select name="new_page_owner" id="new_page_owner">
<f:if condition="{addCurrentUser}">
<f:then><option value="{ownerUid}" selected>{username}</option></f:then>
<f:else><option value="0"></option></f:else>
</f:if>
<f:for each="{users}" key="uid" as="user">
<option value="{uid}" {f:if(condition: '{uid} == {ownerUid}', then: 'selected')}>{user.username}</option>
</f:for>
</select>
<span class="btn-group">
<button type="button"
class="saveowner btn btn-default"
title="{f:translate(key:'LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:changeOwner')}"
data-page="{pageId}"
data-owner="{ownerUid}"
data-element-id="{elementId}"
>
<core:icon identifier="actions-document-save" size="small"/>
</button>
<button type="button"
class="restoreowner btn btn-default"
title="{f:translate(key:'LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:cancel')}"
data-page="{pageId}"
data-owner="{ownerUid}"
data-element-id="{elementId}"
data-if-not-set="[not set]"
{f:if(condition: username, then: 'data-username="{username}"')}
>
<core:icon identifier="actions-close" size="small"/>
</button>
</span>
</span>
......@@ -13,26 +13,43 @@
<f:comment><!-- {base} is defined as it's *required* for mathematical operations in Fluid --></f:comment>
<f:variable name="base" value="2" />
<f:form action="update" name="editform" id="PermissionControllerEdit">
<form action="{formAction}" method="post" name="editform" id="PermissionControllerEdit">
<div class="form-group">
<label><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Owner" /></label>
<f:form.select name="data[pages][{id}][perms_userid]" options="{beUserData}" value="{currentBeUser}" class="form-select" />
</div>
<div class="form-group">
<label><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Group" /></label>
<f:if condition="{f:count(subject:beGroupData)} > 1">
<f:then>
<f:form.select name="data[pages][{id}][perms_groupid]" options="{beGroupData}" value="{currentBeGroup}" class="form-select" />
</f:then>
<f:else>
<div class="alert alert-notice"><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:noGroups" /></div>
</f:else>
</f:if>
<div class="row row-cols-auto align-items-end g-3">
<div class="col">
<label for="selectOwner"><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Owner" /></label>
<select class="form-select" name="data[pages][{id}][perms_userid]" id="selectOwner">
<f:for each="{beUserData}" key="userId" as="user">
<option value="{userId}" {f:if(condition: '{userId} == {currentBeUser}', then: 'selected')}>{user}</option>
</f:for>
</select>
</div>
<div class="col">
<label for="selectGroup"><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Group" /></label>
<f:if condition="{f:count(subject:beGroupData)} > 1">
<f:then>
<select class="form-select" name="data[pages][{id}][perms_groupid]" id="selectGroup">
<f:for each="{beGroupData}" key="groupId" as="group">
<option value="{groupId}" {f:if(condition: '{groupId} == {currentBeGroup}', then: 'selected')}>{group}</option>
</f:for>
</select>
</f:then>
<f:else>
<div class="alert alert-notice"><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:noGroups" /></div>
</f:else>
</f:if>
</div>
<div class="col">
<label for="recursionLevel"><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Depth" /></label>
<select class="form-select" name="mirror[pages][{id}]" id="recursionLevel">
<f:for each="{recursiveSelectOptions}" key="depth" as="depthLabel">
<option value="{depth}" {f:if(condition: '{depth} == {currentBeUser}', then: 'selected')}>{depthLabel}</option>
</f:for>
</select>
</div>
</div>
<div class="panel panel-default panel-space">
<div class="panel panel-default mt-3">
<div class="table-fit">
<table class="table table-striped table-hover" id="typo3-permissionMatrix">
<thead>
......@@ -48,47 +65,40 @@
<tbody>
<tr>
<td><strong><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Owner" /></strong></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 0}" data-check-change-permissions="check[perms_user],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 4}" data-check-change-permissions="check[perms_user],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 1}" data-check-change-permissions="check[perms_user],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 2}" data-check-change-permissions="check[perms_user],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 3}" data-check-change-permissions="check[perms_user],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 0}" data-check-change-permissions="check[perms_user],data[pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 4}" data-check-change-permissions="check[perms_user],data[pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 1}" data-check-change-permissions="check[perms_user],data[pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 2}" data-check-change-permissions="check[perms_user],data[pages][{id}][perms_user]" /></td>
<td><input type="checkbox" name="check[perms_user][]" value="{base ^ 3}" data-check-change-permissions="check[perms_user],data[pages][{id}][perms_user]" /></td>
</tr>
<tr>
<td><strong><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Group" /></strong></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 0}" data-check-change-permissions="check[perms_group],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 4}" data-check-change-permissions="check[perms_group],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 1}" data-check-change-permissions="check[perms_group],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 2}" data-check-change-permissions="check[perms_group],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 3}" data-check-change-permissions="check[perms_group],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 0}" data-check-change-permissions="check[perms_group],data[pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 4}" data-check-change-permissions="check[perms_group],data[pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 1}" data-check-change-permissions="check[perms_group],data[pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 2}" data-check-change-permissions="check[perms_group],data[pages][{id}][perms_group]" /></td>
<td><input type="checkbox" name="check[perms_group][]" value="{base ^ 3}" data-check-change-permissions="check[perms_group],data[pages][{id}][perms_group]" /></td>
</tr>
<tr>
<td><strong><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Everybody" /></strong></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 0}" data-check-change-permissions="check[perms_everybody],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 4}" data-check-change-permissions="check[perms_everybody],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 1}" data-check-change-permissions="check[perms_everybody],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 2}" data-check-change-permissions="check[perms_everybody],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 3}" data-check-change-permissions="check[perms_everybody],tx_beuser_system_beusertxpermission[data][pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 0}" data-check-change-permissions="check[perms_everybody],data[pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 4}" data-check-change-permissions="check[perms_everybody],data[pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 1}" data-check-change-permissions="check[perms_everybody],data[pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 2}" data-check-change-permissions="check[perms_everybody],data[pages][{id}][perms_everybody]" /></td>
<td><input type="checkbox" name="check[perms_everybody][]" value="{base ^ 3}" data-check-change-permissions="check[perms_everybody],data[pages][{id}][perms_everybody]" /></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="form-group">
<label for="recursionLevel"><f:translate key="LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Depth" /></label>
<f:form.select id="recursionLevel" name="mirror[pages][{id}]" options="{recursiveSelectOptions}" class="form-select" />
</div>
<f:form.hidden name="data[pages][{id}][perms_user]" value="{pageInfo.perms_user}" data="{'checkbox-group': 'check[perms_user]'}" />
<f:form.hidden name="data[pages][{id}][perms_group]" value="{pageInfo.perms_group}" data="{'checkbox-group': 'check[perms_group]'}" />
<f:form.hidden name="data[pages][{id}][perms_everybody]" value="{pageInfo.perms_everybody}" data="{'checkbox-group': 'check[perms_everybody]'}" />
<f:form.hidden name="depth" value="{depth}" />
<f:form.hidden name="returnUrl" value="{returnUrl}" />
</f:form>
<input type="hidden" name="data[pages][{id}][perms_user]" value="{pageInfo.perms_user}" data-checkbox-group="check[perms_user]" />
<input type="hidden