I noticed that users other than the admin can't add blocked users to a given user-specific entity reference field. I'm not sure why this is, but I went ahead and removed that condition from the validation check query in EntityReference_SelectionHandler_Generic::validateReferencableEntities() from EntityReference_SelectionHandler_Generic.class.php.

/**
   * Implements EntityReferenceHandler::validateReferencableEntities().
   */
  public function validateReferencableEntities(array $ids) {

    if ($ids) {
      $entity_type = $this->field['settings']['target_type'];
      $query = $this->buildEntityFieldQuery();
      $query->entityCondition('entity_id', $ids, 'IN');
      if ($entity_type == "user") {unset($query->propertyConditions);}  // new code
      $result = $query->execute();
      if (!empty($result[$entity_type])) {
        return array_keys($result[$entity_type]);
      }
    }

    return array();
  }

I guess I'm just looking for any comments anyone might have.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

cdmo’s picture

I also had to change the following code:

/**
 * Implements hook_field_formatter_prepare_view().
 */
function node_view_perm_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
  $target_ids = array();

  // Collect every possible entity attached to any of the entities.
  foreach ($entities as $id => $entity) {
    foreach ($items[$id] as $delta => $item) {
      if (isset($item['target_id'])) {
        $target_ids[] = $item['target_id'];
      }
    }
  }

  if ($target_ids) {
    $target_entities = entity_load($field['settings']['target_type'], $target_ids);
  }
  else {
    $target_entities = array();
  }

  // Iterate through the fieldable entities again to attach the loaded data.
  foreach ($entities as $id => $entity) {
    $rekey = FALSE;

    foreach ($items[$id] as $delta => $item) {
      // Check whether the referenced entity could be loaded.
      if (isset($target_entities[$item['target_id']])) {
        // Replace the instance value with the term data.
        $items[$id][$delta]['entity'] = $target_entities[$item['target_id']];
        // Check whether the user has access to the referenced entity.
        $has_view_access = (entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE);
        if (isset($items[$id][$delta]['entity']->uid)) {  //new code
          $has_view_access = TRUE;  //new code
        }  //new code
        $has_update_access = (entity_access('update', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE);
        $items[$id][$delta]['access'] = ($has_view_access || $has_update_access);
      }
      // Otherwise, unset the instance value, since the entity does not exist.
      else {
        unset($items[$id][$delta]);
        $rekey = TRUE;
      }
    }

    if ($rekey) {
      // Rekey the items array.
      $items[$id] = array_values($items[$id]);
    }
  }
}

This works, but it's hacky.

cdmo’s picture

Status: Active » Needs review
FileSize
2.9 KB

Here's a patch that takes care of it all, I welcome any feedback. Thanks.

cdmo’s picture

Removing some errant code in the patch that git diff picked up for some reason

The last submitted patch, 2: entityreference-allow_blocked_users-2329979-2.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 3: entityreference-allow_blocked_users-2329979-3.patch, failed testing.

lucas.constantino’s picture

Hello, cdmo

I found an unobtrusive way to handle access to blocked users only implementing some specific hooks:


/**
 * Implements hook_module_implements_alter().
 */
function MYMODULE_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'entity_info_alter') {
    // Move our hook implementation to the bottom.
    $group = $implementations['MYMODULE'];
    unset($implementations['MYMODULE']);
    $implementations['MYMODULE'] = $group;
  }
}

/**
 * Implements hook_entity_info_alter(&$entities);
 */
function MYMODULE_entity_info_alter(&$entities) {
  $entities['user']['access callback'] = 'MYMODULE_metadata_user_access';
}
/**
 * Implements hook_query_alter().
 * Alter entity queries to always allow access to bloqued users.
 */
function MYMODULE_query_alter(&$query) {
  if ($query->alterTags && !empty($query->alterTags['entityreference']) && isset($query->alterTags['user_access'])) {
    unset($query->propertyCondition);
    $query->entityCondition('status', 0, '>=');
  }
}

/**
 * Access callback to user entities.
 */
function MYMODULE_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type) {
  $access = entity_metadata_user_access($op, $entity, $account, $entity_type);
  if (!$access && $op == 'view' && user_access('access user profiles', $account)) {
    $access = true;
  }
  return $access;
}

Hope it helps someone.

cdmo’s picture

Thanks lucas.constantino! I actually don't work for the group where I originally had this development need, but I forwarded your solution on to them. Thanks for the work and for sharing.

gbirch’s picture

Thanks @lucas.constantino! Helped me a LOT! I know this is an old thread, but one question: why didn't you add a user_access() check to the query_alter function?

MustangGB’s picture

Status: Needs work » Fixed
gbirch’s picture

Sorry, why is this marked "Fixed"? I can't see any changes to the code that would solve this issue.

MustangGB’s picture

@gbirch The answer given in #7 seems like good enough info to answer this Support request. If you feel like this is something that would be worthwhile adding to the module, please feel free to re-open, presumably as a Feature request.

