I have an application where I'd like to assign users to a role programatically, if the user meets some criteria. I am finding this difficult to do, since Drupal appears to be unsetting anything that I set in the users_roles table of the database.

Right now, I use hook_user to add a portion of the user registration/edit screen that deals with the application's user settings. I've put code into the 'validate', 'update' and 'insert' options, but I've tried:

  • Adding records the the users_roles table. I add them; Drupal removes them behind my back.
  • Modifying the $user->roles array in the 'validate' hook. Drupal only laughs at me :-( and the change is ignored.
  • Modifying $_POST['edit']['roles'] to fool user module's form processing code. Alas, Drupal ignores me.

Any idea of how the actually do this?

BTW, you core people: an API for this would be really nice.

Comments

Torenware’s picture

Let me vote again for an API...

I've been walking Drupal through the Zend debugger. The ways roles get set in the 4.6 codebase should not be discussed near young children 8-) Or people with sensitive stomachs, for that matter.

I could try to "out-smart" user.module, but the chances of any code I'd create working for more than a dot-rev is pretty small: even the content type and format of the $edit['roles'] array can change during the same request (from an indexed array to a keyed array, for example), so it isn't clear what a module should do in order to set a role.

Hopefully, someone who groks user.module will tell me what to do, besides punch a hole in my monitor :-)

Rob Thorne
Torenware Networks
http://www.torenware.com

Rob Thorne
Torenware Networks
http://www.torenware.com

Hossam’s picture

You can..

user_multiple_role_edit(array($node->uid), 'add_role', rule_id); //where rule_id is $rid in http://api.drupal.org/api/drupal/modules--user--user.module/function/use...

I use this in my live football subscription site.

kitt’s picture

Someone with more experience with the user.module may correct me, but how about:

<?php
$myuser
= user_load($user_uid);
$myuserroles = $myuser->roles[];
$myuserroles[] = 'newrole';
user_save($myuser, array('roles' => $myuserroles));
 
?>

or, to be more correct, use module_invoke:

<?php
$myuser
= module_invoke('user', 'load', $user_uid);
$myuserroles = $myuser->roles[];
$myuserroles[]  = 'newrole';
module_invoke('user', 'save', $myuser, array('roles' => $myuserroles));
 
?>

The user_save function reloads the user's roles into the session. I'm not sure where the core modules would delete a user's roles without explicitly calling the delete (role or user) functions.

Torenware’s picture

Part of the problem in my case is that I am competing with the form on the the admin/user/create page.

The problem is that the when I set the role in hook_user, some piece of code is setting the roles back afterwards.

So while you are correct about what user_save does, my problem is that user_save will get called again later in the request, with different settings. That is the problem.

Rob Thorne
Torenware Networks
http://www.torenware.com

Rob Thorne
Torenware Networks
http://www.torenware.com

caveman’s picture

I'm not a php programmer, but have been looking for something like this for awhile.

What I'm envisioning would be to have a registration form to apply for a certain role. When they submit it, the page sends me the data, then forwards them to a "Welcome to your new role" page. Could I use this script on the second page to set their new role? There would be no "competing form" like Torenware mentioned, so maybe this script would be fine in my setting...

If so, it would be a perfect solution!

Thanks!
Caveman

beauregard’s picture

