From 44b2eedb3de7ef631f074d85bb47f1f825b38ef8 Mon Sep 17 00:00:00 2001
From: Aaron McHale <aaronmchale@429621.no-reply.drupal.org>
Date: Tue, 8 Apr 2025 15:59:44 +0100
Subject: [PATCH 1/4] #/3164456 Add ability to negate Role Assign selection

---
 config/schema/roleassign.schema.yml |  3 +++
 roleassign.install                  | 16 ++++++++++++++++
 roleassign.module                   | 12 +++++++++---
 src/Form/RoleAssignAdminForm.php    |  8 ++++++++
 4 files changed, 36 insertions(+), 3 deletions(-)
 create mode 100644 roleassign.install

diff --git a/config/schema/roleassign.schema.yml b/config/schema/roleassign.schema.yml
index bdd4f2e..3e7e62f 100644
--- a/config/schema/roleassign.schema.yml
+++ b/config/schema/roleassign.schema.yml
@@ -8,3 +8,6 @@ roleassign.settings:
       sequence:
         type: string
         label: 'role'
+    roleassign_negate:
+      type: boolean
+      label: 'Negate condition'
diff --git a/roleassign.install b/roleassign.install
new file mode 100644
index 0000000..ce64edf
--- /dev/null
+++ b/roleassign.install
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @file
+ * Install hooks for roleassign module.
+ */
+
+/**
+ * Set default config value for roleassign_negate option.
+ */
+function roleassign_update_100001() {
+  $config_factory = \Drupal::service('config.factory');
+  $config = $config_factory->getEditable('roleassign.settings');
+  $config->set('roleassign_negate', FALSE);
+  $config->save();
+}
diff --git a/roleassign.module b/roleassign.module
index 5041a0d..5654382 100755
--- a/roleassign.module
+++ b/roleassign.module
@@ -239,14 +239,20 @@ function _roleassign_restrict_access() {
  * that only has "assign roles" permission, not "administer permissions".
  */
 function _roleassign_get_assignable_roles() {
-  $config = \Drupal::service('config.factory');
-  $roleassign_roles = array_flip($config->get('roleassign.settings')->get('roleassign_roles'));
+  $config_factory = \Drupal::service('config.factory');
+  $config = $config_factory->get('roleassign.settings');
+  $roleassign_roles = array_flip($config->get('roleassign_roles'));
   $roles = Role::loadMultiple();
   unset($roles[RoleInterface::ANONYMOUS_ID]);
   // Ignore Authenticated user as this is not required.
   unset($roles[RoleInterface::AUTHENTICATED_ID]);
   $roles = array_map(fn(RoleInterface $role) => $role->label(), $roles);
-  return array_intersect_key($roles, $roleassign_roles);
+  if ($config->get('roleassign_negate') === TRUE) {
+    return array_diff_key($roles, $roleassign_roles);
+  }
+  else {
+    return array_intersect_key($roles, $roleassign_roles);
+  }
 }
 
 /**
diff --git a/src/Form/RoleAssignAdminForm.php b/src/Form/RoleAssignAdminForm.php
index a71a82d..096770b 100644
--- a/src/Form/RoleAssignAdminForm.php
+++ b/src/Form/RoleAssignAdminForm.php
@@ -73,6 +73,13 @@ class RoleAssignAdminForm extends ConfigFormBase {
         '#options' => $roles,
         '#description' => $this->t('Select roles that should be available for assignment.'),
       ];
+      // Negate condition.
+      $form['roleassign_negate'] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('Negate'),
+        '#default_value' => $config->get('roleassign_negate'),
+        '#description' => $this->t('Checking this will negate the above selection, therefore all roles except the selected will be available for assignment.'),
+      ];
     }
 
     return parent::buildForm($form, $form_state);
@@ -92,6 +99,7 @@ class RoleAssignAdminForm extends ConfigFormBase {
 
     $this->config('roleassign.settings')
       ->set('roleassign_roles', $roleassign_roles)
+      ->set('roleassign_negate', $form_state->getValue('roleassign_negate'))
       ->save();
 
     parent::submitForm($form, $form_state);
-- 
GitLab


From b781ae934f2e3dc460f88395862bfa8185510e0e Mon Sep 17 00:00:00 2001
From: Aaron McHale <aaronmchale@429621.no-reply.drupal.org>
Date: Tue, 8 Apr 2025 16:55:55 +0100
Subject: [PATCH 2/4] Added tests for negate

---
 .../Functional/RoleAssignPermissionTest.php    | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/tests/src/Functional/RoleAssignPermissionTest.php b/tests/src/Functional/RoleAssignPermissionTest.php
index af89cc0..631d0c0 100644
--- a/tests/src/Functional/RoleAssignPermissionTest.php
+++ b/tests/src/Functional/RoleAssignPermissionTest.php
@@ -97,11 +97,15 @@ class RoleAssignPermissionTest extends BrowserTestBase {
    * Tests that RoleAssign settings are set up correctly.
    */
   public function testRoleAssignSettings() {
-    $assignable_roles = array_filter(\Drupal::config('roleassign.settings')->get('roleassign_roles'));
+    $config = \Drupal::config('roleassign.settings');
+    $assignable_roles = array_filter($config->get('roleassign_roles'));
     $this->assertSame([
       'editor' => 'editor',
       'webmaster' => 'webmaster',
     ], $assignable_roles);
+    $negate = $config->get('roleassign_negate');
+    $this->assertIsBool($negate);
+    $this->assertFalse($negate);
   }
 
   /**
@@ -121,6 +125,18 @@ class RoleAssignPermissionTest extends BrowserTestBase {
     $this->assertSession()->fieldNotExists('edit-roles-siteadmin');
     $this->drupalGet('user/' . $this->testAccount->id() . '/edit');
 
+    // Set negate roles option
+    $this->config('roleassign.settings')->set('roleassign_negate', TRUE)->save();
+    $this->drupalGet('user/' . $this->testAccount->id() . '/edit');
+    $this->assertSession()->fieldExists('edit-roles-siteadmin');
+    $this->assertSession()->fieldNotExists('edit-roles-editor');
+    $this->assertSession()->fieldNotExists('edit-roles-webmaster');
+    $this->config('roleassign.settings')->set('roleassign_negate', FALSE)->save();
+    $this->drupalGet('user/' . $this->testAccount->id() . '/edit');
+    $this->assertSession()->fieldNotExists('edit-roles-siteadmin');
+    $this->assertSession()->fieldExists('edit-roles-editor');
+    $this->assertSession()->fieldExists('edit-roles-webmaster');
+
     // Assign the role "editor" to the account.
     $this->submitForm(["roles[editor]" => "editor"], 'Save');
     $this->assertSession()->pageTextContains('The changes have been saved.');
-- 
GitLab


From e49585f0d1f758d36e2a8272ce0c54aea9bde8ef Mon Sep 17 00:00:00 2001
From: Aaron McHale <aaronmchale@429621.no-reply.drupal.org>
Date: Tue, 8 Apr 2025 17:05:28 +0100
Subject: [PATCH 3/4] Set initial value for roleassign_negate in tests

---
 tests/src/Functional/RoleAssignPermissionTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/src/Functional/RoleAssignPermissionTest.php b/tests/src/Functional/RoleAssignPermissionTest.php
index 631d0c0..c7c68bc 100644
--- a/tests/src/Functional/RoleAssignPermissionTest.php
+++ b/tests/src/Functional/RoleAssignPermissionTest.php
@@ -72,6 +72,7 @@ class RoleAssignPermissionTest extends BrowserTestBase {
         'editor' => 'editor',
         'webmaster' => 'webmaster',
       ])
+      ->set('roleassign_negate', FALSE)
       ->save();
 
     // Create a testAccount that we will be trying to assign roles.
-- 
GitLab


From 04f83a180a191d8ad94b5cff3f4b91e20ca8d4f5 Mon Sep 17 00:00:00 2001
From: Aaron McHale <aaronmchale@429621.no-reply.drupal.org>
Date: Tue, 8 Apr 2025 17:11:26 +0100
Subject: [PATCH 4/4] phpcs fix

---
 tests/src/Functional/RoleAssignPermissionTest.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/src/Functional/RoleAssignPermissionTest.php b/tests/src/Functional/RoleAssignPermissionTest.php
index c7c68bc..5920573 100644
--- a/tests/src/Functional/RoleAssignPermissionTest.php
+++ b/tests/src/Functional/RoleAssignPermissionTest.php
@@ -126,7 +126,7 @@ class RoleAssignPermissionTest extends BrowserTestBase {
     $this->assertSession()->fieldNotExists('edit-roles-siteadmin');
     $this->drupalGet('user/' . $this->testAccount->id() . '/edit');
 
-    // Set negate roles option
+    // Set negate roles option.
     $this->config('roleassign.settings')->set('roleassign_negate', TRUE)->save();
     $this->drupalGet('user/' . $this->testAccount->id() . '/edit');
     $this->assertSession()->fieldExists('edit-roles-siteadmin');
-- 
GitLab