diff --git a/plugins/FeedsUserProcessor.inc b/plugins/FeedsUserProcessor.inc
index 496be88..7117e57 100644
--- a/plugins/FeedsUserProcessor.inc
+++ b/plugins/FeedsUserProcessor.inc
@@ -16,6 +16,15 @@ define('FEEDS_BLOCK_NON_EXISTENT', 'block');
* Feeds processor plugin. Create users from feed items.
*/
class FeedsUserProcessor extends FeedsProcessor {
+ /**
+ * Search by role name.
+ */
+ const ROLE_SEARCH_NAME = 'name';
+
+ /**
+ * Search by role id.
+ */
+ const ROLE_SEARCH_RID = 'rid';
/**
* Define entity type.
@@ -161,33 +170,9 @@ class FeedsUserProcessor extends FeedsProcessor {
break;
case 'roles_list':
- if (!empty($value)) {
- // Assure roles list is an array.
- $roles_list = (array) $value;
- foreach ($roles_list as $role_name) {
- $role_name = trim($role_name);
- if (strlen($role_name) < 1) {
- // No role name provided. Continue to the next role.
- continue;
- }
- $role = user_role_load_by_name($role_name);
- if (!$role && !empty($mapping['autocreate'])) {
- // Create new role if role doesn't exist.
- $role = new stdClass();
- $role->name = $role_name;
- user_role_save($role);
- $role = user_role_load_by_name($role->name);
- }
- if ($role) {
- // Check if the role may be assigned.
- if (isset($mapping['allowed_roles'][$role->rid]) && !$mapping['allowed_roles'][$role->rid]) {
- // This role may *not* be assiged.
- continue;
- }
- $target_user->roles[$role->rid] = $role->name;
- }
- }
- }
+ // Ensure that the role list is an array.
+ $value = (array) $value;
+ $this->rolesListSetTarget($source, $target_user, $target_element, $value, $mapping);
break;
default:
@@ -317,8 +302,10 @@ class FeedsUserProcessor extends FeedsProcessor {
$rids = array_keys($roles);
$rids = array_combine($rids, $rids);
return array(
+ 'role_search' => self::ROLE_SEARCH_NAME,
'allowed_roles' => $rids,
'autocreate' => 0,
+ 'revoke_roles' => 1,
);
}
@@ -333,6 +320,11 @@ class FeedsUserProcessor extends FeedsProcessor {
$mapping += $defaults;
$mapping['allowed_roles'] += $defaults['allowed_roles'];
+ // Role search.
+ $role_search_options = $this->rolesListRoleSearchOptions();
+ $options[] = t('Search roles by: @search', array('@search' => $role_search_options[$mapping['role_search']]));
+
+ // Allowed roles.
$role_names = array();
$roles = user_roles(TRUE);
foreach (array_filter($mapping['allowed_roles']) as $rid => $enabled) {
@@ -344,6 +336,7 @@ class FeedsUserProcessor extends FeedsProcessor {
}
$options[] = t('Allowed roles: %roles', array('%roles' => implode(', ', $role_names)));
+ // Autocreate.
if ($mapping['autocreate']) {
$options[] = t('Automatically create roles');
}
@@ -351,6 +344,14 @@ class FeedsUserProcessor extends FeedsProcessor {
$options[] = t('Only assign existing roles');
}
+ // Revoke roles.
+ if ($mapping['revoke_roles']) {
+ $options[] = t('Revoke roles: yes');
+ }
+ else {
+ $options[] = t('Revoke roles: no');
+ }
+
return implode('
', $options);
}
@@ -367,6 +368,12 @@ class FeedsUserProcessor extends FeedsProcessor {
unset($allowed_roles_options[DRUPAL_AUTHENTICATED_RID]);
return array(
+ 'role_search' => array(
+ '#type' => 'select',
+ '#title' => t('Search roles by'),
+ '#options' => $this->rolesListRoleSearchOptions(),
+ '#default_value' => $mapping['role_search'],
+ ),
'allowed_roles' => array(
'#type' => 'checkboxes',
'#title' => t('Allowed roles'),
@@ -380,6 +387,76 @@ class FeedsUserProcessor extends FeedsProcessor {
'#description' => t("Create the role if it doesn't exist."),
'#default_value' => $mapping['autocreate'],
),
+ 'revoke_roles' => array(
+ '#type' => 'checkbox',
+ '#title' => t('Revoke roles'),
+ '#description' => t('If enabled, roles that are not provided by the feed will be revoked for the user. This affects only the "Allowed roles" as configured above.'),
+ '#default_value' => $mapping['revoke_roles'],
+ ),
);
}
+
+ /**
+ * Returns role list options.
+ */
+ public function rolesListRoleSearchOptions() {
+ return array(
+ self::ROLE_SEARCH_NAME => t('Role name'),
+ self::ROLE_SEARCH_RID => t('Role ID'),
+ );
+ }
+
+ /**
+ * Sets role target on the user entity.
+ */
+ public function rolesListSetTarget(FeedsSource $source, $entity, $target, array $values, array $mapping) {
+ // Add in defaults.
+ $defaults = $this->rolesListDefaults();
+ $mapping += $defaults;
+ $mapping['allowed_roles'] += $defaults['allowed_roles'];
+
+ // Eventually revoke roles. Do not touch roles that are not allowed to set
+ // by the source.
+ if ($mapping['revoke_roles']) {
+ foreach ($mapping['allowed_roles'] as $rid) {
+ unset($entity->roles[$rid]);
+ }
+ }
+
+ foreach ($values as $value) {
+ $role = NULL;
+
+ $value = trim($value);
+ if (strlen($value) < 1) {
+ // No role provided. Continue to the next role.
+ continue;
+ }
+
+ switch ($mapping['role_search']) {
+ case self::ROLE_SEARCH_NAME:
+ $role = user_role_load_by_name($value);
+ if (!$role && !empty($mapping['autocreate'])) {
+ // Create new role if role doesn't exist.
+ $role = new stdClass();
+ $role->name = $role_name;
+ user_role_save($role);
+ $role = user_role_load_by_name($role->name);
+ }
+ break;
+
+ case self::ROLE_SEARCH_RID:
+ $role = user_role_load($value);
+ break;
+ }
+
+ if ($role) {
+ // Check if the role may be assigned.
+ if (isset($mapping['allowed_roles'][$role->rid]) && !$mapping['allowed_roles'][$role->rid]) {
+ // This role may *not* be assiged.
+ continue;
+ }
+ $entity->roles[$role->rid] = $role->name;
+ }
+ }
+ }
}
diff --git a/tests/feeds/users_roles.csv b/tests/feeds/users_roles.csv
index 7ccfc28..414cfef 100644
--- a/tests/feeds/users_roles.csv
+++ b/tests/feeds/users_roles.csv
@@ -1,5 +1,5 @@
-name,mail,since,password,roles
-Morticia,morticia@example.com,1244347500,mort,editor
-Fester,fester@example.com,1241865600,fest,manager
-Gomez,gomez@example.com,1228572000,gome,tester| |manager
-Pugsley,pugsley@example.com,1228260225,pugs,
\ No newline at end of file
+name,mail,since,password,roles,rids
+Morticia,morticia@example.com,1244347500,mort,editor,6
+Fester,fester@example.com,1241865600,fest,manager,4
+Gomez,gomez@example.com,1228572000,gome,tester| |manager,5| |4
+Pugsley,pugsley@example.com,1228260225,pugs,,
\ No newline at end of file
diff --git a/tests/feeds_processor_user.test b/tests/feeds_processor_user.test
index 2082484..9b7398a 100644
--- a/tests/feeds_processor_user.test
+++ b/tests/feeds_processor_user.test
@@ -170,7 +170,7 @@ class FeedsCSVtoUsersTest extends FeedsWebTestCase {
$this->assertTrue(isset($account->roles[$manager_rid]), 'Fester has the manager role.');
$this->assertEqual(2, count($account->roles), 'Fester has two roles.');
- // Assert that Gomez got the manager role but not the editor role, since
+ // Assert that Gomez got the manager role but not the tester role, since
// that role doesn't exist on the system.
$account = user_load_by_name('Gomez');
$this->assertTrue(isset($account->roles[$manager_rid]), 'Gomez has the manager role.');
@@ -240,6 +240,59 @@ class FeedsCSVtoUsersTest extends FeedsWebTestCase {
}
/**
+ * Tests mapping to role using role ID's.
+ */
+ public function testRoleTargetRids() {
+ // Add mapping to role.
+ $this->addMappings('user_import', array(
+ 4 => array(
+ 'source' => 'rids',
+ 'target' => 'roles_list',
+ 'role_search' => FeedsUserProcessor::ROLE_SEARCH_RID,
+ ),
+ ));
+
+ // Create manager and tester roles.
+ $manager_rid = $this->drupalCreateRole(array('access content'), 'manager');
+ $tester_rid = $this->drupalCreateRole(array('access content'), 'tester');
+ // Ensure expected ID's of these roles.
+ $this->assertEqual(4, $manager_rid);
+ $this->assertEqual(5, $tester_rid);
+
+ // Import CSV file.
+ $this->importFile('user_import', $this->absolutePath() . '/tests/feeds/users_roles.csv');
+
+ // Assert that Mortica did not get the editor role and has one role in
+ // total.
+ $account = user_load_by_name('Morticia');
+ $this->assertFalse(in_array('editor', $account->roles), 'Morticia does not have the editor role.');
+ $this->assertEqual(1, count($account->roles), 'Morticia has one role.');
+
+ // Assert that Fester got the manager role and two roles in total.
+ $account = user_load_by_name('Fester');
+ $this->assertTrue(isset($account->roles[$manager_rid]), 'Fester has the manager role.');
+ $this->assertEqual(2, count($account->roles), 'Fester has two roles.');
+
+ // Assert that Gomez got the manager and tester roles.
+ $account = user_load_by_name('Gomez');
+ $this->assertTrue(isset($account->roles[$manager_rid]), 'Gomez has the manager role.');
+ $this->assertTrue(isset($account->roles[$manager_rid]), 'Gomez has the tester role.');
+ $this->assertEqual(3, count($account->roles), 'Gomez has two roles.');
+
+ // Assert that Pugsley only has one role.
+ $account = user_load_by_name('Pugsley');
+ $this->assertEqual(1, count($account->roles), 'Pugsley has one role.');
+
+ // Assert that four roles exist:
+ // - authenticated user
+ // - role from the admin user
+ // - manager
+ // - tester
+ $roles = user_roles(TRUE);
+ $this->assertEqual(4, count($roles), 'Four roles exist.');
+ }
+
+ /**
* Tests mapping to role using only allowed roles.
*/
public function testRoleTargetWithAllowedRoles() {
@@ -282,4 +335,95 @@ class FeedsCSVtoUsersTest extends FeedsWebTestCase {
$this->assertTrue(in_array('tester', $account->roles), 'Gomez has the tester role.');
$this->assertEqual(2, count($account->roles), 'Gomez has two roles.');
}
+
+ /**
+ * Tests that roles can be revoked and that only allowed roles are revoked.
+ */
+ public function testRevokeRoles() {
+ // Create manager role.
+ $manager_rid = $this->drupalCreateRole(array('access content'), 'manager');
+ // Create editor role.
+ $editor_rid = $this->drupalCreateRole(array('access content'), 'editor');
+ // Create tester role.
+ $tester_rid = $this->drupalCreateRole(array('access content'), 'tester');
+
+ // Set to update existing users.
+ $this->setSettings('user_import', 'FeedsUserProcessor', array('update_existing' => 2));
+
+ // Add mapping to role.
+ // The manager role may not be revoked, but the editor role may.
+ $this->addMappings('user_import', array(
+ 4 => array(
+ 'source' => 'roles',
+ 'target' => 'roles_list',
+ 'allowed_roles' => array(
+ $manager_rid => FALSE,
+ $editor_rid => $editor_rid,
+ $tester_rid => $tester_rid,
+ ),
+ 'autocreate' => TRUE,
+ ),
+ ));
+
+ // Create account for Mortica with roles "manager" and "editor". In the
+ // source only "editor" is specified. Mortica should keep both roles.
+ user_save(drupal_anonymous_user(), array(
+ 'name' => 'Mortica',
+ 'mail' => 'morticia@example.com',
+ 'pass' => 'mort',
+ 'status' => 1,
+ 'roles' => array(
+ $manager_rid => $manager_rid,
+ $editor_rid => $editor_rid,
+ ),
+ ));
+ // Create account for Pugsley with roles "manager", "editor" and "tester".
+ // Pugsley has no roles in the source so should only keep the "manager"
+ // role.
+ user_save(drupal_anonymous_user(), array(
+ 'name' => 'Pugsley',
+ 'mail' => 'pugsley@example.com',
+ 'pass' => 'pugs',
+ 'status' => 1,
+ 'roles' => array(
+ $manager_rid => $manager_rid,
+ $editor_rid => $editor_rid,
+ $tester_rid => $tester_rid,
+ ),
+ ));
+ // Create account for Gomez and give it the "editor" role. Gomez has roles
+ // "tester" and "manager" in the source, so it should lose the "editor" role
+ // and gain the "tester" role.
+ user_save(drupal_anonymous_user(), array(
+ 'name' => 'Gomez',
+ 'mail' => 'gomez@example.com',
+ 'pass' => 'gome',
+ 'status' => 1,
+ 'roles' => array(
+ $editor_rid => $editor_rid,
+ ),
+ ));
+
+ // Import CSV file.
+ $this->importFile('user_import', $this->absolutePath() . '/tests/feeds/users_roles.csv');
+
+ // Assert that Mortica kept the manager and editor roles.
+ $account = user_load_by_name('Morticia');
+ $this->assertTrue(isset($account->roles[$manager_rid]), 'Morticia still has the manager role.');
+ $this->assertTrue(isset($account->roles[$editor_rid]), 'Morticia has the editor role.');
+ $this->assertEqual(3, count($account->roles), 'Morticia has three roles.');
+
+ // Assert that Pugsley only kept the manager role.
+ $account = user_load_by_name('Pugsley');
+ $this->assertTrue(isset($account->roles[$manager_rid]), 'Pugsley still has the manager role.');
+ $this->assertFalse(isset($account->roles[$editor_rid]), 'Pugsley no longer has the editor role.');
+ $this->assertFalse(isset($account->roles[$tester_rid]), 'Pugsley no longer has the tester role.');
+ $this->assertEqual(2, count($account->roles), 'Pugsley has two roles.');
+
+ // Assert that Gomez lost the editor role, and gained the tester role.
+ $account = user_load_by_name('Gomez');
+ $this->assertFalse(isset($account->roles[$editor_rid]), 'Gomez no longer has the editor role.');
+ $this->assertTrue(isset($account->roles[$tester_rid]), 'Gomez has the tester role.');
+ $this->assertEqual(2, count($account->roles), 'Gomez has two roles.');
+ }
}
diff --git a/tests/feeds_tests.module b/tests/feeds_tests.module
index 6816b57..5688b66 100644
--- a/tests/feeds_tests.module
+++ b/tests/feeds_tests.module
@@ -352,26 +352,19 @@ function feeds_tests_mapper_unique(FeedsSource $source, $entity_type, $bundle, $
/**
* Implements hook_feeds_after_parse().
- *
- * Empties the list of items to import in case the test says that there are
- * items in there with encoding issues. These items can not be processed during
- * tests without having a test failure because in < PHP 5.4 that would produce
- * the following warning:
- * htmlspecialchars(): Invalid multibyte sequence in argument
- *
- * @see FeedsCSVParserTestCase::testMbstringExtensionDisabled()
*/
function feeds_tests_feeds_after_parse(FeedsSource $source, FeedsParserResult $result) {
+ // Empties the list of items to import in case the test says that there are
+ // items in there with encoding issues. These items can not be processed
+ // during tests without having a test failure because in < PHP 5.4 that would
+ // produce the following warning:
+ // htmlspecialchars(): Invalid multibyte sequence in argument
+ // @see FeedsCSVParserTestCase::testMbstringExtensionDisabled()
if (variable_get('feeds_tests_feeds_after_parse_empty_items', FALSE)) {
// Remove all items. No items will be processed.
$result->items = array();
}
-}
-/**
- * Implements hook_feeds_after_parse().
- */
-function feeds_tests_feeds_after_parse(FeedsSource $source, FeedsParserResult $result) {
if ($source->id == 'user_import') {
foreach ($result->items as &$item) {
if (!empty($item['roles']) && strpos($item['roles'], '|')) {
@@ -381,6 +374,11 @@ function feeds_tests_feeds_after_parse(FeedsSource $source, FeedsParserResult $r
// @see FeedsCSVtoUsersTest::testRoleTargetWithAllowedRoles()
$item['roles'] = explode('|', $item['roles']);
}
+ if (!empty($item['rids']) && strpos($item['rids'], '|')) {
+ // Convert roles value to multiple values.
+ // @see FeedsCSVtoUsersTest::testRoleTargetRids()
+ $item['rids'] = explode('|', $item['rids']);
+ }
}
}
}