3

It seems that User name on blocked user account not visible in view for anonymous users is basically the same question I'm asking, but it doesn't have an accepted answer or one that works for me. This is a different take on the question than the one I'm making.

I am migrating a site from another CMS. I am bringing over staff members, both past and current as users of the site. As part of the migration, users who are no longer with the organization are being marked as blocked to prevent them from logging into the site.

These users are then associated with nodes on the site via various entity reference fields. The problem I run into is that for anonymous users, indeed for anyone that doesn't have bypass access controls on their account, these user accounts are not visible.

What is the proper way to keep an account visible, while not allowing users to log-in to it? For me, the answer will need to be something that will work with the Migrate Module.

Obviously blocking the user seemed like the obvious way to do it for me, but I can't find any permission that will allow access to blocked user info. Is there another way of doing this, or is there a way of making blocked account info visible?

My Solution

Based on the accepted answer from @kiamlaluno, I created a solution that works for me:

I already have an end_date field for each user (field_user_end_date), so most of the time, that would suffice as a limiting factor for the login, but based on discussions with the stakeholders, I added an additional admin only boolean field named field_user_account_locked.

I implement hook_form_alter to add my validation function to the form:

function mymodule_form_alter(&$form, &$form_state, $form_id)
{
    if ($form_id == 'user_login' || $form_id == 'user_login_block') {
        $form['#validate'][] = 'mymodule_user_login_validate';
    }
}

And here is my validation module:

function mymodule_user_login_validate($form, &$form_state)
{
    if (!empty($form_state['uid'])) {
        // The user trying to login is not limited by flood control;
        // user_login_authenticate_validate() found a user with the username and password entered in the login form.
        $user = user_load($form_state['uid']);
        $end_date = (isset($user->field_user_end_date[LANGUAGE_NONE][0]['value'])) ? strtotime($user->field_user_end_date[LANGUAGE_NONE][0]['value']) : time() + 300000;
        if ($end_date < time() || $user->field_user_account_locked[LANGUAGE_NONE][0]['value'] == 1) {
            form_set_error('name', t('The username %name cannot be used to login.', array('%name' => $form_state['values']['name'])));
        }
    }
}

I'd love some constructive criticism on it, if you have some.

  • 1
    Rather than blocking them, you could just reset/change their password, so they won't be able to login. – No Sssweat Mar 31 '16 at 8:58
  • 2
    You'd have to reset their email too though @NoSssweat, otherwise they can just request a reset password link – Clive Mar 31 '16 at 8:58
  • @Clive right, forgot about that, hehe. – No Sssweat Mar 31 '16 at 8:59
  • I guess setting some randomly generated e-mail address and password would be one way to do it, but if possible I'd like for no one to be able to log into that account, and for the account to still be visible. I keep running into things that I'm sure people run into all the time, but that drupal seems not to have an easy way to do. It is really surprising. – UltraBob Mar 31 '16 at 9:00
2

Using a custom module, you could change the permission to see user profiles. For example, the following code could be used for Drupal 7.

function mymodule_permission() {
  return array(
    'access blocked user profiles' => array(
      'title' => t('View user profiles of blocked users'),
    ),
  );
}

function mymodule_menu_alter(&$items) {
  if (isset($items['user/%user'])) {
    $items['user/%user']['access callback'] = 'mymodule_view_access';
  }
}

function mymodule_view_access($account) {
  $uid = is_object($account) ? $account->uid : (int) $account;

  // Never allow access to view the anonymous user account.
  if ($uid) {
    // Admins can view all, users can view own profiles at all times.
    if ($GLOBALS['user']->uid == $uid || user_access('administer users')) {
      return TRUE;
    }
    elseif (user_access('access user profiles')) {
      // At this point, load the complete account object.
      if (!is_object($account)) {
        $account = user_load($uid);
      }
      if (is_object($account)) {
        // If the account is blocked, show the profile to users with the right
        // permission.
        return ($account->status ? TRUE : user_access('access blocked user profiles'));
      }
    }
  }
  return FALSE;
}

Give the new permission to anonymous users, and they will be able to see the user profile of blocked users.

This is probably easier than changing password and email, and it's easier to revert if you block by mistake a current employee: Just unblock the account.

The code I shown works for a direct access to the user profile, and for modules that only verify the user has the access user profile permission to show the link to a user profile. This is what Drupal does, for example in template_preprocess_username().

  $variables['profile_access'] = user_access('access user profiles');
  $variables['link_attributes'] = array();
  // Populate link path and attributes if appropriate.
  if ($variables['uid'] && $variables['profile_access']) {
    // We are linking to a local user.
    $variables['link_attributes'] = array('title' => t('View user profile.'));
    $variables['link_path'] = 'user/' . $variables['uid'];
  }
  elseif (!empty($account->homepage)) {
    // Like the 'class' attribute, the 'rel' attribute can hold a
    // space-separated set of values, so initialize it as an array to make it
    // easier for other preprocess functions to append to it.
    $variables['link_attributes'] = array('rel' => array('nofollow'));
    $variables['link_path'] = $account->homepage;
    $variables['homepage'] = $account->homepage;
  }

