3 namespace Drupal\devel\Plugin\Block;
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;
16 use Drupal\user\Entity\Role;
17 use Symfony\Component\DependencyInjection\ContainerInterface;
20 * Provides a block for switching users.
23 * id = "devel_switch_user",
24 * admin_label = @Translation("Switch user"),
25 * category = @Translation("Forms")
28 class SwitchUserBlock extends BlockBase implements ContainerFactoryPluginInterface {
30 use RedirectDestinationTrait;
33 * The FormBuilder object.
35 * @var \Drupal\Core\Form\FormBuilderInterface
37 protected $formBuilder;
40 * The Current User object.
42 * @var \Drupal\Core\Session\AccountInterface
44 protected $currentUser;
49 * @var \Drupal\Core\Entity\EntityStorageInterface
51 protected $userStorage;
54 * Constructs a new SwitchUserBlock object.
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
64 * @param \Drupal\Core\Entity\EntityStorageInterface $user_storage
66 * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
67 * The form builder service.
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;
79 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
84 $container->get('current_user'),
85 $container->get('entity.manager')->getStorage('user'),
86 $container->get('form_builder')
93 public function defaultConfiguration() {
96 'include_anon' => FALSE,
104 public function blockAccess(AccountInterface $account) {
105 return AccessResult::allowedIfHasPermission($account, 'switch users');
111 public function blockForm($form, FormStateInterface $form_state) {
112 $anononymous = new AnonymousUserSession();
113 $form['list_size'] = [
115 '#title' => $this->t('Number of users to display in the list'),
116 '#default_value' => $this->configuration['list_size'],
120 $form['include_anon'] = [
121 '#type' => 'checkbox',
122 '#title' => $this->t('Include %anonymous', ['%anonymous' => $anononymous->getAccountName()]),
123 '#default_value' => $this->configuration['include_anon'],
125 $form['show_form'] = [
126 '#type' => 'checkbox',
127 '#title' => $this->t('Allow entering any user name'),
128 '#default_value' => $this->configuration['show_form'],
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');
146 public function getCacheMaxAge() {
153 public function build() {
155 if ($accounts = $this->getUsers()) {
156 $build['devel_links'] = $this->buildUserList($accounts);
158 if ($this->configuration['show_form']) {
159 $build['devel_form'] = $this->formBuilder->getForm('\Drupal\devel\Form\SwitchUserForm');
167 * Provides the list of accounts that can be used for the user switch.
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.
173 * @return \Drupal\core\Session\AccountInterface[]
174 * List of accounts to be used for the switch.
176 protected function getUsers() {
177 $list_size = $this->configuration['list_size'];
178 $include_anonymous = $this->configuration['include_anon'];
180 $list_size = $include_anonymous ? $list_size - 1 : $list_size;
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);
190 $roles = user_roles(TRUE, 'switch users');
192 if (!empty($roles) && !isset($roles[Role::AUTHENTICATED_ID])) {
193 $query->condition('roles', array_keys($roles), 'IN');
196 $user_ids = $query->execute();
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);
207 // Excludes the prioritized user ids only if the previous query return
209 if (!empty($user_ids)) {
210 $query->condition('uid', array_keys($user_ids), 'NOT IN');
211 $query->range(0, $list_size - count($user_ids));
214 $user_ids += $query->execute();
217 $accounts = $this->userStorage->loadMultiple($user_ids);
219 if ($include_anonymous) {
220 $anonymous = new AnonymousUserSession();
221 $accounts[$anonymous->id()] = $anonymous;
224 uasort($accounts, 'static::sortUserList');
230 * Builds the user listing as renderable array.
232 * @param \Drupal\core\Session\AccountInterface[] $accounts
233 * The accounts to be rendered in the list.
236 * A renderable array.
238 protected function buildUserList(array $accounts) {
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(),
247 'title' => $account->hasPermission('switch users') ? $this->t('This user can switch back.') : $this->t('Caution: this user will be unable to switch back.'),
251 if ($account->isAnonymous()) {
252 $links[$account->id()]['url'] = Url::fromRoute('user.logout');
255 if ($this->currentUser->id() === $account->id()) {
256 $links[$account->id()]['title'] = new FormattableMarkup('<strong>%user</strong>', ['%user' => $account->getDisplayName()]);
263 '#attached' => ['library' => ['devel/devel']],
268 * Helper callback for uasort() to sort accounts by last access.
270 public static function sortUserList(AccountInterface $a, AccountInterface $b) {
271 $a_access = (int) $a->getLastAccessedTime();
272 $b_access = (int) $b->getLastAccessedTime();
274 if ($a_access === $b_access) {
278 // User never access to site.
279 if ($a_access === 0) {
283 return ($a_access > $b_access) ? -1 : 1;