I am looking for the same, was seaching a lot already, posted also a topic (http://drupal.org/node/52549), up to now also no answer

Tharion’s picture

Hello,

I'm looking for the same feature, a possibility to assign a user role to a user programatically. If You will creeate any solution, please contact me.

For my purpuse, a hook function for manipulating user roles would be a good solution.

Tharion

titouille’s picture

Hello !

I have a problem with the role assignement. I have build a multi-site system with a domain site and some sub-domain sub-sites. Each one represent a store with e-commerce module. I have shared the users / users_roles and others db tables to have same login to all stores, and enabled singlesignon module to give to user a single log-in phase to all domain/sub-domains.

Now I have created a specific profile field (profile_store_key) and a variable "ec_store_key" to match if user is in it's own shop or in another. If it's it shop I add "seller" role and if it's not I remove the seller role if exists.
I have scripted a module with an "init" hook to tests the values and add/remove the role on the fly, but nothing work like I want... user must log-out to reflect the change, and even he logout some time stanges behaviours occurs.

I have tried to add / remove the roles simply with SQL and insert / delete into users_roles tables, to use "save_user" method but I allready have the sames behaviours.

Example : I'm the owner of www.mydomain.com and seller1 is owner of shoes.mydomain.com
When I connect seller1 with seller role on main domain, seller role is supress and customer role added, but seller1 have access to all seller privileges, even if in it's profile he don't have the seller role. If I go to shoes sub-domain, the system add the seller role to him but he doesn't have acess to the privileges of seller...

this is my function, some code are commented to test different approach but result are always same...

<?php
function seller_switch_domain_match_keys( $user, $mainDBName )
    {
   
$sitev = seller_switch_domain_get_variable( 'ec_store_key' );
   
$profilev = $user->profile_store_key;
   
   
$v = false;
   
$r = null;
   
   
$uid = $user->uid;
   
    if(
$profilev != null && $profilev != "" )
        {
        if(
$sitev == $profilev && !isset( $user->roles[5] ) )
            {
           
// if user dosen't have seller role and is the owner of the shop, add "seller role";

            #db_query( "insert into $mainDBName.users_roles set uid=$uid, rid=5;" );
            #db_query( "delete from $mainDBName.users_roles where uid=$uid and rid=7;" );

           
$v = true;
           
$r = $user->roles;
            unset(
$r[7] );
           
$r[5] = "seller";
            }
        else if(
$sitev != $profilev && isset( $user->roles[5] ) )
            {
           
// if user has seller role but isn't the owner of the shop, remove "seller role";

            #db_query( "insert into $mainDBName.users_roles set uid=$uid, rid=7;" );
            #db_query( "delete from $mainDBName.users_roles where uid=$uid and rid=5;" );

           
$v = true;
           
$r = $user->roles;
           
$r[7] = "customer";
            unset(
$r[5] );
            }
       
        }
    else
        {
        if( isset(
$user->roles[5] ) )
            {
           
// if user has seller role but isn't the owner of the shop, remove "seller role";

            #db_query( "insert into $mainDBName.users_roles set uid=$uid, rid=7;" );
            #db_query( "delete from $mainDBName.users_roles where uid=$uid and rid=5;" );

           
$v = true;
           
$r = $user->roles;
           
$r[7] = "customer";
            unset(
$r[5] );
            }
       
        }
   
/*
    if( $v == true )
        {
        $user = user_save( $user, array( "roles" => $r ), 'account' );
          // Delete that user's menu cache:
          cache_clear_all($uid .':', 'cache_menu', TRUE);
       
          // Clear the page cache because pages can contain usernames and/or profile information:
          cache_clear_all();
        }
    */
   
if( $v )
        {
       
db_query( "delete from $mainDBName.users_roles where uid=$uid;" );
        foreach(
$r as $key => $value )
           
db_query( "insert into $mainDBName.users_roles set uid=$uid, rid=$key;" );
        }
   
cache_clear_all();
   
cache_clear_all($uid .':', 'cache_menu', TRUE);
    return
$user;
    }
?>

Is there some mistake in my process ? is there data into sessions that I must to change ? I don't understand why but impossible to do what I want :-(
If anyone has some suggestions about that it was very helpfull.

Thanks in advance :)

arosboro’s picture

Creating the role:

$user_role = new stdClass();
$user_role->name = 'vtiger portal user';
$user_role->weight = 2;
// save the role to the Drupal database
user_role_save($user_role);

Assigning the role:

