Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
85.71% covered (warning)
85.71%
72 / 84
94.12% covered (success)
94.12%
32 / 34
40.00% covered (danger)
40.00%
14 / 35
87.50% covered (warning)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConfigFormBuilder
85.71% covered (warning)
85.71%
72 / 84
94.12% covered (success)
94.12%
32 / 34
40.00% covered (danger)
40.00%
14 / 35
87.50% covered (warning)
87.50%
7 / 8
126.54
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 build
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
10 / 10
26.67% covered (danger)
26.67%
4 / 15
100.00% covered (success)
100.00%
1 / 1
26.32
 getAllowedProfiles
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
5 / 5
25.00% covered (danger)
25.00%
1 / 4
100.00% covered (success)
100.00%
1 / 1
6.80
 isAllowed
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 buildSelect
50.00% covered (danger)
50.00%
12 / 24
75.00% covered (warning)
75.00%
6 / 8
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
10.75
 buildLink
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 buildDisabledSelect
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isCurrentUserAllowedToAdministrate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder;
6
7use Drupal\Core\Entity\EntityTypeManagerInterface;
8use Drupal\Core\Extension\ModuleHandlerInterface;
9use Drupal\Core\Session\AccountInterface;
10use Drupal\Core\Session\AccountProxyInterface;
11use Drupal\Core\StringTranslation\StringTranslationTrait;
12use Drupal\Core\Url;
13
14/**
15 * Config form builder.
16 */
17class ConfigFormBuilder implements ConfigFormBuilderInterface {
18
19  use StringTranslationTrait;
20
21  public function __construct(
22    protected EntityTypeManagerInterface $entityTypeManager,
23    protected AccountProxyInterface $currentUser,
24    protected readonly ModuleHandlerInterface $moduleHandler,
25  ) {}
26
27  /**
28   * {@inheritdoc}
29   */
30  public function build(DisplayBuildableInterface $buildable, bool $mandatory = TRUE): array {
31    $profile = $buildable->getProfile();
32    $allowed = $this->isAllowed($buildable);
33
34    if (!$allowed && !$profile) {
35      return [
36        ConfigFormBuilderInterface::PROFILE_PROPERTY => [
37          '#markup' => $this->t('You are not allowed to use Display Builder.'),
38        ],
39      ];
40    }
41
42    if (!$allowed && $profile) {
43      return [
44        ConfigFormBuilderInterface::PROFILE_PROPERTY => $this->buildDisabledSelect($profile),
45      ];
46    }
47
48    $form = [
49      ConfigFormBuilderInterface::PROFILE_PROPERTY => $this->buildSelect($profile, $mandatory),
50    ];
51
52    // Add the builder link to edit.
53    if ($buildable->getInstanceId() && $profile) {
54      $form['link'] = $this->buildLink($buildable);
55    }
56
57    return $form;
58  }
59
60  /**
61   * {@inheritdoc}
62   */
63  public function getAllowedProfiles(?AccountInterface $account = NULL): array {
64    $account = $account ?? $this->currentUser;
65    $options = [];
66    $storage = $this->entityTypeManager->getStorage('display_builder_profile');
67    $entity_ids = $storage->getQuery()->accessCheck(TRUE)->sort('weight', 'ASC')->execute();
68    /** @var \Drupal\display_builder\ProfileInterface[] $display_builders */
69    $display_builders = $storage->loadMultiple($entity_ids);
70
71    // Entity query doesn't execute access control handlers for config
72    // entities. So we need to do an extra check here.
73    foreach ($display_builders as $entity_id => $entity) {
74      // We don't execute $entity->access() to not catch 'administer display
75      // builder profile' permission. See ProfileAccessControlHandler.
76      // Administrators can use any profile, but it is better to only propose
77      // them the ones related to their permissions.
78      if ($account->hasPermission($entity->getPermissionName())) {
79        $options[$entity_id] = $entity->label();
80      }
81    }
82
83    return $options;
84  }
85
86  /**
87   * {@inheritdoc}
88   */
89  public function isAllowed(DisplayBuildableInterface $buildable, ?AccountInterface $account = NULL): bool {
90    $options = $this->getAllowedProfiles($account);
91
92    if (empty($options)) {
93      return FALSE;
94    }
95    $profile = $buildable->getProfile();
96
97    if (!$profile) {
98      return TRUE;
99    }
100
101    return isset($options[(string) $profile->id()]);
102  }
103
104  /**
105   * Build profile select when user is allowed to select one.
106   *
107   * @param ?ProfileInterface $profile
108   *   Display Builder profile (or not)
109   * @param bool $mandatory
110   *   (Optional). Is it mandatory to use Display Builder? (for example, in
111   *   Page Layouts or in Entity View display Overrides). If not mandatory,
112   *   the Display Builder is activated only if a Display Builder config entity
113   *   is selected.
114   *
115   * @return array
116   *   A renderable form array.
117   */
118  protected function buildSelect(?ProfileInterface $profile, bool $mandatory): array {
119    $select = [
120      '#type' => 'select',
121      '#title' => $this->t('Profile'),
122      '#description' => $this->t('The profile defines the features available in the builder.'),
123      '#options' => $this->getAllowedProfiles(),
124    ];
125
126    if ($profile) {
127      $select['#default_value'] = (string) $profile->id();
128    }
129
130    if ($mandatory) {
131      $select['#required'] = TRUE;
132    }
133    else {
134      $select['#empty_option'] = $this->t('- Disabled -');
135    }
136
137    // Add admin information to link the profiles.
138    if ($this->isCurrentUserAllowedToAdministrate()) {
139      $select['#description'] = [
140        [
141          '#markup' => $select['#description'] . '<br>',
142        ],
143        [
144          '#type' => 'link',
145          '#title' => $this->t('Add and configure display builder profiles'),
146          '#url' => Url::fromRoute('entity.display_builder_profile.collection'),
147          '#suffix' => '.',
148        ],
149      ];
150    }
151
152    return $select;
153  }
154
155  /**
156   * Build link to Display Builder.
157   *
158   * @param \Drupal\display_builder\DisplayBuildableInterface $buildable
159   *   An entity allowing the use of Display Builder.
160   *
161   * @return array
162   *   A renderable array.
163   */
164  protected function buildLink(DisplayBuildableInterface $buildable): array {
165    return [
166      '#type' => 'html_tag',
167      '#tag' => 'p',
168      '#attributes' => [
169        'class' => ['form-item__description'],
170      ],
171      'content' => [
172        '#type' => 'link',
173        '#title' => $this->t('Build the display'),
174        '#url' => $buildable->getBuilderUrl(),
175        '#attributes' => [
176          'class' => ['button', 'button--small'],
177        ],
178      ],
179    ];
180  }
181
182  /**
183   * Build disabled profile select when user is not allowed to select one.
184   *
185   * @param ProfileInterface $profile
186   *   Display Builder profile (or not)
187   *
188   * @return array
189   *   A renderable form array.
190   */
191  protected function buildDisabledSelect(ProfileInterface $profile): array {
192    return [
193      '#type' => 'select',
194      '#title' => $this->t('Profile'),
195      '#description' => $this->t('You are not allowed to use Display Builder here.'),
196      '#options' => [
197        (string) $profile->id() => $profile->label(),
198      ],
199      '#disabled' => TRUE,
200    ];
201  }
202
203  /**
204   * Is the current user allowed to use administrate display builder profiles?
205   *
206   * @return bool
207   *   Allowed or not.
208   */
209  protected function isCurrentUserAllowedToAdministrate(): bool {
210    return $this->moduleHandler->moduleExists('display_builder_ui') && $this->currentUser->hasPermission('administer display builder profile');
211  }
212
213}