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