Chris@0: connection = $connection; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { Chris@0: return new static( Chris@0: $configuration, Chris@0: $plugin_id, Chris@0: $plugin_definition, Chris@0: $container->get('entity.manager'), Chris@0: $container->get('module_handler'), Chris@0: $container->get('current_user'), Chris@18: $container->get('database'), Chris@18: $container->get('entity_field.manager'), Chris@18: $container->get('entity_type.bundle.info'), Chris@18: $container->get('entity.repository') Chris@0: ); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function defaultConfiguration() { Chris@0: return [ Chris@0: 'filter' => [ Chris@0: 'type' => '_none', Chris@0: 'role' => NULL, Chris@0: ], Chris@0: 'include_anonymous' => TRUE, Chris@0: ] + parent::defaultConfiguration(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function buildConfigurationForm(array $form, FormStateInterface $form_state) { Chris@0: $configuration = $this->getConfiguration(); Chris@0: Chris@0: $form['include_anonymous'] = [ Chris@0: '#type' => 'checkbox', Chris@0: '#title' => $this->t('Include the anonymous user.'), Chris@0: '#default_value' => $configuration['include_anonymous'], Chris@0: ]; Chris@0: Chris@0: // Add user specific filter options. Chris@0: $form['filter']['type'] = [ Chris@0: '#type' => 'select', Chris@0: '#title' => $this->t('Filter by'), Chris@0: '#options' => [ Chris@0: '_none' => $this->t('- None -'), Chris@0: 'role' => $this->t('User role'), Chris@0: ], Chris@0: '#ajax' => TRUE, Chris@0: '#limit_validation_errors' => [], Chris@0: '#default_value' => $configuration['filter']['type'], Chris@0: ]; Chris@0: Chris@0: $form['filter']['settings'] = [ Chris@0: '#type' => 'container', Chris@0: '#attributes' => ['class' => ['entity_reference-settings']], Chris@0: '#process' => [['\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', 'formProcessMergeParent']], Chris@0: ]; Chris@0: Chris@0: if ($configuration['filter']['type'] == 'role') { Chris@0: $form['filter']['settings']['role'] = [ Chris@0: '#type' => 'checkboxes', Chris@0: '#title' => $this->t('Restrict to the selected roles'), Chris@0: '#required' => TRUE, Chris@0: '#options' => array_diff_key(user_role_names(TRUE), [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]), Chris@0: '#default_value' => $configuration['filter']['role'], Chris@0: ]; Chris@0: } Chris@0: Chris@0: $form += parent::buildConfigurationForm($form, $form_state); Chris@0: Chris@0: return $form; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') { Chris@0: $query = parent::buildEntityQuery($match, $match_operator); Chris@0: Chris@0: $configuration = $this->getConfiguration(); Chris@0: Chris@0: // Filter out the Anonymous user if the selection handler is configured to Chris@0: // exclude it. Chris@0: if (!$configuration['include_anonymous']) { Chris@0: $query->condition('uid', 0, '<>'); Chris@0: } Chris@0: Chris@0: // The user entity doesn't have a label column. Chris@0: if (isset($match)) { Chris@0: $query->condition('name', $match, $match_operator); Chris@0: } Chris@0: Chris@0: // Filter by role. Chris@0: if (!empty($configuration['filter']['role'])) { Chris@0: $query->condition('roles', $configuration['filter']['role'], 'IN'); Chris@0: } Chris@0: Chris@0: // Adding the permission check is sadly insufficient for users: core Chris@0: // requires us to also know about the concept of 'blocked' and 'active'. Chris@0: if (!$this->currentUser->hasPermission('administer users')) { Chris@0: $query->condition('status', 1); Chris@0: } Chris@0: return $query; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function createNewEntity($entity_type_id, $bundle, $label, $uid) { Chris@0: $user = parent::createNewEntity($entity_type_id, $bundle, $label, $uid); Chris@0: Chris@0: // In order to create a referenceable user, it needs to be active. Chris@0: if (!$this->currentUser->hasPermission('administer users')) { Chris@0: /** @var \Drupal\user\UserInterface $user */ Chris@0: $user->activate(); Chris@0: } Chris@0: Chris@0: return $user; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function validateReferenceableNewEntities(array $entities) { Chris@0: $entities = parent::validateReferenceableNewEntities($entities); Chris@0: // Mirror the conditions checked in buildEntityQuery(). Chris@0: if ($role = $this->getConfiguration()['filter']['role']) { Chris@0: $entities = array_filter($entities, function ($user) use ($role) { Chris@0: /** @var \Drupal\user\UserInterface $user */ Chris@0: return !empty(array_intersect($user->getRoles(), $role)); Chris@0: }); Chris@0: } Chris@0: if (!$this->currentUser->hasPermission('administer users')) { Chris@0: $entities = array_filter($entities, function ($user) { Chris@0: /** @var \Drupal\user\UserInterface $user */ Chris@0: return $user->isActive(); Chris@0: }); Chris@0: } Chris@0: return $entities; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function entityQueryAlter(SelectInterface $query) { Chris@0: parent::entityQueryAlter($query); Chris@0: Chris@0: // Bail out early if we do not need to match the Anonymous user. Chris@0: if (!$this->getConfiguration()['include_anonymous']) { Chris@0: return; Chris@0: } Chris@0: Chris@0: if ($this->currentUser->hasPermission('administer users')) { Chris@0: // In addition, if the user is administrator, we need to make sure to Chris@0: // match the anonymous user, that doesn't actually have a name in the Chris@0: // database. Chris@0: $conditions = &$query->conditions(); Chris@0: foreach ($conditions as $key => $condition) { Chris@0: if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users_field_data.name') { Chris@0: // Remove the condition. Chris@0: unset($conditions[$key]); Chris@0: Chris@0: // Re-add the condition and a condition on uid = 0 so that we end up Chris@0: // with a query in the form: Chris@0: // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0) Chris@0: $or = new Condition('OR'); Chris@0: $or->condition($condition['field'], $condition['value'], $condition['operator']); Chris@0: // Sadly, the Database layer doesn't allow us to build a condition Chris@0: // in the form ':placeholder = :placeholder2', because the 'field' Chris@0: // part of a condition is always escaped. Chris@0: // As a (cheap) workaround, we separately build a condition with no Chris@0: // field, and concatenate the field and the condition separately. Chris@0: $value_part = new Condition('AND'); Chris@0: $value_part->condition('anonymous_name', $condition['value'], $condition['operator']); Chris@0: $value_part->compile($this->connection, $query); Chris@0: $or->condition((new Condition('AND')) Chris@17: ->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: ->condition('base_table.uid', 0) Chris@0: ); Chris@0: $query->condition($or); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: }