Any module using theme('username') (for which template_preprocess_username is a preprocess function) to show the user name will print the link to the user profile, even if the shown user account is blocked, if the currently logged-in user has the access user profile permission.

If there are modules that check the user account is blocked before showing a link to the user profile, then this method will not work.

In this case, the alternative I would adopt is the following:

  • Don't block the user accounts for those ex employees
  • Add a flag to the user accounts that says if an account can be used to log in
  • Using an extra validation handler for the login form, return an error if one of those accounts is used to log in
function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'user_login' || $form_id == 'user_login_block') {
    $form['#validate'][] = 'mymodule_user_login_validate';
  }
  elseif ($form_id == 'user_register_form') {
    $form['account']['can_login'] = array(
      '#type' => 'checkbox',
      '#title' => t('The user account can be used to log in'),
      // The form is for registering a new user; there is nothing to load from the database.
      '#default_value' => TRUE,
      // See user_register_form() which set the following value to TRUE if
      // the currently logged-in user administer users.
      '#access' => $form['administer_users']['#value'],
    );
    $form['#submit'][] = 'mymodule_register_submit';
  }
  elseif ($form_id == 'user_profile_form') {
    if ($form['#user_category'] == 'account') {
      $form['account']['can_login'] = array(
        '#type' => 'checkbox',
        '#title' => t('The user account can be used to log in'),
        '#default_value' => mymodule_load_flag($form['#user']->uid),
        '#access' => user_access('administer users'),
      );
      $form['#submit'][] = 'mymodule_profile_submit';
    }
  }
}

function mymodule_user_login_validate($form, &$form_state) {
  if (!empty($form_state['uid'])) {
    // The user trying to login is not limited by flood control;
    // user_login_authenticate_validate() found a user with the username and password entered in the login form.
    if (!mymodule_load_flag($form_state['uid'])) {
      form_set_error('name', t('The username %name cannot be used to login.', array('%name' => $form_state['values']['name'])));
    }
  }
}

function mymodule_register_submit($form, &$form_state) {
  if ($form_state['values']['administer_users']) {
    // The user submitting the form can administer users. 
    // user_register_submit() sets $form_state['values']['uid']; save it in
    // a database table the module creates when installed.
    mymodule_save_flag($form_state['values']['uid'], $form_state['values']['can_login']);
  }
}

function mymodule_profile_submit($form, &$form_state) {
  if (user_access('administer users') && $form['#user_category'] == 'account') {
    // user_profile_submit() sets $form_state['values']['uid']; save it in
    // a database table the module creates when installed.
    mymodule_save_flag($form_state['values']['uid'], $form_state['values']['can_login']);
  }
}
  • This is exactly what I'm looking for, but it unfortunately doesn't seem to work. The permission block does pop up, but when I check the boxes for everyone, those names still don't show up on the page and I get an access denied error when trying to access the user profile page as an anonymous user. – UltraBob Apr 1 '16 at 5:21
  • Probably there is something that explicitly checks the account is blocked, and I understand other modules don't know the new permission. It's still a matter of changing other access callbacks, or implement other alter hooks. – kiamlaluno Apr 1 '16 at 6:56
  • I don't know enough about Drupal development yet to be able to act on this advice. Can you be a little more explicit? – UltraBob Apr 1 '16 at 6:58
  • I will add a different solution; it would allow you to still have blocked users (if you need to use them as drupal.org does), and avoid some users login (without changing passwords or email addresses). – kiamlaluno Apr 1 '16 at 7:03
  • Great! Thank you! I have been scanning the api for a hook that would enable me to alter permissions for user profiles, but I'm not finding one. I assume that is what I would need to do with this solution right -- implement some kind of hook_user_access_alter function to check for the new permission that has been defined? – UltraBob Apr 1 '16 at 7:07
1

Show all the users in a View. Views is capable of showing all data to everybody (so you will have to be careful).

When you create a view that lists users, you have an option to filter by status. If you omit that filter, you will list every user in the system blocked and unblocked. If it is still not working, change the default 'View user profiles' permission into something else.

  1. Install and enable the Views module.
  2. Create a View that lists "Users".
  3. Add additional filters.
  4. Check permissions.
  • Thank you. These users are being attached to nodes via paragraphs module and within that by entity reference. It seems like putting everything through views and overriding default user view permissions for every view would get quite complicated quite quickly. – UltraBob Mar 31 '16 at 9:14
  • Views has excellent support for entity references, so that should not cause any problems (relations are in the advanced sections). And you only would need to change the view permissions (perhaps not even) on one View, which is possible from within the Views UI. It is really all in one place and not as difficult as you might think :) – Neograph734 Mar 31 '16 at 9:16
  • What I was trying to say is that my problems are not arising on user lists, but rather on views that need to include users. As an example I currently have a node view that is set to show full content. This is showing a display suite configured look at the node, but when viewed as an anonymous user it doesn't show the blocked users. How would one modify a view like that to make it show the blocked users? – UltraBob Mar 31 '16 at 9:25
  • It depends quite a lot on the situation, Display Suite for instance offers two Views display modes. but in general; Add the entity relations as references. Then either add some user fields or the rendered entity field (this last might not work because it uses Entity API access logic not only Views'). Then add those fields to your DS configuration.. – Neograph734 Mar 31 '16 at 9:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.