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 }
|