Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 108 |
|
0.00% |
0 / 47 |
|
0.00% |
0 / 35 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
| BlockLibraryPanel | |
0.00% |
0 / 102 |
|
0.00% |
0 / 47 |
|
0.00% |
0 / 35 |
|
0.00% |
0 / 10 |
756 | |
0.00% |
0 / 1 |
| create | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| defaultConfiguration | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildConfigurationForm | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| configurationSummary | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
| build | |
0.00% |
0 / 20 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| label | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildCategorySection | |
0.00% |
0 / 13 |
|
0.00% |
0 / 9 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
| getSources | |
0.00% |
0 / 17 |
|
0.00% |
0 / 9 |
|
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
30 | |||
| getProvidersOptions | |
0.00% |
0 / 8 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| getProviders | |
0.00% |
0 / 18 |
|
0.00% |
0 / 16 |
|
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
72 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder\Plugin\display_builder\Island; |
| 6 | |
| 7 | use Drupal\Core\Extension\ModuleExtensionList; |
| 8 | use Drupal\Core\Form\FormStateInterface; |
| 9 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 10 | use Drupal\display_builder\Attribute\Island; |
| 11 | use Drupal\display_builder\BlockLibrarySourceHelper; |
| 12 | use Drupal\display_builder\InstanceInterface; |
| 13 | use Drupal\display_builder\IslandConfigurationFormInterface; |
| 14 | use Drupal\display_builder\IslandConfigurationFormTrait; |
| 15 | use Drupal\display_builder\IslandPluginBase; |
| 16 | use Drupal\display_builder\IslandType; |
| 17 | use Drupal\ui_patterns\SourcePluginBase; |
| 18 | use Drupal\ui_patterns\SourcePluginManager; |
| 19 | use Drupal\ui_patterns\SourceWithChoicesInterface; |
| 20 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 21 | |
| 22 | /** |
| 23 | * Block library island plugin implementation. |
| 24 | */ |
| 25 | #[Island( |
| 26 | id: 'block_library', |
| 27 | enabled_by_default: TRUE, |
| 28 | label: new TranslatableMarkup('Blocks library'), |
| 29 | description: new TranslatableMarkup('List of available blocks.'), |
| 30 | type: IslandType::Library, |
| 31 | )] |
| 32 | class BlockLibraryPanel extends IslandPluginBase implements IslandConfigurationFormInterface { |
| 33 | |
| 34 | use IslandConfigurationFormTrait; |
| 35 | |
| 36 | private const HIDE_SOURCE = [ |
| 37 | 'component', |
| 38 | // Used only for imports from Manage Display and Layout Builder. |
| 39 | 'extra_field', |
| 40 | ]; |
| 41 | |
| 42 | private const HIDE_PROVIDER = ['ui_patterns_blocks']; |
| 43 | |
| 44 | /** |
| 45 | * The sources. |
| 46 | */ |
| 47 | protected ?array $sources = NULL; |
| 48 | |
| 49 | /** |
| 50 | * The module list extension service. |
| 51 | */ |
| 52 | protected ModuleExtensionList $moduleList; |
| 53 | |
| 54 | /** |
| 55 | * The UI Patterns source plugin manager. |
| 56 | */ |
| 57 | protected SourcePluginManager $sourceManager; |
| 58 | |
| 59 | /** |
| 60 | * {@inheritdoc} |
| 61 | */ |
| 62 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 63 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 64 | $instance->moduleList = $container->get('extension.list.module'); |
| 65 | $instance->sourceManager = $container->get('plugin.manager.ui_patterns_source'); |
| 66 | |
| 67 | return $instance; |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * {@inheritdoc} |
| 72 | */ |
| 73 | public function defaultConfiguration(): array { |
| 74 | return [ |
| 75 | 'exclude' => [ |
| 76 | 'devel', |
| 77 | 'htmx', |
| 78 | 'shortcut', |
| 79 | ], |
| 80 | ]; |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * {@inheritdoc} |
| 85 | */ |
| 86 | public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { |
| 87 | $configuration = $this->getConfiguration(); |
| 88 | |
| 89 | $form['exclude'] = [ |
| 90 | '#type' => 'checkboxes', |
| 91 | '#title' => $this->t('Exclude modules'), |
| 92 | '#options' => $this->getProvidersOptions(), |
| 93 | '#default_value' => $configuration['exclude'], |
| 94 | ]; |
| 95 | |
| 96 | return $form; |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * {@inheritdoc} |
| 101 | */ |
| 102 | public function configurationSummary(): array { |
| 103 | $configuration = $this->getConfiguration(); |
| 104 | |
| 105 | return [ |
| 106 | $this->t('Excluded modules: @exclude', [ |
| 107 | '@exclude' => \implode(', ', \array_filter($configuration['exclude'] ?? []) ?: [$this->t('None')]), |
| 108 | ]), |
| 109 | ]; |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * {@inheritdoc} |
| 114 | */ |
| 115 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 116 | $builder_id = (string) $builder->id(); |
| 117 | $configuration = $this->getConfiguration(); |
| 118 | |
| 119 | $exclude_providers = \array_merge( |
| 120 | $configuration['exclude'] ?? [], |
| 121 | self::HIDE_PROVIDER |
| 122 | ); |
| 123 | $categories = BlockLibrarySourceHelper::getGroupedChoices( |
| 124 | $this->getSources(), |
| 125 | $exclude_providers, |
| 126 | ); |
| 127 | |
| 128 | $build = []; |
| 129 | |
| 130 | foreach ($categories as $category_data) { |
| 131 | $build[] = $this->buildCategorySection($category_data, $builder_id); |
| 132 | } |
| 133 | |
| 134 | return [ |
| 135 | '#type' => 'component', |
| 136 | '#component' => 'display_builder:library_panel', |
| 137 | '#slots' => [ |
| 138 | 'content' => $this->buildDraggables($builder_id, $build), |
| 139 | ], |
| 140 | ]; |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * {@inheritdoc} |
| 145 | */ |
| 146 | public function label(): string { |
| 147 | return 'Blocks'; |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Build a category section. |
| 152 | * |
| 153 | * @param array $category_data |
| 154 | * The category data. |
| 155 | * @param string $builder_id |
| 156 | * The builder ID. |
| 157 | * |
| 158 | * @return array |
| 159 | * The render array for the category section. |
| 160 | */ |
| 161 | private function buildCategorySection(array $category_data, string $builder_id): array { |
| 162 | $section = []; |
| 163 | |
| 164 | if (!empty($category_data['label'])) { |
| 165 | $section[] = [ |
| 166 | '#type' => 'html_tag', |
| 167 | '#tag' => 'h4', |
| 168 | '#attributes' => ['class' => 'db-filter-hide-on-search'], |
| 169 | '#value' => $category_data['label'], |
| 170 | ]; |
| 171 | } |
| 172 | |
| 173 | foreach ($category_data['choices'] as $choice) { |
| 174 | $section[] = $choice['preview'] |
| 175 | ? $this->buildPlaceholderButtonWithPreview($builder_id, $choice['label'], $choice['data'] ?? [], $choice['preview'], $choice['keywords'] ?? '') |
| 176 | : $this->buildPlaceholderButton($choice['label'], $choice['data'] ?? [], $choice['keywords'] ?? ''); |
| 177 | } |
| 178 | |
| 179 | return $section; |
| 180 | } |
| 181 | |
| 182 | /** |
| 183 | * Returns all possible sources. |
| 184 | * |
| 185 | * @throws \Drupal\Component\Plugin\Exception\PluginException |
| 186 | * |
| 187 | * @return array<string, array> |
| 188 | * An array of sources. |
| 189 | */ |
| 190 | private function getSources(): array { |
| 191 | if ($this->sources !== NULL) { |
| 192 | return $this->sources; |
| 193 | } |
| 194 | |
| 195 | $definitions = $this->sourceManager->getDefinitionsForPropType('slot', $this->configuration['contexts'] ?? []); |
| 196 | $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]]; |
| 197 | |
| 198 | foreach ($definitions as $source_id => $definition) { |
| 199 | if (\in_array($source_id, self::HIDE_SOURCE, TRUE)) { |
| 200 | continue; |
| 201 | } |
| 202 | $source = $this->sourceManager->createInstance($source_id, |
| 203 | SourcePluginBase::buildConfiguration('slot', $slot_definition, ['source' => []], $this->configuration['contexts'] ?? []) |
| 204 | ); |
| 205 | $this->sources[$source_id] = [ |
| 206 | 'definition' => $definition, |
| 207 | 'source' => $source, |
| 208 | ]; |
| 209 | |
| 210 | if ($source instanceof SourceWithChoicesInterface) { |
| 211 | $this->sources[$source_id]['choices'] = $source->getChoices(); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | return $this->sources; |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Get providers options for select input. |
| 220 | * |
| 221 | * @return array |
| 222 | * An associative array with module ID as key and module description as |
| 223 | * value. |
| 224 | */ |
| 225 | private function getProvidersOptions(): array { |
| 226 | $options = []; |
| 227 | |
| 228 | foreach ($this->getProviders() as $provider_id => $provider) { |
| 229 | $params = [ |
| 230 | '@name' => $provider['name'], |
| 231 | '@count' => $provider['count'], |
| 232 | ]; |
| 233 | $options[$provider_id] = $this->formatPlural($provider['count'], '@name (@count block)', '@name (@count blocks)', $params); |
| 234 | } |
| 235 | |
| 236 | return $options; |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Get all providers. |
| 241 | * |
| 242 | * @return array |
| 243 | * Drupal modules definitions, keyed by extension ID |
| 244 | */ |
| 245 | private function getProviders(): array { |
| 246 | $sources = $this->getSources(); |
| 247 | $providers = []; |
| 248 | $modules = $this->moduleList->getAllInstalledInfo(); |
| 249 | |
| 250 | foreach ($sources as $source_data) { |
| 251 | if (!isset($source_data['choices'])) { |
| 252 | continue; |
| 253 | } |
| 254 | $choices = $source_data['choices']; |
| 255 | |
| 256 | foreach ($choices as $choice) { |
| 257 | $provider = $choice['provider'] ?? ''; |
| 258 | |
| 259 | if (!$provider || \in_array($provider, self::HIDE_PROVIDER, TRUE)) { |
| 260 | continue; |
| 261 | } |
| 262 | |
| 263 | if (!isset($modules[$provider])) { |
| 264 | // If the provider is not a module, skip it. |
| 265 | continue; |
| 266 | } |
| 267 | |
| 268 | if (!isset($providers[$provider])) { |
| 269 | $providers[$provider] = $modules[$provider]; |
| 270 | $providers[$provider]['count'] = 0; |
| 271 | } |
| 272 | ++$providers[$provider]['count']; |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | return $providers; |
| 277 | } |
| 278 | |
| 279 | } |
Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 115 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 116 | $builder_id = (string) $builder->id(); |
| 117 | $configuration = $this->getConfiguration(); |
| 118 | |
| 119 | $exclude_providers = \array_merge( |
| 120 | $configuration['exclude'] ?? [], |
| 121 | self::HIDE_PROVIDER |
| 122 | ); |
| 123 | $categories = BlockLibrarySourceHelper::getGroupedChoices( |
| 124 | $this->getSources(), |
| 125 | $exclude_providers, |
| 126 | ); |
| 127 | |
| 128 | $build = []; |
| 129 | |
| 130 | foreach ($categories as $category_data) { |
| 130 | foreach ($categories as $category_data) { |
| 130 | foreach ($categories as $category_data) { |
| 131 | $build[] = $this->buildCategorySection($category_data, $builder_id); |
| 130 | foreach ($categories as $category_data) { |
| 131 | $build[] = $this->buildCategorySection($category_data, $builder_id); |
| 132 | } |
| 133 | |
| 134 | return [ |
| 135 | '#type' => 'component', |
| 136 | '#component' => 'display_builder:library_panel', |
| 137 | '#slots' => [ |
| 138 | 'content' => $this->buildDraggables($builder_id, $build), |
| 161 | private function buildCategorySection(array $category_data, string $builder_id): array { |
| 162 | $section = []; |
| 163 | |
| 164 | if (!empty($category_data['label'])) { |
| 166 | '#type' => 'html_tag', |
| 167 | '#tag' => 'h4', |
| 168 | '#attributes' => ['class' => 'db-filter-hide-on-search'], |
| 169 | '#value' => $category_data['label'], |
| 170 | ]; |
| 171 | } |
| 172 | |
| 173 | foreach ($category_data['choices'] as $choice) { |
| 173 | foreach ($category_data['choices'] as $choice) { |
| 173 | foreach ($category_data['choices'] as $choice) { |
| 174 | $section[] = $choice['preview'] |
| 175 | ? $this->buildPlaceholderButtonWithPreview($builder_id, $choice['label'], $choice['data'] ?? [], $choice['preview'], $choice['keywords'] ?? '') |
| 174 | $section[] = $choice['preview'] |
| 175 | ? $this->buildPlaceholderButtonWithPreview($builder_id, $choice['label'], $choice['data'] ?? [], $choice['preview'], $choice['keywords'] ?? '') |
| 176 | : $this->buildPlaceholderButton($choice['label'], $choice['data'] ?? [], $choice['keywords'] ?? ''); |
| 173 | foreach ($category_data['choices'] as $choice) { |
| 174 | $section[] = $choice['preview'] |
| 173 | foreach ($category_data['choices'] as $choice) { |
| 174 | $section[] = $choice['preview'] |
| 175 | ? $this->buildPlaceholderButtonWithPreview($builder_id, $choice['label'], $choice['data'] ?? [], $choice['preview'], $choice['keywords'] ?? '') |
| 176 | : $this->buildPlaceholderButton($choice['label'], $choice['data'] ?? [], $choice['keywords'] ?? ''); |
| 177 | } |
| 178 | |
| 179 | return $section; |
| 86 | public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { |
| 87 | $configuration = $this->getConfiguration(); |
| 88 | |
| 89 | $form['exclude'] = [ |
| 90 | '#type' => 'checkboxes', |
| 91 | '#title' => $this->t('Exclude modules'), |
| 92 | '#options' => $this->getProvidersOptions(), |
| 93 | '#default_value' => $configuration['exclude'], |
| 94 | ]; |
| 95 | |
| 96 | return $form; |
| 103 | $configuration = $this->getConfiguration(); |
| 104 | |
| 105 | return [ |
| 106 | $this->t('Excluded modules: @exclude', [ |
| 107 | '@exclude' => \implode(', ', \array_filter($configuration['exclude'] ?? []) ?: [$this->t('None')]), |
| 62 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 63 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 64 | $instance->moduleList = $container->get('extension.list.module'); |
| 65 | $instance->sourceManager = $container->get('plugin.manager.ui_patterns_source'); |
| 66 | |
| 67 | return $instance; |
| 76 | 'devel', |
| 246 | $sources = $this->getSources(); |
| 247 | $providers = []; |
| 248 | $modules = $this->moduleList->getAllInstalledInfo(); |
| 249 | |
| 250 | foreach ($sources as $source_data) { |
| 250 | foreach ($sources as $source_data) { |
| 251 | if (!isset($source_data['choices'])) { |
| 252 | continue; |
| 254 | $choices = $source_data['choices']; |
| 255 | |
| 256 | foreach ($choices as $choice) { |
| 256 | foreach ($choices as $choice) { |
| 257 | $provider = $choice['provider'] ?? ''; |
| 258 | |
| 259 | if (!$provider || \in_array($provider, self::HIDE_PROVIDER, TRUE)) { |
| 259 | if (!$provider || \in_array($provider, self::HIDE_PROVIDER, TRUE)) { |
| 260 | continue; |
| 263 | if (!isset($modules[$provider])) { |
| 265 | continue; |
| 268 | if (!isset($providers[$provider])) { |
| 269 | $providers[$provider] = $modules[$provider]; |
| 270 | $providers[$provider]['count'] = 0; |
| 271 | } |
| 272 | ++$providers[$provider]['count']; |
| 256 | foreach ($choices as $choice) { |
| 257 | $provider = $choice['provider'] ?? ''; |
| 258 | |
| 259 | if (!$provider || \in_array($provider, self::HIDE_PROVIDER, TRUE)) { |
| 260 | continue; |
| 261 | } |
| 262 | |
| 263 | if (!isset($modules[$provider])) { |
| 264 | // If the provider is not a module, skip it. |
| 265 | continue; |
| 266 | } |
| 267 | |
| 268 | if (!isset($providers[$provider])) { |
| 269 | $providers[$provider] = $modules[$provider]; |
| 270 | $providers[$provider]['count'] = 0; |
| 271 | } |
| 272 | ++$providers[$provider]['count']; |
| 250 | foreach ($sources as $source_data) { |
| 251 | if (!isset($source_data['choices'])) { |
| 252 | continue; |
| 253 | } |
| 254 | $choices = $source_data['choices']; |
| 255 | |
| 256 | foreach ($choices as $choice) { |
| 250 | foreach ($sources as $source_data) { |
| 251 | if (!isset($source_data['choices'])) { |
| 252 | continue; |
| 253 | } |
| 254 | $choices = $source_data['choices']; |
| 255 | |
| 256 | foreach ($choices as $choice) { |
| 257 | $provider = $choice['provider'] ?? ''; |
| 258 | |
| 259 | if (!$provider || \in_array($provider, self::HIDE_PROVIDER, TRUE)) { |
| 260 | continue; |
| 261 | } |
| 262 | |
| 263 | if (!isset($modules[$provider])) { |
| 264 | // If the provider is not a module, skip it. |
| 265 | continue; |
| 266 | } |
| 267 | |
| 268 | if (!isset($providers[$provider])) { |
| 269 | $providers[$provider] = $modules[$provider]; |
| 270 | $providers[$provider]['count'] = 0; |
| 271 | } |
| 272 | ++$providers[$provider]['count']; |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | return $providers; |
| 226 | $options = []; |
| 227 | |
| 228 | foreach ($this->getProviders() as $provider_id => $provider) { |
| 228 | foreach ($this->getProviders() as $provider_id => $provider) { |
| 228 | foreach ($this->getProviders() as $provider_id => $provider) { |
| 228 | foreach ($this->getProviders() as $provider_id => $provider) { |
| 229 | $params = [ |
| 230 | '@name' => $provider['name'], |
| 231 | '@count' => $provider['count'], |
| 232 | ]; |
| 233 | $options[$provider_id] = $this->formatPlural($provider['count'], '@name (@count block)', '@name (@count blocks)', $params); |
| 234 | } |
| 235 | |
| 236 | return $options; |
| 191 | if ($this->sources !== NULL) { |
| 192 | return $this->sources; |
| 195 | $definitions = $this->sourceManager->getDefinitionsForPropType('slot', $this->configuration['contexts'] ?? []); |
| 196 | $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]]; |
| 197 | |
| 198 | foreach ($definitions as $source_id => $definition) { |
| 198 | foreach ($definitions as $source_id => $definition) { |
| 198 | foreach ($definitions as $source_id => $definition) { |
| 199 | if (\in_array($source_id, self::HIDE_SOURCE, TRUE)) { |
| 200 | continue; |
| 202 | $source = $this->sourceManager->createInstance($source_id, |
| 203 | SourcePluginBase::buildConfiguration('slot', $slot_definition, ['source' => []], $this->configuration['contexts'] ?? []) |
| 204 | ); |
| 205 | $this->sources[$source_id] = [ |
| 206 | 'definition' => $definition, |
| 207 | 'source' => $source, |
| 208 | ]; |
| 209 | |
| 210 | if ($source instanceof SourceWithChoicesInterface) { |
| 198 | foreach ($definitions as $source_id => $definition) { |
| 199 | if (\in_array($source_id, self::HIDE_SOURCE, TRUE)) { |
| 200 | continue; |
| 201 | } |
| 202 | $source = $this->sourceManager->createInstance($source_id, |
| 203 | SourcePluginBase::buildConfiguration('slot', $slot_definition, ['source' => []], $this->configuration['contexts'] ?? []) |
| 204 | ); |
| 205 | $this->sources[$source_id] = [ |
| 206 | 'definition' => $definition, |
| 207 | 'source' => $source, |
| 208 | ]; |
| 209 | |
| 210 | if ($source instanceof SourceWithChoicesInterface) { |
| 211 | $this->sources[$source_id]['choices'] = $source->getChoices(); |
| 198 | foreach ($definitions as $source_id => $definition) { |
| 199 | if (\in_array($source_id, self::HIDE_SOURCE, TRUE)) { |
| 200 | continue; |
| 201 | } |
| 202 | $source = $this->sourceManager->createInstance($source_id, |
| 203 | SourcePluginBase::buildConfiguration('slot', $slot_definition, ['source' => []], $this->configuration['contexts'] ?? []) |
| 204 | ); |
| 205 | $this->sources[$source_id] = [ |
| 206 | 'definition' => $definition, |
| 207 | 'source' => $source, |
| 208 | ]; |
| 209 | |
| 210 | if ($source instanceof SourceWithChoicesInterface) { |
| 211 | $this->sources[$source_id]['choices'] = $source->getChoices(); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | return $this->sources; |
| 147 | return 'Blocks'; |