annotate core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php @ 15:e200cb7efeb3

Update Drupal core to 8.5.3 via Composer
author Chris Cannam
date Thu, 26 Apr 2018 11:26:54 +0100
parents 4c8ae668cc8c
children 129ea1e6d783
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\user\Plugin\EntityReferenceSelection;
Chris@0 4
Chris@0 5 use Drupal\Core\Database\Connection;
Chris@0 6 use Drupal\Core\Database\Query\Condition;
Chris@0 7 use Drupal\Core\Database\Query\SelectInterface;
Chris@0 8 use Drupal\Core\Entity\EntityManagerInterface;
Chris@0 9 use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
Chris@0 10 use Drupal\Core\Extension\ModuleHandlerInterface;
Chris@0 11 use Drupal\Core\Form\FormStateInterface;
Chris@0 12 use Drupal\Core\Session\AccountInterface;
Chris@0 13 use Drupal\user\RoleInterface;
Chris@0 14 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@0 15
Chris@0 16 /**
Chris@0 17 * Provides specific access control for the user entity type.
Chris@0 18 *
Chris@0 19 * @EntityReferenceSelection(
Chris@0 20 * id = "default:user",
Chris@0 21 * label = @Translation("User selection"),
Chris@0 22 * entity_types = {"user"},
Chris@0 23 * group = "default",
Chris@0 24 * weight = 1
Chris@0 25 * )
Chris@0 26 */
Chris@0 27 class UserSelection extends DefaultSelection {
Chris@0 28
Chris@0 29 /**
Chris@0 30 * The database connection.
Chris@0 31 *
Chris@0 32 * @var \Drupal\Core\Database\Connection
Chris@0 33 */
Chris@0 34 protected $connection;
Chris@0 35
Chris@0 36 /**
Chris@0 37 * The user storage.
Chris@0 38 *
Chris@0 39 * @var \Drupal\user\UserStorageInterface
Chris@0 40 */
Chris@0 41 protected $userStorage;
Chris@0 42
Chris@0 43 /**
Chris@0 44 * Constructs a new UserSelection object.
Chris@0 45 *
Chris@0 46 * @param array $configuration
Chris@0 47 * A configuration array containing information about the plugin instance.
Chris@0 48 * @param string $plugin_id
Chris@0 49 * The plugin_id for the plugin instance.
Chris@0 50 * @param mixed $plugin_definition
Chris@0 51 * The plugin implementation definition.
Chris@0 52 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
Chris@0 53 * The entity manager service.
Chris@0 54 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
Chris@0 55 * The module handler service.
Chris@0 56 * @param \Drupal\Core\Session\AccountInterface $current_user
Chris@0 57 * The current user.
Chris@0 58 * @param \Drupal\Core\Database\Connection $connection
Chris@0 59 * The database connection.
Chris@0 60 */
Chris@0 61 public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user, Connection $connection) {
Chris@0 62 parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager, $module_handler, $current_user);
Chris@0 63
Chris@0 64 $this->connection = $connection;
Chris@0 65 $this->userStorage = $entity_manager->getStorage('user');
Chris@0 66 }
Chris@0 67
Chris@0 68 /**
Chris@0 69 * {@inheritdoc}
Chris@0 70 */
Chris@0 71 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
Chris@0 72 return new static(
Chris@0 73 $configuration,
Chris@0 74 $plugin_id,
Chris@0 75 $plugin_definition,
Chris@0 76 $container->get('entity.manager'),
Chris@0 77 $container->get('module_handler'),
Chris@0 78 $container->get('current_user'),
Chris@0 79 $container->get('database')
Chris@0 80 );
Chris@0 81 }
Chris@0 82
Chris@0 83 /**
Chris@0 84 * {@inheritdoc}
Chris@0 85 */
Chris@0 86 public function defaultConfiguration() {
Chris@0 87 return [
Chris@0 88 'filter' => [
Chris@0 89 'type' => '_none',
Chris@0 90 'role' => NULL,
Chris@0 91 ],
Chris@0 92 'include_anonymous' => TRUE,
Chris@0 93 ] + parent::defaultConfiguration();
Chris@0 94 }
Chris@0 95
Chris@0 96 /**
Chris@0 97 * {@inheritdoc}
Chris@0 98 */
Chris@0 99 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
Chris@0 100 $configuration = $this->getConfiguration();
Chris@0 101
Chris@0 102 $form['include_anonymous'] = [
Chris@0 103 '#type' => 'checkbox',
Chris@0 104 '#title' => $this->t('Include the anonymous user.'),
Chris@0 105 '#default_value' => $configuration['include_anonymous'],
Chris@0 106 ];
Chris@0 107
Chris@0 108 // Add user specific filter options.
Chris@0 109 $form['filter']['type'] = [
Chris@0 110 '#type' => 'select',
Chris@0 111 '#title' => $this->t('Filter by'),
Chris@0 112 '#options' => [
Chris@0 113 '_none' => $this->t('- None -'),
Chris@0 114 'role' => $this->t('User role'),
Chris@0 115 ],
Chris@0 116 '#ajax' => TRUE,
Chris@0 117 '#limit_validation_errors' => [],
Chris@0 118 '#default_value' => $configuration['filter']['type'],
Chris@0 119 ];
Chris@0 120
Chris@0 121 $form['filter']['settings'] = [
Chris@0 122 '#type' => 'container',
Chris@0 123 '#attributes' => ['class' => ['entity_reference-settings']],
Chris@0 124 '#process' => [['\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', 'formProcessMergeParent']],
Chris@0 125 ];
Chris@0 126
Chris@0 127 if ($configuration['filter']['type'] == 'role') {
Chris@0 128 $form['filter']['settings']['role'] = [
Chris@0 129 '#type' => 'checkboxes',
Chris@0 130 '#title' => $this->t('Restrict to the selected roles'),
Chris@0 131 '#required' => TRUE,
Chris@0 132 '#options' => array_diff_key(user_role_names(TRUE), [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]),
Chris@0 133 '#default_value' => $configuration['filter']['role'],
Chris@0 134 ];
Chris@0 135 }
Chris@0 136
Chris@0 137 $form += parent::buildConfigurationForm($form, $form_state);
Chris@0 138
Chris@0 139 return $form;
Chris@0 140 }
Chris@0 141
Chris@0 142 /**
Chris@0 143 * {@inheritdoc}
Chris@0 144 */
Chris@0 145 protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
Chris@0 146 $query = parent::buildEntityQuery($match, $match_operator);
Chris@0 147
Chris@0 148 $configuration = $this->getConfiguration();
Chris@0 149
Chris@0 150 // Filter out the Anonymous user if the selection handler is configured to
Chris@0 151 // exclude it.
Chris@0 152 if (!$configuration['include_anonymous']) {
Chris@0 153 $query->condition('uid', 0, '<>');
Chris@0 154 }
Chris@0 155
Chris@0 156 // The user entity doesn't have a label column.
Chris@0 157 if (isset($match)) {
Chris@0 158 $query->condition('name', $match, $match_operator);
Chris@0 159 }
Chris@0 160
Chris@0 161 // Filter by role.
Chris@0 162 if (!empty($configuration['filter']['role'])) {
Chris@0 163 $query->condition('roles', $configuration['filter']['role'], 'IN');
Chris@0 164 }
Chris@0 165
Chris@0 166 // Adding the permission check is sadly insufficient for users: core
Chris@0 167 // requires us to also know about the concept of 'blocked' and 'active'.
Chris@0 168 if (!$this->currentUser->hasPermission('administer users')) {
Chris@0 169 $query->condition('status', 1);
Chris@0 170 }
Chris@0 171 return $query;
Chris@0 172 }
Chris@0 173
Chris@0 174 /**
Chris@0 175 * {@inheritdoc}
Chris@0 176 */
Chris@0 177 public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
Chris@0 178 $user = parent::createNewEntity($entity_type_id, $bundle, $label, $uid);
Chris@0 179
Chris@0 180 // In order to create a referenceable user, it needs to be active.
Chris@0 181 if (!$this->currentUser->hasPermission('administer users')) {
Chris@0 182 /** @var \Drupal\user\UserInterface $user */
Chris@0 183 $user->activate();
Chris@0 184 }
Chris@0 185
Chris@0 186 return $user;
Chris@0 187 }
Chris@0 188
Chris@0 189 /**
Chris@0 190 * {@inheritdoc}
Chris@0 191 */
Chris@0 192 public function validateReferenceableNewEntities(array $entities) {
Chris@0 193 $entities = parent::validateReferenceableNewEntities($entities);
Chris@0 194 // Mirror the conditions checked in buildEntityQuery().
Chris@0 195 if ($role = $this->getConfiguration()['filter']['role']) {
Chris@0 196 $entities = array_filter($entities, function ($user) use ($role) {
Chris@0 197 /** @var \Drupal\user\UserInterface $user */
Chris@0 198 return !empty(array_intersect($user->getRoles(), $role));
Chris@0 199 });
Chris@0 200 }
Chris@0 201 if (!$this->currentUser->hasPermission('administer users')) {
Chris@0 202 $entities = array_filter($entities, function ($user) {
Chris@0 203 /** @var \Drupal\user\UserInterface $user */
Chris@0 204 return $user->isActive();
Chris@0 205 });
Chris@0 206 }
Chris@0 207 return $entities;
Chris@0 208 }
Chris@0 209
Chris@0 210 /**
Chris@0 211 * {@inheritdoc}
Chris@0 212 */
Chris@0 213 public function entityQueryAlter(SelectInterface $query) {
Chris@0 214 parent::entityQueryAlter($query);
Chris@0 215
Chris@0 216 // Bail out early if we do not need to match the Anonymous user.
Chris@0 217 if (!$this->getConfiguration()['include_anonymous']) {
Chris@0 218 return;
Chris@0 219 }
Chris@0 220
Chris@0 221 if ($this->currentUser->hasPermission('administer users')) {
Chris@0 222 // In addition, if the user is administrator, we need to make sure to
Chris@0 223 // match the anonymous user, that doesn't actually have a name in the
Chris@0 224 // database.
Chris@0 225 $conditions = &$query->conditions();
Chris@0 226 foreach ($conditions as $key => $condition) {
Chris@0 227 if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users_field_data.name') {
Chris@0 228 // Remove the condition.
Chris@0 229 unset($conditions[$key]);
Chris@0 230
Chris@0 231 // Re-add the condition and a condition on uid = 0 so that we end up
Chris@0 232 // with a query in the form:
Chris@0 233 // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0)
Chris@0 234 $or = new Condition('OR');
Chris@0 235 $or->condition($condition['field'], $condition['value'], $condition['operator']);
Chris@0 236 // Sadly, the Database layer doesn't allow us to build a condition
Chris@0 237 // in the form ':placeholder = :placeholder2', because the 'field'
Chris@0 238 // part of a condition is always escaped.
Chris@0 239 // As a (cheap) workaround, we separately build a condition with no
Chris@0 240 // field, and concatenate the field and the condition separately.
Chris@0 241 $value_part = new Condition('AND');
Chris@0 242 $value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
Chris@0 243 $value_part->compile($this->connection, $query);
Chris@0 244 $or->condition((new Condition('AND'))
Chris@0 245 ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + [':anonymous_name' => \Drupal::config('user.settings')->get('anonymous')])
Chris@0 246 ->condition('base_table.uid', 0)
Chris@0 247 );
Chris@0 248 $query->condition($or);
Chris@0 249 }
Chris@0 250 }
Chris@0 251 }
Chris@0 252 }
Chris@0 253
Chris@0 254 }