global $user;
$key = array_search('your role', $user->roles);
if ($key == FALSE) {
  $roles = user_roles(TRUE);
  $rid = array_search('your role', $roles);
  if ($rid != FALSE) {
    $new_role[$rid] = 'your role';
    user_save($user, array('roles' => $new_role));
  }
}
munkle’s picture

Hello,

I don't know if this is any help, but this is how I solved a similar problem.

I wanted to make it so that when users register they basically get to chose a role. I found a way that worked for me.

First you need to install logintoboggan and patch the module using this patch: http://drupal.org/node/58084

I have the patched version if anyone needs it.

I got logintoboggan to forward new users to a page I'd created with a simple form on it:

<?php

  

global $user;

   if (

$_POST["UpgradeRole"] == "Upgrade")
      {
        
$userid = $user->uid;
        
db_query("INSERT INTO {users_roles} (rid, uid) VALUES ('%d','%d')", 5, $userid);
   }
?>

<form method="POST"  action="/node/46">
Click checkbox to upgrade role:

<input name="UpgradeRole" type="checkbox" value="Upgrade">
<input type="submit" name="Go">
</form>

So when the user clicks 'Go' the form will call itself and update the roles. I've set the role id in this example to 5 as you can see on the db_query part, but this could be any role id.

dbotton’s picture

It is important that you clear the menu cache following a change to the roles. Use:

cache_clear_all('menu:'. $user->uid, TRUE);

If you do not do this you will find strange things happen if you have changed access to things like blogs, nodes, etc.

grendzy’s picture

Thanks - I was updating the users_roles table, and the results were very strange indeed.

Clearing the menu cache fixed it!

Pauly Jura’s picture

I just did something similar on my site, where I automatically assign new users a "pending" role, and can assign other roles based on information in their "profile" whenever they update their details.

Code looks something like this:

<?php
function module_user ($op, &$edit, &$user, $category=null)
{
  if (
$op == "insert" || $op == "update")
  {
   
// clear first
   
db_query( 'DELETE FROM {users_roles} WHERE uid = %d', $user->uid );

   
// custom code here based on $user->profile_* settings
   
profile_load_profile( $user ); // make sure we load profile data here before continuing
   
if ($user->profile_foo == "bar")
    {
     
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $user->uid, $rid); // $rid = whatever you like
   
}

   
// special case for new users
   
if ($op == "insert")
    {
     
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $user->uid, $rid); // $rid = whatever you like
   
}
  }
}
?>

The profile module has a check for if (empty($user->{$field->name})) in the profile_load_profile function that prevents it from overwriting the current settings with just-submitted ones, just comment that out so you have access to the correct profile data.

Seems to work well. Hope this helps.

-Pauly

awakenedvoice’s picture

Will the fix you mentioned work if the user selects a certain profile field during registration? I have a list box with three different choices that I would like a user to choose from and then have their role assigned based on the choice of in that listbox.

Rob Safuto
Media Master
http://www.podcastnyc.net

Rob Safuto
Learn By The Drop is a place to learn Drupal. If you're new to Drupal I recommend having a look at my Beginner's Guide To Drupal.

alienzed’s picture

this is exactly what I need as well

SoftDux’s picture

Hi Pauly

Where do you use this code? It seems like your example of how this would work is what I need, but I'm not sure how to use / implement it. Could this work if I have a drop down on the registration page, with the three different roles?

----------------------------------------------------------------------------------------------------------
*cPanel :: Fantastico :: RVSkin :: WHM :: ModernBill
*Reseller Hosting :: SSL Certificates :: Domain Registrations :: Affiliate Program
*Bl

SomebodySysop’s picture

Where does this function go?

function module_user

In what module, and where/how is it called?

Thanks.

moradizx’s picture

how to CREATE new role programatically ? (based on email domain.)

for example i have these roles:

school_com for name@school.com
persianblog_com for name@persianblog.com
yahoo_ir for name@yahoo.ir

when a new user register, create new role if its not available:

new user email: meysam@noavar.ir
then create 'noavar_ir' role programatically

