3 namespace Drupal\user\Plugin\Block;
5 use Drupal\Core\Access\AccessResult;
6 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
7 use Drupal\Core\Routing\RedirectDestinationTrait;
8 use Drupal\Core\Routing\RouteMatchInterface;
10 use Drupal\Core\Session\AccountInterface;
11 use Drupal\Core\Block\BlockBase;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
15 * Provides a 'User login' block.
18 * id = "user_login_block",
19 * admin_label = @Translation("User login"),
20 * category = @Translation("Forms")
23 class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterface {
25 use RedirectDestinationTrait;
30 * @var \Drupal\Core\Routing\RouteMatchInterface
32 protected $routeMatch;
35 * Constructs a new UserLoginBlock instance.
37 * @param array $configuration
38 * The plugin configuration, i.e. an array with configuration values keyed
39 * by configuration option name. The special key 'context' may be used to
40 * initialize the defined contexts by setting it to an array of context
41 * values keyed by context names.
42 * @param string $plugin_id
43 * The plugin_id for the plugin instance.
44 * @param mixed $plugin_definition
45 * The plugin implementation definition.
46 * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
49 public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
50 parent::__construct($configuration, $plugin_id, $plugin_definition);
52 $this->routeMatch = $route_match;
58 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
63 $container->get('current_route_match')
70 protected function blockAccess(AccountInterface $account) {
71 $route_name = $this->routeMatch->getRouteName();
72 if ($account->isAnonymous() && !in_array($route_name, ['user.login', 'user.logout'])) {
73 return AccessResult::allowed()
74 ->addCacheContexts(['route.name', 'user.roles:anonymous']);
76 return AccessResult::forbidden();
82 public function build() {
83 $form = \Drupal::formBuilder()->getForm('Drupal\user\Form\UserLoginForm');
84 unset($form['name']['#attributes']['autofocus']);
85 // When unsetting field descriptions, also unset aria-describedby attributes
86 // to avoid introducing an accessibility bug.
87 // @todo Do this automatically in https://www.drupal.org/node/2547063.
88 unset($form['name']['#description']);
89 unset($form['name']['#attributes']['aria-describedby']);
90 unset($form['pass']['#description']);
91 unset($form['pass']['#attributes']['aria-describedby']);
92 $form['name']['#size'] = 15;
93 $form['pass']['#size'] = 15;
95 // Instead of setting an actual action URL, we set the placeholder, which
96 // will be replaced at the very last moment. This ensures forms with
97 // dynamically generated action URLs don't have poor cacheability.
98 // Use the proper API to generate the placeholder, when we have one. See
99 // https://www.drupal.org/node/2562341. The placeholder uses a fixed string
101 // Crypt::hashBase64('\Drupal\user\Plugin\Block\UserLoginBlock::build');
102 // This is based on the implementation in
103 // \Drupal\Core\Form\FormBuilder::prepareForm(), but the user login block
104 // requires different behavior for the destination query argument.
105 $placeholder = 'form_action_p_4r8ITd22yaUvXM6SzwrSe9rnQWe48hz9k1Sxto3pBvE';
107 $form['#attached']['placeholders'][$placeholder] = [
108 '#lazy_builder' => ['\Drupal\user\Plugin\Block\UserLoginBlock::renderPlaceholderFormAction', []],
110 $form['#action'] = $placeholder;
112 // Build action links.
114 if (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
115 $items['create_account'] = [
117 '#title' => $this->t('Create new account'),
118 '#url' => Url::fromRoute('user.register', [], [
120 'title' => $this->t('Create a new user account.'),
121 'class' => ['create-account-link'],
126 $items['request_password'] = [
128 '#title' => $this->t('Reset your password'),
129 '#url' => Url::fromRoute('user.pass', [], [
131 'title' => $this->t('Send password reset instructions via email.'),
132 'class' => ['request-password-link'],
137 'user_login_form' => $form,
139 '#theme' => 'item_list',
146 * #lazy_builder callback; renders a form action URL including destination.
149 * A renderable array representing the form action.
151 * @see \Drupal\Core\Form\FormBuilder::renderPlaceholderFormAction()
153 public static function renderPlaceholderFormAction() {
156 '#markup' => Url::fromRoute('<current>', [], ['query' => \Drupal::destination()->getAsArray(), 'external' => FALSE])->toString(),
157 '#cache' => ['contexts' => ['url.path', 'url.query_args']],