From b79bdea482bbd1720548eeda1a901a23430940fb Mon Sep 17 00:00:00 2001
From: Steve Boyd
Date: Thu, 15 Sep 2022 15:06:59 +1200
Subject: [PATCH] API Convert SecurityAdmin to be a ModelAdmin
Co-authored-by: Maxime Rainville
---
client/dist/js/MemberImportForm.js | 2 +-
client/src/legacy/MemberImportForm.js | 12 +-
code/GroupImportForm.php | 121 -------
code/MemberImportForm.php | 146 ---------
code/SecurityAdmin.php | 371 ++++++----------------
tests/behat/features/manage-users.feature | 45 ++-
tests/php/SecurityAdminTest.php | 2 +-
7 files changed, 134 insertions(+), 565 deletions(-)
delete mode 100644 code/GroupImportForm.php
delete mode 100644 code/MemberImportForm.php
diff --git a/client/dist/js/MemberImportForm.js b/client/dist/js/MemberImportForm.js
index f16dd0871..3372bbd31 100644
--- a/client/dist/js/MemberImportForm.js
+++ b/client/dist/js/MemberImportForm.js
@@ -1 +1 @@
-webpackJsonp([2],{"./client/src/legacy/MemberImportForm.js":function(e,n,t){"use strict";var o=t(3);(function(e){return e&&e.__esModule?e:{default:e}})(o).default.entwine("ss",function(e){e(".import-form .advanced").entwine({onmatch:function(){this._super(),this.hide()},onunmatch:function(){this._super()}}),e(".import-form a.toggle-advanced").entwine({onclick:function(e){return this.parents("form:eq(0)").find(".advanced").toggle(),!1}})})}},["./client/src/legacy/MemberImportForm.js"]);
\ No newline at end of file
+webpackJsonp([2],{"./client/src/legacy/MemberImportForm.js":function(n,e,t){"use strict";var o=t(3);(function(n){return n&&n.__esModule?n:{default:n}})(o).default.entwine("ss",function(n){n("#Form_ImportForm .advanced").entwine({onmatch:function(){this._super(),this.hide()},onunmatch:function(){this._super()}}),n("#Form_ImportForm a.toggle-advanced").entwine({onclick:function(e){var t=n("#Form_ImportForm .advanced");return"none"===t.css("display")?t.show():t.hide(),!1}})})}},["./client/src/legacy/MemberImportForm.js"]);
\ No newline at end of file
diff --git a/client/src/legacy/MemberImportForm.js b/client/src/legacy/MemberImportForm.js
index 065e68fa9..fc149a543 100644
--- a/client/src/legacy/MemberImportForm.js
+++ b/client/src/legacy/MemberImportForm.js
@@ -7,7 +7,7 @@ $.entwine('ss', function($){
/**
* Class: .import-form .advanced
*/
- $('.import-form .advanced').entwine({
+ $('#Form_ImportForm .advanced').entwine({
onmatch: function() {
this._super();
@@ -21,13 +21,19 @@ $.entwine('ss', function($){
/**
* Class: .import-form a.toggle-advanced
*/
- $('.import-form a.toggle-advanced').entwine({
+ $('#Form_ImportForm a.toggle-advanced').entwine({
/**
* Function: onclick
*/
onclick: function(e) {
- this.parents('form:eq(0)').find('.advanced').toggle();
+ let $el = $('#Form_ImportForm .advanced');
+ // .toggle() not working for some strange reason, so doing manually
+ if ($el.css('display') === 'none') {
+ $el.show();
+ } else {
+ $el.hide();
+ }
return false;
}
});
diff --git a/code/GroupImportForm.php b/code/GroupImportForm.php
deleted file mode 100644
index 437c73cbb..000000000
--- a/code/GroupImportForm.php
+++ /dev/null
@@ -1,121 +0,0 @@
-Import one or more groups in CSV format (comma-separated values).'
- . ' Show advanced usage
'
- );
-
- $importer = new GroupCsvBulkLoader();
- $importSpec = $importer->getImportSpec();
-
- $columns = implode(', ', array_keys($importSpec['fields'] ?? []));
- $helpHtml .= _t(
- __CLASS__ . '.Help2',
- ''
- . '
Advanced usage
'
- . '
'
- . '- Allowed columns: {columns}
'
- . '- Existing groups are matched by their unique Code value, and updated with any new values from the '
- . 'imported file
'
- . '- Group hierarchies can be created by using a ParentCode column.
'
- . '- Permission codes can be assigned by the PermissionCode column. Existing permission codes are not '
- . 'cleared.
'
- . '
'
- . '
',
- ['columns' => $columns]
- );
-
- $fields = new FieldList(
- new LiteralField('Help', $helpHtml),
- $fileField = new FileField(
- 'CsvFile',
- DBField::create_field('HTMLFragment', _t(
- MemberImportForm::class . '.FileFieldLabel',
- 'CSV File (Allowed extensions: *.csv)'
- ))
- )
- );
- $fileField->getValidator()->setAllowedExtensions(['csv']);
- }
-
- if (!$actions) {
- $action = new FormAction('doImport', _t(MemberImportForm::class . '.BtnImport', 'Import from CSV'));
- $action->addExtraClass('btn btn-outline-secondary font-icon-upload');
- $actions = new FieldList($action);
- }
-
- if (!$validator) {
- $validator = new RequiredFields('CsvFile');
- }
-
- parent::__construct($controller, $name, $fields, $actions, $validator);
-
- $this->addExtraClass('cms');
- $this->addExtraClass('import-form');
- }
-
- public function doImport($data, $form)
- {
- $loader = new GroupCsvBulkLoader();
-
- // load file
- $result = $loader->load($data['CsvFile']['tmp_name']);
-
- // result message
- $msgArr = [];
- if ($result->CreatedCount()) {
- $msgArr[] = _t(
- __CLASS__ . '.ResultCreated',
- 'Created {count} groups',
- ['count' => $result->CreatedCount()]
- );
- }
- if ($result->UpdatedCount()) {
- $msgArr[] = _t(
- __CLASS__ . '.ResultUpdated',
- 'Updated {count} groups',
- ['count' => $result->UpdatedCount()]
- );
- }
- if ($result->DeletedCount()) {
- $msgArr[] = _t(
- __CLASS__ . '.ResultDeleted',
- 'Deleted {count} groups',
- ['count' => $result->DeletedCount()]
- );
- }
- $msg = ($msgArr) ? implode(',', $msgArr) : _t(MemberImportForm::class . '.ResultNone', 'No changes');
-
- $this->sessionMessage($msg, 'good');
-
- $this->controller->redirectBack();
- }
-}
diff --git a/code/MemberImportForm.php b/code/MemberImportForm.php
deleted file mode 100644
index f12566aa6..000000000
--- a/code/MemberImportForm.php
+++ /dev/null
@@ -1,146 +0,0 @@
-Import users in CSV format (comma-separated values).'
- . ' Show advanced usage'
- );
-
- $importer = new MemberCsvBulkLoader();
- $importSpec = $importer->getImportSpec();
-
- $columns = implode(', ', array_keys($importSpec['fields'] ?? []));
- $helpHtml .= _t(
- __CLASS__ . '.Help2',
- ''
- . '
Advanced usage
'
- . '
'
- . '- Allowed columns: {columns}
'
- . '- Existing users are matched by their unique Code property, and updated with any new values from '
- . 'the imported file.
'
- . '- Groups can be assigned by the Groups column. Groups are identified by their Code property, '
- . 'multiple groups can be separated by comma. Existing group memberships are not cleared.
'
- . '
'
- . '
',
- ['columns' => $columns]
- );
-
- $fields = new FieldList(
- new LiteralField('Help', $helpHtml),
- $fileField = new FileField(
- 'CsvFile',
- DBField::create_field('HTMLFragment', _t(
- __CLASS__ . '.FileFieldLabel',
- 'CSV File (Allowed extensions: *.csv)'
- ))
- )
- );
- $fileField->getValidator()->setAllowedExtensions(['csv']);
- }
-
- if (!$actions) {
- $action = new FormAction('doImport', _t(__CLASS__ . '.BtnImport', 'Import from CSV'));
- $action->addExtraClass('btn btn-outline-secondary font-icon-upload');
- $actions = new FieldList($action);
- }
-
- if (!$validator) {
- $validator = new RequiredFields('CsvFile');
- }
-
- parent::__construct($controller, $name, $fields, $actions, $validator);
-
- Requirements::javascript('silverstripe/admin:client/dist/js/vendor.js');
- Requirements::javascript('silverstripe/admin:client/dist/js/MemberImportForm.js');
- Requirements::css('silverstripe/admin:client/dist/styles/bundle.css');
-
- $this->addExtraClass('cms');
- $this->addExtraClass('import-form');
- }
-
- public function doImport($data, $form)
- {
- $loader = new MemberCsvBulkLoader();
-
- // optionally set group relation
- if ($this->group) {
- $loader->setGroups([$this->group]);
- }
-
- // load file
- $result = $loader->load($data['CsvFile']['tmp_name']);
-
- // result message
- $msgArr = [];
- if ($result->CreatedCount()) {
- $msgArr[] = _t(
- __CLASS__ . '.ResultCreated',
- 'Created {count} members',
- ['count' => $result->CreatedCount()]
- );
- }
- if ($result->UpdatedCount()) {
- $msgArr[] = _t(
- __CLASS__ . '.ResultUpdated',
- 'Updated {count} members',
- ['count' => $result->UpdatedCount()]
- );
- }
- if ($result->DeletedCount()) {
- $msgArr[] = _t(
- __CLASS__ . '.ResultDeleted',
- 'Deleted {count} members',
- ['count' => $result->DeletedCount()]
- );
- }
- $msg = ($msgArr) ? implode(',', $msgArr) : _t(__CLASS__ . '.ResultNone', 'No changes');
-
- $this->sessionMessage($msg, 'good');
-
- $this->controller->redirectBack();
- }
-
- /**
- * @param $group Group
- */
- public function setGroup($group)
- {
- $this->group = $group;
- }
-
- /**
- * @return Group
- */
- public function getGroup($group)
- {
- return $this->group;
- }
-}
diff --git a/code/SecurityAdmin.php b/code/SecurityAdmin.php
index e9d3b39f3..8242c7e00 100755
--- a/code/SecurityAdmin.php
+++ b/code/SecurityAdmin.php
@@ -3,314 +3,145 @@
namespace SilverStripe\Admin;
use SilverStripe\CMS\Controllers\CMSMain;
-use SilverStripe\Control\HTTPRequest;
-use SilverStripe\Control\HTTPResponse;
-use SilverStripe\Core\Convert;
-use SilverStripe\Core\Injector\Injector;
-use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
-use SilverStripe\Forms\GridField\GridField;
-use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
-use SilverStripe\Forms\GridField\GridFieldDataColumns;
-use SilverStripe\Forms\GridField\GridFieldDetailForm;
-use SilverStripe\Forms\GridField\GridFieldExportButton;
+use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\Forms\GridField\GridFieldImportButton;
-use SilverStripe\Forms\GridField\GridFieldPageCount;
-use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\LiteralField;
-use SilverStripe\Forms\Tab;
-use SilverStripe\Forms\TabSet;
use SilverStripe\Security\Group;
+use SilverStripe\Security\GroupCsvBulkLoader;
use SilverStripe\Security\Member;
+use SilverStripe\Security\MemberCsvBulkLoader;
use SilverStripe\Security\Permission;
use SilverStripe\Security\PermissionProvider;
use SilverStripe\Security\PermissionRole;
-use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
/**
* Security section of the CMS
*/
-class SecurityAdmin extends LeftAndMain implements PermissionProvider
+class SecurityAdmin extends ModelAdmin implements PermissionProvider
{
-
- private static $url_segment = 'security';
-
- private static $url_rule = '/$Action/$ID/$OtherID';
-
- private static $menu_title = 'Security';
-
- private static $tree_class = Group::class;
-
- private static $subitem_class = Member::class;
-
- private static $required_permission_codes = 'CMS_ACCESS_SecurityAdmin';
-
- private static $menu_icon_class = 'font-icon-torsos-all';
-
- private static $allowed_actions = [
- 'EditForm',
- 'MemberImportForm',
- 'memberimport',
- 'GroupImportForm',
- 'groupimport',
- 'groups',
- 'users',
- 'roles'
+ private static $managed_models = [
+ 'users' => [
+ 'title' => 'Users',
+ 'dataClass' => Member::class
+ ],
+ 'groups' => [
+ 'title' => 'Groups',
+ 'dataClass' => Group::class
+ ],
+ 'roles' => [
+ 'title' => 'Roles',
+ 'dataClass' => PermissionRole::class
+ ],
];
/**
- * Shortcut action for setting the correct active tab.
- *
- * @param HTTPRequest $request
- * @return HTTPResponse
- */
- public function users($request)
- {
- return $this->index($request);
- }
-
- /**
- * Shortcut action for setting the correct active tab.
- *
- * @param HTTPRequest $request
- * @return HTTPResponse
+ * We have to add both the model tab reference and the class name as keys for the importers because ModelAdmin
+ * currently checks for $importers[$modelClass] in some places and $importers[$this->modelTab] in others.
+ * This is a bug that we should fix in CMS 5
*/
- public function groups($request)
- {
- return $this->index($request);
- }
+ private static $model_importers = [
+ 'users' => MemberCsvBulkLoader::class,
+ Member::class => MemberCsvBulkLoader::class,
+ 'groups' => GroupCsvBulkLoader::class,
+ Group::class => GroupCsvBulkLoader::class,
+ ];
- /**
- * Shortcut action for setting the correct active tab.
- *
- * @param HTTPRequest $request
- * @return HTTPResponse
- */
- public function roles($request)
- {
- return $this->index($request);
- }
+ private static $allowed_actions = [
+ 'ImportForm',
+ ];
- public function getEditForm($id = null, $fields = null)
- {
- // Build gridfield configs
- $memberListConfig = GridFieldConfig_RecordEditor::create()
- ->addComponent(Injector::inst()->createWithArgs(GridFieldExportButton::class, ['buttons-before-left']));
- $groupListConfig = GridFieldConfig_RecordEditor::create()
- ->addComponent(Injector::inst()->createWithArgs(GridFieldExportButton::class, ['buttons-before-left']));
+ private static $url_segment = 'security';
- /** @var GridFieldDetailForm $detailForm */
- $detailForm = $memberListConfig->getComponentByType(GridFieldDetailForm::class);
- $memberValidator = Member::singleton()->getValidator();
- $detailForm->setValidator($memberValidator);
+ private static $menu_title = 'Security';
- /** @var GridFieldPageCount $groupPaginator */
- $groupListConfig->removeComponentsByType(GridFieldPageCount::class);
- $groupListConfig->addComponent(Injector::inst()->createWithArgs(GridFieldPageCount::class, ['buttons-before-right']));
+ private static $menu_priority = 0;
- // Add import capabilities. Limit to admin since the import logic can affect assigned permissions
- if (Permission::check('ADMIN')) {
- // @todo when grid field is converted to react use the react component
- $memberListConfig->addComponent(
- GridFieldImportButton::create('buttons-before-left')
- ->setImportIframe($this->Link('memberimport'))
- ->setModalTitle(_t(__CLASS__ . '.IMPORTUSERS', 'Import users'))
- );
- $groupListConfig->addComponent(
- GridFieldImportButton::create('buttons-before-left')
- ->setImportIframe($this->Link('groupimport'))
- ->setModalTitle(_t(__CLASS__ . '.IMPORTGROUPS', 'Import groups'))
- );
- }
+ private static $required_permission_codes = 'CMS_ACCESS_SecurityAdmin';
- // Build gridfield
- $memberList = GridField::create(
- 'Members',
- false,
- Member::get(),
- $memberListConfig
- )->addExtraClass("members_grid");
+ private static $menu_icon_class = 'font-icon-torsos-all';
- // Build group fields
- $groupList = GridField::create(
- 'Groups',
- false,
- Group::get(),
- $groupListConfig
- );
- /** @var GridFieldDataColumns $columns */
- $columns = $groupList->getConfig()->getComponentByType(GridFieldDataColumns::class);
- $columns->setDisplayFields([
- 'Breadcrumbs' => Group::singleton()->fieldLabel('Title')
- ]);
- $columns->setFieldFormatting([
- 'Breadcrumbs' => function ($val, $item) {
- /** @var Group $item */
- return Convert::raw2xml($item->getBreadcrumbs(' > '));
+ public function getManagedModels()
+ {
+ $models = parent::getManagedModels();
+ // Ensure tab titles can be localised
+ foreach ($models as $key => $spec) {
+ switch ($spec['dataClass']) {
+ case Member::class:
+ $spec['title'] = _t(__CLASS__ . '.Users', 'Users');
+ break;
+ case Group::class:
+ case PermissionRole::class:
+ $spec['title'] = singleton($spec['dataClass'])->i18n_plural_name();
}
- ]);
-
- $fields = FieldList::create(
- TabSet::create(
- 'Root',
- Tab::create(
- 'Users',
- _t(__CLASS__ . '.Users', 'Users'),
- LiteralField::create(
- 'MembersCautionText',
- sprintf(
- '%s
',
- _t(
- __CLASS__ . '.MemberListCaution',
- 'Caution: Removing members from this list will remove them from all groups and the database'
- )
- )
- ),
- $memberList
- ),
- Tab::create(
- 'Groups',
- Group::singleton()->i18n_plural_name(),
- $groupList
- )
- )->setTemplate('SilverStripe\\Forms\\CMSTabSet'),
- // necessary for tree node selection in LeftAndMain.EditForm.js
- new HiddenField('ID', false, 0)
- );
-
- // Add roles editing interface
- $rolesTab = null;
- if (Permission::check('APPLY_ROLES')) {
- $rolesField = GridField::create(
- 'Roles',
- false,
- PermissionRole::get(),
- GridFieldConfig_RecordEditor::create()
- );
-
- $rolesTab = $fields->findOrMakeTab('Root.Roles', _t(__CLASS__ . '.TABROLES', 'Roles'));
- $rolesTab->push($rolesField);
}
-
- // Build replacement form
- $form = Form::create(
- $this,
- 'EditForm',
- $fields,
- new FieldList()
- )->setHTMLID('Form_EditForm');
- $form->addExtraClass('cms-edit-form fill-height');
- $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
- $form->addExtraClass('ss-tabset cms-tabset ' . $this->BaseCSSClasses());
- $form->setAttribute('data-pjax-fragment', 'CurrentForm');
-
- $this->extend('updateEditForm', $form);
-
- return $form;
- }
-
- public function memberimport()
- {
- Requirements::clear();
- Requirements::javascript('silverstripe/admin: client/dist/js/vendor.js');
- Requirements::javascript('silverstripe/admin: client/dist/js/MemberImportForm.js');
- Requirements::css('silverstripe/admin: client/dist/styles/bundle.css');
-
- return $this->renderWith('BlankPage', [
- 'Form' => $this->MemberImportForm()->forTemplate(),
- 'Content' => ' '
- ]);
+ return $models;
}
/**
- * @see SecurityAdmin_MemberImportForm
- *
- * @return Form
+ * @return Form|false
*/
- public function MemberImportForm()
+ public function ImportForm()
{
- if (!Permission::check('ADMIN')) {
- return null;
+ $form = parent::ImportForm();
+ if (!$form) {
+ return $form;
}
-
- /** @var Group $group */
- $group = $this->currentPage();
- $form = new MemberImportForm($this, __FUNCTION__);
- $form->setGroup($group);
-
- return $form;
- }
-
- public function groupimport()
- {
- Requirements::clear();
- Requirements::javascript('silverstripe/admin: client/dist/js/vendor.js');
- Requirements::javascript('silverstripe/admin: client/dist/js/MemberImportForm.js');
- Requirements::css('silverstripe/admin: client/dist/styles/bundle.css');
-
- return $this->renderWith('BlankPage', [
- 'Content' => ' ',
- 'Form' => $this->GroupImportForm()->forTemplate()
+ Requirements::javascript('silverstripe/admin:client/dist/js/MemberImportForm.js');
+ $form->Fields()->removeByName('EmptyBeforeImport');
+ $name = match ($this->modelClass) {
+ Member::class => 'SpecForMember',
+ Group::class => 'SpecForGroup',
+ };
+ $form->Fields()->removeByName($name);
+ $class = match ($this->modelClass) {
+ Member::class => MemberCsvBulkLoader::class,
+ Group::class => GroupCsvBulkLoader::class,
+ };
+ $importSpec = $class::create()->getImportSpec();
+ $columns = implode(', ', array_keys($importSpec['fields'] ?? []));
+ $helpHtml = implode("\n", [
+ _t(
+ __CLASS__ . '.Help1',
+ 'Import users in CSV format (comma-separated values).'
+ . ' Show advanced usage
'
+ ),
+ _t(
+ __CLASS__ . '.Help2',
+ ''
+ . '
Advanced usage
'
+ . '
'
+ . '- Allowed columns: {columns}
'
+ . '- Existing users are matched by their unique Code property, and updated with any new values from '
+ . 'the imported file.
'
+ . '- Groups can be assigned by the Groups column. Groups are identified by their Code property, '
+ . 'multiple groups can be separated by comma. Existing group memberships are not cleared.
'
+ . '
'
+ . '
',
+ ['columns' => $columns]
+ )
]);
+ $form->Fields()->insertBefore('_CsvFile', LiteralField::create('Help', $helpHtml));
+ return $form;
}
- /**
- * @see SecurityAdmin_MemberImportForm
- *
- * @skipUpgrade
- * @return Form
- */
- public function GroupImportForm()
- {
- if (!Permission::check('ADMIN')) {
- return null;
- }
-
- return new GroupImportForm($this, __FUNCTION__);
- }
-
- /**
- * Disable GridFieldDetailForm backlinks for this view, as its
- */
- public function Backlink()
- {
- return false;
- }
-
- public function Breadcrumbs($unlinked = false)
+ protected function getGridFieldConfig(): GridFieldConfig
{
- $crumbs = parent::Breadcrumbs($unlinked);
-
- // Name root breadcrumb based on which record is edited,
- // which can only be determined by looking for the fieldname of the GridField.
- // Note: Titles should be same titles as tabs in RootForm().
- $params = $this->getRequest()->allParams();
- if (isset($params['FieldName'])) {
- // TODO FieldName param gets overwritten by nested GridFields,
- // so shows "Members" rather than "Groups" for the following URL:
- // admin/security/EditForm/field/Groups/item/2/ItemEditForm/field/Members/item/1/edit
- $firstCrumb = $crumbs->shift();
- if ($params['FieldName'] == 'Groups') {
- $crumbs->unshift(new ArrayData([
- 'Title' => Group::singleton()->i18n_plural_name(),
- 'Link' => $this->Link('groups')
- ]));
- } elseif ($params['FieldName'] == 'Users') {
- $crumbs->unshift(new ArrayData([
- 'Title' => _t(__CLASS__ . '.Users', 'Users'),
- 'Link' => $this->Link('users')
- ]));
- } elseif ($params['FieldName'] == 'Roles') {
- $crumbs->unshift(new ArrayData([
- 'Title' => _t(__CLASS__ . '.TABROLES', 'Roles'),
- 'Link' => $this->Link('roles')
- ]));
- }
- $crumbs->unshift($firstCrumb);
+ $config = parent::getGridFieldConfig();
+ // Limit import to admin since the import logic can affect assigned permissions
+ if (!Permission::check('ADMIN') || $this->modelClass == PermissionRole::class) {
+ $config->removeComponentsByType(GridFieldImportButton::class);
+ return $config;
}
-
- return $crumbs;
+ /** @var GridFieldImportButton $importButton */
+ $importButton = $config->getComponentByType(GridFieldImportButton::class);
+ $modalTitle = match ($this->modelClass) {
+ Member::class => _t(__CLASS__ . '.IMPORTUSERS', 'Import users'),
+ Group::class => _t(__CLASS__ . '.IMPORTGROUPS', 'Import groups'),
+ };
+ $importButton->setModalTitle($modalTitle);
+ return $config;
}
public function providePermissions()
diff --git a/tests/behat/features/manage-users.feature b/tests/behat/features/manage-users.feature
index 1cecf226b..08184b3f9 100644
--- a/tests/behat/features/manage-users.feature
+++ b/tests/behat/features/manage-users.feature
@@ -14,26 +14,25 @@ Feature: Manage users
And I am logged in with "ADMIN" permissions
And I go to "/admin/security"
-
Scenario: I cannot remove my admin access, but can remove myself from an admin group
When I click the "Groups" CMS tab
- And I click "ADMIN group" in the "#Root_Groups" element
+ And I click "ADMIN group" in the "#Form_EditForm_groups" element
And I should see the "Unlink" button in the "Members" gridfield for the "ADMIN" row
Then I click "Groups" in the ".breadcrumbs-wrapper" element
And I click the "Groups" CMS tab
- And I click "ADMIN group2" in the "#Root_Groups" element
+ And I click "ADMIN group2" in the "#Form_EditForm_groups" element
And I should see the "Unlink" button in the "Members" gridfield for the "ADMIN" row
Then I click the "Unlink" button in the "Members" gridfield for the "ADMIN" row
And I should not see the "Unlink" button in the "Members" gridfield for the "ADMIN" row
Then I click "Groups" in the ".breadcrumbs-wrapper" element
And I click the "Groups" CMS tab
- And I click "ADMIN group" in the "#Root_Groups" element
+ And I click "ADMIN group" in the "#Form_EditForm_groups" element
And I should not see the "Unlink" button in the "Members" gridfield for the "ADMIN" row
Scenario: I can list all users regardless of group
When I click the "Users" CMS tab
- Then I should see "admin@example.org" in the "#Root_Users" element
- And I should see "staffmember@example.org" in the "#Root_Users" element
+ Then I should see "admin@example.org" in the "#Form_EditForm_users" element
+ And I should see "staffmember@example.org" in the "#Form_EditForm_users" element
Scenario: I can search for an existing user by name
When I click the "Users" CMS tab
@@ -41,8 +40,8 @@ Feature: Manage users
And I press the "Advanced" button
And I fill in "Search__FirstName" with "ADMIN"
And I press the "Enter" key in the "Search__FirstName" field
- Then I should see "admin@example.org" in the "#Root_Users" element
- But I should not see "staffmember@example.org" in the "#Root_Users" element
+ Then I should see "admin@example.org" in the "#Form_EditForm_users" element
+ But I should not see "staffmember@example.org" in the "#Form_EditForm_users" element
# Required to avoid "unsaved changes" browser dialog
Then I press the "Close" button
@@ -52,8 +51,8 @@ Feature: Manage users
And I press the "Advanced" button
And I fill in "Search__Email" with "staffmember@example.org"
And I press the "Search" button
- Then I should see "staffmember@example.org" in the "#Root_Users" element
- But I should not see "admin@example.org" in the "#Root_Users" element
+ Then I should see "staffmember@example.org" in the "#Form_EditForm_users" element
+ But I should not see "admin@example.org" in the "#Form_EditForm_users" element
# Required to avoid "unsaved changes" browser dialog
Then I press the "Close" button
@@ -63,17 +62,17 @@ Feature: Manage users
And I press the "Advanced" button
And I fill in "Search__Email" with "staffmember@example.org"
And I press the "Search" button
- And I should see "staffmember@example.org" in the "#Root_Users" element
- And I should not see "admin@example.org" in the "#Root_Users" element
+ And I should see "staffmember@example.org" in the "#Form_EditForm_users" element
+ And I should not see "admin@example.org" in the "#Form_EditForm_users" element
When I press the "Close" button
Then I should see a "Open search and filter" button
- And I should see "staffmember@example.org" in the "#Root_Users" element
- And I should see "admin@example.org" in the "#Root_Users" element
+ And I should see "staffmember@example.org" in the "#Form_EditForm_users" element
+ And I should see "admin@example.org" in the "#Form_EditForm_users" element
Scenario: I can list all users in a specific group
When I click the "Groups" CMS tab
# TODO Please check how performant this is
- And I click "ADMIN group" in the "#Root_Groups" element
+ And I click "ADMIN group" in the "#Form_EditForm_groups" element
Then I should see "admin@example.org" in the "#Root_Members" element
And I should not see "staffmember@example.org" in the "#Root_Members" element
@@ -88,18 +87,18 @@ Feature: Manage users
Then I should see a "Saved member" message
When I go to "admin/security/"
- Then I should see "john.doe@example.org" in the "#Root_Users" element
+ Then I should see "john.doe@example.org" in the "#Form_EditForm_users" element
@modal
Scenario: I can navigate users from the edit form and retain my search query
When I click the "Users" CMS tab
And I press the "First Name" button
- Then I should see "admin@example.org" in the "#Root_Users" element
- And I should see "othermember@example.org" in the "#Root_Users" element
- And I should see "staffmember@example.org" in the "#Root_Users" element
+ Then I should see "admin@example.org" in the "#Form_EditForm_users" element
+ And I should see "othermember@example.org" in the "#Form_EditForm_users" element
+ And I should see "staffmember@example.org" in the "#Form_EditForm_users" element
- When I click "othermember@example.org" in the "#Root_Users" element
+ When I click "othermember@example.org" in the "#Form_EditForm_users" element
And I follow "Go to next record"
Then the "Email" field should contain "staffmember@example.org"
@@ -116,19 +115,19 @@ Feature: Manage users
Scenario: I can edit an existing user and add him to an existing group
When I go to "admin/security/"
And I click the "Users" CMS tab
- And I click "staffmember@example.org" in the "#Root_Users" element
+ And I click "staffmember@example.org" in the "#Form_EditForm_users" element
And I select "ADMIN group" from "Groups"
And I press the "Apply changes|Save" buttons
Then I should see a "Saved Member" message
When I go to "admin/security"
And I click the "Groups" CMS tab
- And I click "ADMIN group" in the "#Root_Groups" element
+ And I click "ADMIN group" in the "#Form_EditForm_groups" element
Then I should see "staffmember@example.org"
Scenario: I can delete an existing user
When I click the "Users" CMS tab
- And I click "staffmember@example.org" in the "#Root_Users" element
+ And I click "staffmember@example.org" in the "#Form_EditForm_users" element
And I press the "Delete" button, confirming the dialog
Then I should see "admin@example.org"
And I should not see "staffmember@example.org"
diff --git a/tests/php/SecurityAdminTest.php b/tests/php/SecurityAdminTest.php
index 586cbedbf..45ac93b00 100644
--- a/tests/php/SecurityAdminTest.php
+++ b/tests/php/SecurityAdminTest.php
@@ -55,7 +55,7 @@ public function testPermissionFieldRespectsHiddenPermissions()
$group = $this->objFromFixture(Group::class, 'admin');
Config::modify()->merge(Permission::class, 'hidden_permissions', ['CMS_ACCESS_ReportAdmin']);
- $response = $this->get(sprintf('admin/security/EditForm/field/Groups/item/%d/edit', $group->ID));
+ $response = $this->get(sprintf('/admin/security/groups/EditForm/field/groups/item/%d/edit', $group->ID));
$this->assertStringContainsString(
'CMS_ACCESS_SecurityAdmin',