added:
noavar_ir for meysam@noavar.ir

(help me for create new role programatically)

All the best

patrickharris’s picture

and still no answer. It's often very frustrating when you try to dig deeper into Drupal - especially when you strike a problem like this, that would be simple to code yourself, if you were writing your own system. I can't wait for the "Pro Drupal" book due out in April. Maybe that will solve some of our problems.

alexis’s picture

The subscriptions code in the ecommerce module and the lm_paypal modify roles, take a look at the code of both.

Regards!

Alexis Bellido
Aprende a usar Drupal en menos de 48 horas

dwees’s picture

If you check out this feature request, you can find the modification I made to the logintobbogan module that does what you want here.

If you examine the code, you may have some of your specific questions answered.

Dave

Website: http://davidwees.com
Twitter: @davidwees

harro’s picture

I made a new post with a similar question (and used input from this thread, thanks!) here: http://drupal.org/node/139949

The difference is that I already use the login_destination module and didn't want to install yet another module, just to be able to change a user's role. I posted a working script, but it's ot pretty (and doesn't feel safe), with the request for some improvement hints.

So if you end up here with the question how to modify user roles 'programmatically' (nice word), check it out.

Bye,

Harro

spiritkarma’s picture

Hi,
I created a php file with this in it, then navigated ther with my browser, but it doesn't work!

"

<?php
function cache_clear_all('menu:'. $user->uid, TRUE);

"

I got his message :
"Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting '&' or T_VARIABLE in PATH/FILE.php on line 2"
PATH/FILE is of course the path on my server and the file's name.

My new admin accounts can't admin after i make new roles and assign them...
where and how do i execute this code?
please be very specific.. thank you so much!

[making my first drupal based community.]

dwees’s picture

If you use PhpMyAdmin or something similar, you can truncate (clear) the tables directly in your database. If you want to run a Drupal function, you don't need the function declaration before the function name. Ie, try

<?php
cache_clear_all
('menu', $user->uid, TRUE);
?>

Website: http://davidwees.com
Twitter: @davidwees

arindamghatak794’s picture

subscribing

www.MyResearchFunds.com
The World's Fastest Growing Network of Researchers

www.MyResearchFunds.com
The World's Fastest Growing Network of Researchers

B747’s picture

So, just to clear things up: am I right in saying that there is yet no way to let users choose their role registration? I think dwees has come pretty close with his patch for logintoboggan, but unfortunately his code doesn't work for Drupal 5...

Can anyone correct me - please say I'm wrong! Me and loads of other people would love this functionality.

heebie

SimonP’s picture

Hi

do you mean you're looking for what the Role Signup module does?

"Allows users to register and get assigned to a role. The user will go to /user/register/ and get redirected to a role select page. Once selected they are redirected to the register page."

Thanks

==============================
it's not hardware, it's not software, it's headware
==============================

==============================
it's not hardware, it's not software, it's headware
==============================

dwees’s picture

Apparently these two modules don't play nice with each other as they are modifying the same form.

Dave

Website: http://davidwees.com
Twitter: @davidwees

krisvannest’s picture

Sounds exactly what we need... unfortunately, our site is still on 4.7.x since not all of our other custom mods are 5.x compatible yet.

Anyone know if there's something like Rolesignup mod for 4.7.x sites?

dwees’s picture

I've created a patch for this functionality to the Logintoboggan module. See http://drupal.org/node/90983#comment-270094

Dave

Website: http://davidwees.com
Twitter: @davidwees

bob.hinrichs’s picture

user_multiple_role_edit(array($node->uid), 'add_role', 5);

RobLoach’s picture

In Drupal 5, be sure to clear the menu cache afterwards:

<?php
cache_clear_all
('*', 'cache_menu', TRUE);
?>
kalinchernev’s picture

Hi,

Where do you do this and how?

Thanks

winner’s picture

i think that this is a drupal bug.
find at user.module

