Version 1
[yaffs-website] / web / modules / contrib / devel / src / Plugin / Block / SwitchUserBlock.php
1 <?php
2
3 namespace Drupal\devel\Plugin\Block;
4
5 use Drupal\Component\Render\FormattableMarkup;
6 use Drupal\Core\Access\AccessResult;
7 use Drupal\Core\Block\BlockBase;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\Form\FormBuilderInterface;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12 use Drupal\Core\Routing\RedirectDestinationTrait;
13 use Drupal\Core\Session\AccountInterface;
14 use Drupal\Core\Session\AnonymousUserSession;
15 use Drupal\Core\Url;
16 use Drupal\user\Entity\Role;
17 use Symfony\Component\DependencyInjection\ContainerInterface;
18
19 /**
20  * Provides a block for switching users.
21  *
22  * @Block(
23  *   id = "devel_switch_user",
24  *   admin_label = @Translation("Switch user"),
25  *   category = @Translation("Forms")
26  * )
27  */
28 class SwitchUserBlock extends BlockBase implements ContainerFactoryPluginInterface {
29
30   use RedirectDestinationTrait;
31
32   /**
33    * The FormBuilder object.
34    *
35    * @var \Drupal\Core\Form\FormBuilderInterface
36    */
37   protected $formBuilder;
38
39   /**
40    * The Current User object.
41    *
42    * @var \Drupal\Core\Session\AccountInterface
43    */
44   protected $currentUser;
45
46   /**
47    * The user storage.
48    *
49    * @var \Drupal\Core\Entity\EntityStorageInterface
50    */
51   protected $userStorage;
52
53   /**
54    * Constructs a new SwitchUserBlock object.
55    *
56    * @param array $configuration
57    *   A configuration array containing information about the plugin instance.
58    * @param string $plugin_id
59    *   The plugin_id for the plugin instance.
60    * @param mixed $plugin_definition
61    *   The plugin implementation definition.
62    * @param \Drupal\Core\Session\AccountInterface $current_user
63    *   Current user.
64    * @param \Drupal\Core\Entity\EntityStorageInterface $user_storage
65    *   The user storage.
66    * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
67    *   The form builder service.
68    */
69   public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountInterface $current_user, EntityStorageInterface $user_storage, FormBuilderInterface $form_builder) {
70     parent::__construct($configuration, $plugin_id, $plugin_definition);
71     $this->formBuilder = $form_builder;
72     $this->currentUser = $current_user;
73     $this->userStorage = $user_storage;
74   }
75
76   /**
77    * {@inheritdoc}
78    */
79   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
80     return new static(
81       $configuration,
82       $plugin_id,
83       $plugin_definition,
84       $container->get('current_user'),
85       $container->get('entity.manager')->getStorage('user'),
86       $container->get('form_builder')
87     );
88   }
89
90   /**
91    * {@inheritdoc}
92    */
93   public function defaultConfiguration() {
94     return [
95       'list_size' => 12,
96       'include_anon' => FALSE,
97       'show_form' => TRUE,
98     ];
99   }
100
101   /**
102    * {@inheritdoc}
103    */
104   public function blockAccess(AccountInterface $account) {
105     return AccessResult::allowedIfHasPermission($account, 'switch users');
106   }
107
108   /**
109    * {@inheritdoc}
110    */
111   public function blockForm($form, FormStateInterface $form_state) {
112     $anononymous = new AnonymousUserSession();
113     $form['list_size'] = [
114       '#type' => 'number',
115       '#title' => $this->t('Number of users to display in the list'),
116       '#default_value' => $this->configuration['list_size'],
117       '#min' => 1,
118       '#max' => 50,
119     ];
120     $form['include_anon'] = [
121       '#type' => 'checkbox',
122       '#title' => $this->t('Include %anonymous', ['%anonymous' => $anononymous->getAccountName()]),
123       '#default_value' => $this->configuration['include_anon'],
124     ];
125     $form['show_form'] = [
126       '#type' => 'checkbox',
127       '#title' => $this->t('Allow entering any user name'),
128       '#default_value' => $this->configuration['show_form'],
129     ];
130
131     return $form;
132   }
133
134   /**
135    * {@inheritdoc}
136    */
137   public function blockSubmit($form, FormStateInterface $form_state) {
138     $this->configuration['list_size'] = $form_state->getValue('list_size');
139     $this->configuration['include_anon'] = $form_state->getValue('include_anon');
140     $this->configuration['show_form'] = $form_state->getValue('show_form');
141   }
142
143   /**
144    * {@inheritdoc}
145    */
146   public function getCacheMaxAge() {
147     return 0;
148   }
149
150   /**
151    * {@inheritdoc}
152    */
153   public function build() {
154     $build = [];
155     if ($accounts = $this->getUsers()) {
156       $build['devel_links'] = $this->buildUserList($accounts);
157
158       if ($this->configuration['show_form']) {
159         $build['devel_form'] = $this->formBuilder->getForm('\Drupal\devel\Form\SwitchUserForm');
160       }
161     }
162
163     return $build;
164   }
165
166   /**
167    * Provides the list of accounts that can be used for the user switch.
168    *
169    * Inactive users are omitted from all of the following db selects. Users
170    * with 'switch users' permission and anonymous user if include_anon property
171    * is set to TRUE, are prioritized.
172    *
173    * @return \Drupal\core\Session\AccountInterface[]
174    *   List of accounts to be used for the switch.
175    */
176   protected function getUsers() {
177     $list_size = $this->configuration['list_size'];
178     $include_anonymous = $this->configuration['include_anon'];
179
180     $list_size = $include_anonymous ? $list_size - 1 : $list_size;
181
182     // Users with 'switch users' permission are prioritized so
183     // we try to load first users with this permission.
184     $query = $this->userStorage->getQuery()
185       ->condition('uid', 0, '>')
186       ->condition('status', 0, '>')
187       ->sort('access', 'DESC')
188       ->range(0, $list_size);
189
190     $roles = user_roles(TRUE, 'switch users');
191
192     if (!empty($roles) && !isset($roles[Role::AUTHENTICATED_ID])) {
193       $query->condition('roles', array_keys($roles), 'IN');
194     }
195
196     $user_ids = $query->execute();
197
198     // If we don't have enough users with 'switch users' permission, add
199     // uids until we hit $list_size.
200     if (count($user_ids) < $list_size) {
201       $query = $this->userStorage->getQuery()
202         ->condition('uid', 0, '>')
203         ->condition('status', 0, '>')
204         ->sort('access', 'DESC')
205         ->range(0, $list_size);
206
207       // Excludes the prioritized user ids only if the previous query return
208       // some records.
209       if (!empty($user_ids)) {
210         $query->condition('uid', array_keys($user_ids), 'NOT IN');
211         $query->range(0, $list_size - count($user_ids));
212       }
213
214       $user_ids += $query->execute();
215     }
216
217     $accounts = $this->userStorage->loadMultiple($user_ids);
218
219     if ($include_anonymous) {
220       $anonymous = new AnonymousUserSession();
221       $accounts[$anonymous->id()] = $anonymous;
222     }
223
224     uasort($accounts, 'static::sortUserList');
225
226     return $accounts;
227   }
228
229   /**
230    * Builds the user listing as renderable array.
231    *
232    * @param \Drupal\core\Session\AccountInterface[] $accounts
233    *   The accounts to be rendered in the list.
234    *
235    * @return array
236    *   A renderable array.
237    */
238   protected function buildUserList(array $accounts) {
239     $links = [];
240
241     foreach ($accounts as $account) {
242       $links[$account->id()] = [
243         'title' => $account->getDisplayName(),
244         'url' => Url::fromRoute('devel.switch', ['name' => $account->getAccountName()]),
245         'query' => $this->getDestinationArray(),
246         'attributes' => [
247           'title' => $account->hasPermission('switch users') ? $this->t('This user can switch back.') : $this->t('Caution: this user will be unable to switch back.'),
248         ],
249       ];
250
251       if ($account->isAnonymous()) {
252         $links[$account->id()]['url'] = Url::fromRoute('user.logout');
253       }
254
255       if ($this->currentUser->id() === $account->id()) {
256         $links[$account->id()]['title'] = new FormattableMarkup('<strong>%user</strong>', ['%user' => $account->getDisplayName()]);
257       }
258     }
259
260     return [
261       '#theme' => 'links',
262       '#links' => $links,
263       '#attached' => ['library' => ['devel/devel']],
264     ];
265   }
266
267   /**
268    * Helper callback for uasort() to sort accounts by last access.
269    */
270   public static function sortUserList(AccountInterface $a, AccountInterface $b) {
271     $a_access = (int) $a->getLastAccessedTime();
272     $b_access = (int) $b->getLastAccessedTime();
273
274     if ($a_access === $b_access) {
275       return 0;
276     }
277
278     // User never access to site.
279     if ($a_access === 0) {
280       return 1;
281     }
282
283     return ($a_access > $b_access) ? -1 : 1;
284   }
285
286 }