gbirch’s picture

@MustangGB Thanks for clarification. Just to prevent future users from being confused, shouldn't this be "Won't fix" then, or "Postponed"?

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Majk07’s picture

Update patch to work with 7.15

Majk07’s picture

Forgot to change the relatief path, this has to work

Majk07’s picture

angelamnr’s picture

#16 works for me.

pawel.traczynski’s picture

I also needed similar thing in my company where blocked user accounts were employees who don't work anymore but they had to still appear in the system (for example an employee was doing something before they left and now after they are gone, current staff still needs to see these blocked accounts everywhere where they were refferred to).

Here is my complete solution to this problem. This could probably be wrapped as a complete mini-module.


/*
 * The following hooks allow users with 'access user profiles' permission
 * to view blocked user profiles.
 *
 * This is needed so that any pages showing user data keep showing data
 * related to accounts that have been blocked, so that other users can
 * see them.
 *
 * Accounts might blocked when users are being fired from the company, but
 * their names should still show up where they were before they got blocked.
 *
 * There are 3 sets of hooks where these permissions are being altered:
 *
 * 1. Permission to view user profile pages uses:
 *    - MODULENAME_menu_alter()
 *    - MODULENAME_user_view_access()
 *    - MODULENAME_file_download_access().
 *
 * 2. Permission to view referred by entityreference user profile details uses:
 *    - MODULENAME_entity_info_alter()
 *    - MODULENAME_module_implements_alter()
 *    - MODULENAME_metadata_user_access().
 *
 * 3. Permission to view user details when editing entityreference form field:
 *    - MODULENAME_query_alter().
 */



/**
 * Implements hook_menu_alter().
 */
function MODULENAME_menu_alter(&$items) {
  // Set our custom user view page access callback.
  // This allows blocked user account view pages to be viewable just the same
  // way as active user accounts are.
  $items['user/%user']['access callback'] = 'MODULENAME_user_view_access';
}



/**
 * User account page access callback based on original user_view_access().
 *
 * @param $account
 *   Can either be a full user object or a $uid.
 *
 * @return bool
 *   TRUE if user is granted access to view user account, FALSE otherwise.
 */
function MODULENAME_user_view_access($account) {
  $access = user_view_access($account);

  // Ignore if trying to view the anonymous user account.
  $uid = is_object($account) ? $account->uid : (int) $account;

  // Additionally to drupal core, grant access to view blocked user accounts
  // to any user with the 'access user profiles' permission.
  if ($uid && user_access('access user profiles')) {
    if (!is_object($account)) {
      $account = user_load($uid);
    }

    $access = is_object($account);
  }

  return $access;
}



/**
 * Implements hook_file_download_access().
 *
 * This covers checking access for private files uploaded to user accounts.
 */
function MODULENAME_file_download_access($field, $entity_type, $entity) {
  if ($entity_type == 'user') {
    return MODULENAME_user_view_access($entity);
  }
}



/**
 * Implements hook_entity_info_alter().
 */
function MODULENAME_entity_info_alter(&$entities) {
  // This is our custom user entity access callback, which is used when
  // printing user details pulled in using entity API (e.g. when the
  // 'entityreference' module is used to pull user data into views which
  // primarily shows nodes).
  $entities['user']['access callback'] = 'MODULENAME_metadata_user_access';
}



/**
 * Implements hook_module_implements_alter().
 */
function MODULENAME_module_implements_alter(&$implementations, $hook) {
  if ($hook != 'entity_info_alter') {
    return;
  }

  // Move our hook_entity_info_alter() implementation to the bottom.
  $MODULENAME = $implementations['MODULENAME'];
  unset($implementations['MODULENAME']);
  $implementations['MODULENAME'] = $MODULENAME;
}



/**
 * Custom access callback for the user entity metadata.
 *
 * Grants access to blocked accounts metadata for users with
 * 'access user profiles' permission.
 */
function MODULENAME_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
  // First call default implementation from Entity API.
  $access = entity_metadata_user_access($op, $entity, $account, $entity_type);

  // Additional custom grants.
  if (!$access && $op == 'view' && user_access('access user profiles', $account)) {
    $access = TRUE;
  }

  return $access;
}



/**
 * Implements hook_query_alter().
 *
 * Alter entity queries to allow access to blocked users for users with
 * 'access user profiles' permission.
 *
 * https://www.drupal.org/node/310077.
 */
function MODULENAME_query_alter(&$query) {
  // If the query is a entityreference query for listing users.
  if ($query->hasAllTags('entityreference', 'user_access') && user_access('access user profiles')) {
    $conditions =& $query->conditions();

    // Remove the condition that filters out blocked accounts.
    foreach ($conditions as $key => $condition) {
      if (isset($condition['field']) && isset($condition['value'])) {
        if ($condition['field'] == 'users.status' && $condition['value'] == 1) {
          unset($conditions[$key]);
        }
      }
    }
  }
}