// Save user roles (delete just to be safe).
    if (isset($array['roles']) && is_array($array['roles']) ))

modify it to

// Save user roles (delete just to be safe).
    if (isset($array['roles']) && is_array($array['roles']) && !empty($array['roles']))
wstein’s picture

I'm pretty sure this is not a bug... but rather this is here to close two security holes:
1) If there are any records hanging around in the users_roles table, then this cleans them up before a new user is added. This way, a new user doesn't accidentally get assigned to some random (or not so random, malicious attack) role via left-over records.
2) This prevents a visitor from arbitrarily adding records into the user registration form to assign themselves to any roles (like "administrator").

So, all new users are given an empty $array['roles'][] array, and are assigned to no roles.
Unless the admin is adding the user, in this case, the admin can assign roles, and the $array['roles'][] array is not so empty. (see user_register_submit ).

alienzed’s picture

I fixed this by hacking user.module around line 2320 where it calls

$account = user_save(...
I set my role in $merge_date and voila!

Hossam’s picture

Check above

wstein’s picture

It was SO tempting to hack user.module as others have done to make it easy to add roles during registration...

After all, you can create a custom form _validate function for your module, and adjust other fields in the $form_state array before it gets saved.

However, it seems that there is a potential security vulnerability if roles were allowed to be assigned via $form_state['values']['roles'], and so doing that is specifically disallowed in user_register_submit().

Finally, after studying the user.module functions user_register_submit and user_save, I discovered a way to fairly easily assign roles without breaking any security rules that I know of:

  • First, use the hook_form_alter to assign your module's validate function to the user_profile_form.
  • Next, in your validate function, determine what role a registering user should be assigned, and add a special value to the $form_state array that your own code will later interpret.
  • Finally, use hook_user('insert') to react to that special value and add the role to the $edit array.
  • Here it is again as code fragments, assuming your module is called "MyModule":

    <?php
    /**
     * Implementation of hook_form_alter
     */
    function MyModule_form_alter(&$form, $form_state, $form_id) {
      if (
    $form_id == 'user_profile_form' || $form_id == 'user_edit' || $form_id == 'user_register' ) {
       
    $form['#validate'][] = 'MyModule_profile_validate';
      }
    }
    function
    MyModule_profile_validate( $form, &$form_state ) {
      if (...
    this user should be auto-assigned to a role... ) {
       
    $role_name = 'role to auto-assign';
       
    $rid = MyModule_get_rid($role_name);
        if (!
    is_null($rid)) { // make sure we found the role
         
    $form_state['values']['MyModule_user role-to-add'] = array($rid => $role_name);
        };
      };
    }
    function
    MyModule_get_rid($role) {
     
    $result = db_query("SELECT rid FROM {role} WHERE name = '%s'",$role);
     
    $ret = NULL;
      if (
    $obj_role = db_fetch_object($result)) {
       
    $ret = $obj_role->rid;
      };
      return
    $ret;
    }
    /**
     * Implementation of hook_user('insert')
     */
    function MyModule_user( $op, &$edit, &$account, $category=NULL ) {
    /**
     * Implementation of hook_user('insert')
     */
     
    switch ($op) {
        case
    'insert':
         
    // The user account is being added.
          // The module should save its custom additions to the user object into the database
          //   and set the saved fields to NULL in $edit.

         
    if (isset($edit['MyModule_user role-to-add'])
              &&
    is_array($edit['MyModule_user role-to-add']) ) {
    // This would show you immediately that your code is working:
    //drupal_set_message("Automatically adding custom role!", 'status');
             
    $edit['roles'] = $edit['MyModule_user role-to-add'] + $edit['roles'];
             
    $edit['MyModule_user role-to-add'] = NULL;
          };
      };
    }
    ?>

    ... Initially, I used the $form_status['values'] / $edit array as described above. But, this might actually leave a security hole where, if a visitor knew your 'secret' field value, then they could assign themselves to an arbitrary role.

    So, it might be better if a value were used like $_SESSION['MyModule_user role-to-add'] instead.

    ron2x80’s picture

    check this module's implementation of hook_user().
    https://drupal.org/project/registration_role
    something like this

    <?php
      
    if ($op == "insert" ){
       
    $edit['roles'][$rid] = $roles[$rid];
      }
    ?>
    ThouArtJay’s picture

    I created a function to review the form role options in the registration form which takes two parameters: the role name you are looking for and the form where the options live. The function could use a little cleanup, but it works for now.

    <?php
    function get_role_array($role_name, $form)
    {
       
    $roles = array();

        foreach(
    $form['account']['roles']['#options'] as $key => $value) {
            if(
    $value == $role_name) {
               
    $roles[] = $key;
                break;
            }
        }

        return
    $roles;
    }
    ?>

    Example call and how to apply it to the form:

    <?php
    $roles
    = get_role_array('Content Editor', $form);
    $form['account']['roles']['#default_value'] = $roles;
    ?>

    $roles needs to be an array when applied back to the default value for the form.

    ThouArtJay

    narcoclepsy’s picture

    Hi I created a shorter hand method of adding roles with a homegrown function

    function morroni_add_role(&$user, $role_name) {
    $success = false;
    // jwatson load roles and get key of our desired role (if it exists)
    $roles = user_roles();
    $key = ($k = array_search($role_name, $roles)) > 0 ? $k : 0;

    if ($k > 0) {
    // add role to existing roles
    $user->roles[$key] = $role_name;
    // build array for user_save data
    $array = array('roles' => $user->roles);
    // save
    user_save($user, $array);
    $success = true;
    }
    return $success;
    }

    //usage
    $user = user_load(3);
    $result = (bool) morroni_add_role(&$user, 'role_name_goes_here');

    Jaypan’s picture

    You can write this line:

    <?php
    $result
    = (bool) morroni_add_role(&$user, 'role_name_goes_here');
    ?>

    Like this:

    <?php
    $result
    = morroni_add_role($user, 'role_name_goes_here');
    ?>

    You don't need to cast it as a boolean, since both your return statements specify a boolean, and passing values by reference (with the & sign) has been deprecated, and will throw warnings.

    I will soon be leaving the Drupal forums permanently. To understand why, please see this thread.

    StChat’s picture

    //promote myrole role

    $user = user_load($uid); // all you need here is user's uid
    $key = array_search('myrole', $user->roles);
    if ($key == FALSE) {
      $roles = user_roles(TRUE);
      $rid = array_search('myrole', $roles);
      if ($rid != FALSE) {
       $new_role[$rid] = 'myrole';
       user_save($user, array('roles' => $new_role));

      }
    }

    Works fine but if user already has a role then replaces it.
    How can I keep previous roles intact ?

    Vincenzo’s picture

    Initialise $new_role to $user->roles.

    StChat’s picture

    //promote myrole role

    $user = user_load($uid);
    $key = array_search('myrole', $user->roles);

    if ($key == FALSE) {
         $roles = user_roles(TRUE);
         $rid = array_search('myrole', $roles);
    if ($rid != FALSE) {
         $new_role = $user->roles;
         $new_role[$rid] = 'myrole';
         user_save($user, array('roles' => $new_role));
        }

    nodeAche’s picture

    Thanks stChat, I implemented your code like this:

    <?php
    function custom_user_insert(&$edit, $account, $category) {
     
     
    $user = user_load($account->uid);
     
    $key = array_search('Employer', $user->roles);

      if (
    $key == FALSE) {
          
    $roles = user_roles(TRUE);
          
    $rid = array_search('Employer', $roles);
      if (
    $rid != FALSE) {
          
    $new_role = $user->roles;
          
    $new_role[$rid] = 'Employer';
          
    user_save($user, array('roles' => $new_role));
          }
      }

    }
    ?>

    But I'm still wondering if it safe or not. Thanks

    Currently cooking -> stackoverflow clone with Drupal.
    check me at@ http://www.linkedin.com/pub/saad-lulu/4/346/4a4

    cmcintosh’s picture

    To do this with drupal 7, take a peak at how they did it in the install profile:

    <?php
    db_insert
    ('users_roles')
        ->
    fields(array('uid' => 1, 'rid' => $admin_role->rid))
        ->
    execute();
    ?>

    Replace your uid for the target user, and the rid for the target roll, rinse and repeat as needed.

    wing112707’s picture

    Works for me.

    I can do this through Christ

    ressa’s picture

    ...can be done with this:

    $uid = 123;// User ID of user that you want to add remove role from.
    $role_name = 'customer'; // The name of the role to remove.
    if ($role = user_role_load_by_name($role_name)) {
      user_multiple_role_edit(array($uid), 'remove_role', $role->rid);
    }

    Inspiration from this page: http://www.computerminds.co.uk/articles/quick-tips-adding-role-user-drup...
    Drupal API: http://api.drupal.org/api/drupal/modules!user!user.module/function/user_...

    everton.dallago’s picture

    Hi guys,

    This code worked for me:

    <?php
      $key
    = array_search('role name', $user->roles);
        if (!
    $key) {
           
    $roles = user_roles(TRUE);
           
    $rid = array_search('role name', $roles);
            if (
    $rid) {
               
    $user_roles = $user->roles;
                if (
    is_array($user_roles)) {
                   
    $user_roles [$rid] = 'role name';
                   
    user_save($user, array('roles' => $user_roles));
                }
            }
        }
    ?>

    You need insert new key after load all user roles, if you do it before you'll subscribe another roles.

    Thank's

    infoasistencia’s picture

    Not need modify bbdd for user roles changes?

    earnshae’s picture

    I realize this post is very old.

    But I was looking to do something similar and while searching I found it.

    This code is almost correct:

    $myuser = user_load($user_uid);
    $myuserroles = $myuser->roles[];
    $myuserroles[] = 'newrole';
    user_save($myuser, array('roles' => $myuserroles));

    It should look more like this.

    $myuser = user_load($user_uid);
    $myuserroles = $myuser->roles[$rid];
    $myuserroles[$rid] = 'newrole';
    $myuser->roles =$myuserroles;
    user_save($myuser, array('roles' => $myuserroles));

    Where $rid is the rid of new role in the roles table.

    Why? It appears that the array contains NULL values to form switches for permissions. Most-likely due to optimization.
    This may also work.

    $myuser = user_load($user_uid);
    $myuser->roles[$rid] = 'newrole';
    user_save($myuser);

    pocketfluff’s picture

    <?php
     
    $role_name
    = 'MY_ROLE_NAME'
    $roles_flipped = array_flip(user_roles());

    // add role.
    $account->roles[$roles_flipped[$role_name]] = $role_name;

    // remove role.
    unset($account->roles[$roles_flipped[$role_name]]);
    ?>

    Don't forget to user_save($account) !

    mrded’s picture

    How to add:

    <?php
      $role
    = user_role_load_by_name('Role-name');
     
    user_save($account, array('roles' => $account->roles + array($role->rid => $role->name)));
    ?>

    How to delete:

    <?php
      $role
    = user_role_load_by_name('Role-name');
     
    user_save($account, array('roles' => array_filter($account->roles, function($item) use ($role) {
        return
    $item != $role->name;
      })));
    ?>

    Mix:

    <?php
      $role
    = user_role_load_by_name('Role-name');

     
    $roles = $some_logic_here
       
    ? $account->roles + array($role->rid => $role->name)
        :
    array_filter($account->roles, function($role_name) use ($role) {
          return
    $role_name != $role->name;
        });

     
    user_save($account, array('roles' => $roles));
    ?>
    Tharick’s picture

    I think Rules module can help you lot, please have a look on it https://www.drupal.org/project/rules.