annotate core/modules/user/src/Plugin/EntityReferenceSelection/UserSelection.php @ 19:fa3358dc1485 tip

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