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