Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 85 |
|
0.00% |
0 / 11 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
| PreviewPanel | |
0.00% |
0 / 77 |
|
0.00% |
0 / 11 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 4 |
56 | |
0.00% |
0 / 1 |
| create | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| keyboardShortcuts | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| build | |
0.00% |
0 / 11 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
| alterPreviewPlaceholder | |
0.00% |
0 / 58 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder\Plugin\display_builder\Island; |
| 6 | |
| 7 | use Drupal\Core\Render\RendererInterface; |
| 8 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 9 | use Drupal\display_builder\Attribute\Island; |
| 10 | use Drupal\display_builder\DisplayBuilderHelpers; |
| 11 | use Drupal\display_builder\InstanceInterface; |
| 12 | use Drupal\display_builder\Island\IslandPluginBase; |
| 13 | use Drupal\display_builder\Island\IslandReloadEventsTrait; |
| 14 | use Drupal\display_builder\Island\IslandType; |
| 15 | use Drupal\ui_patterns\Element\ComponentElementBuilder; |
| 16 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 17 | |
| 18 | /** |
| 19 | * Preview island plugin implementation. |
| 20 | */ |
| 21 | #[Island( |
| 22 | id: 'preview', |
| 23 | enabled_by_default: TRUE, |
| 24 | label: new TranslatableMarkup('Preview'), |
| 25 | description: new TranslatableMarkup('Show a real time preview of the display.'), |
| 26 | type: IslandType::View, |
| 27 | default_region: 'main', |
| 28 | icon: 'binoculars', |
| 29 | )] |
| 30 | class PreviewPanel extends IslandPluginBase { |
| 31 | |
| 32 | use IslandReloadEventsTrait; |
| 33 | |
| 34 | /** |
| 35 | * The component element builder. |
| 36 | */ |
| 37 | protected ComponentElementBuilder $componentElementBuilder; |
| 38 | |
| 39 | /** |
| 40 | * The renderer. |
| 41 | */ |
| 42 | protected RendererInterface $renderer; |
| 43 | |
| 44 | /** |
| 45 | * {@inheritdoc} |
| 46 | */ |
| 47 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 48 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 49 | $instance->componentElementBuilder = $container->get('ui_patterns.component_element_builder'); |
| 50 | $instance->renderer = $container->get('renderer'); |
| 51 | |
| 52 | return $instance; |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * {@inheritdoc} |
| 57 | */ |
| 58 | public static function keyboardShortcuts(): array { |
| 59 | return [ |
| 60 | 'key' => 'p', |
| 61 | 'help' => t('Show the preview'), |
| 62 | ]; |
| 63 | } |
| 64 | |
| 65 | /** |
| 66 | * {@inheritdoc} |
| 67 | */ |
| 68 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 69 | if (empty($data)) { |
| 70 | return []; |
| 71 | } |
| 72 | |
| 73 | $this->alterPreviewPlaceholder($data); |
| 74 | |
| 75 | $returned = []; |
| 76 | |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 94 | } |
| 95 | |
| 96 | return $returned; |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Replace placeholder for preview. |
| 101 | * |
| 102 | * Some block source will not be created, create a simple placeholder to have |
| 103 | * a preview instead of nothing. |
| 104 | * |
| 105 | * @todo move as an issue to UI Patterns? |
| 106 | * |
| 107 | * @param array $data |
| 108 | * The instance data to replace. |
| 109 | */ |
| 110 | private function alterPreviewPlaceholder(array &$data): void { |
| 111 | $replacements = [ |
| 112 | [ |
| 113 | 'search' => ['plugin_id' => 'system_messages_block'], |
| 114 | 'new_value_title' => new TranslatableMarkup('[Placeholder] Block messages'), |
| 115 | ], |
| 116 | [ |
| 117 | 'search' => ['source_id' => 'page_title'], |
| 118 | 'new_value_title' => new TranslatableMarkup('[Placeholder] Page title'), |
| 119 | ], |
| 120 | [ |
| 121 | 'search' => ['source_id' => 'main_page_content'], |
| 122 | 'new_value_title' => new TranslatableMarkup('[Placeholder] Page content'), |
| 123 | 'new_value_class' => 'db-preview-placeholder-lg', |
| 124 | ], |
| 125 | [ |
| 126 | 'search' => ['source_id' => 'view_attachment_before'], |
| 127 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment before'), |
| 128 | 'new_value_class' => 'db-preview-placeholder-md', |
| 129 | ], |
| 130 | [ |
| 131 | 'search' => ['source_id' => 'view_exposed'], |
| 132 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View exposed form'), |
| 133 | 'new_value_class' => 'db-preview-placeholder-md', |
| 134 | ], |
| 135 | [ |
| 136 | 'search' => ['source_id' => 'view_header'], |
| 137 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View header'), |
| 138 | 'new_value_class' => 'db-preview-placeholder-md', |
| 139 | ], |
| 140 | [ |
| 141 | 'search' => ['source_id' => 'view_rows'], |
| 142 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View rows'), |
| 143 | 'new_value_class' => 'db-preview-placeholder-lg', |
| 144 | ], |
| 145 | [ |
| 146 | 'search' => ['source_id' => 'view_attachment_after'], |
| 147 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment after'), |
| 148 | 'new_value_class' => 'db-preview-placeholder-md', |
| 149 | ], |
| 150 | [ |
| 151 | 'search' => ['source_id' => 'view_pager'], |
| 152 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View pager'), |
| 153 | ], |
| 154 | [ |
| 155 | 'search' => ['source_id' => 'view_more'], |
| 156 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View more'), |
| 157 | ], |
| 158 | [ |
| 159 | 'search' => ['source_id' => 'view_footer'], |
| 160 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View footer'), |
| 161 | 'new_value_class' => 'db-preview-placeholder-md', |
| 162 | ], |
| 163 | [ |
| 164 | 'search' => ['source_id' => 'view_feed_icons'], |
| 165 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View feed icons'), |
| 166 | ], |
| 167 | ]; |
| 168 | |
| 169 | DisplayBuilderHelpers::findAndReplaceInArray($data, $replacements); |
| 170 | } |
| 171 | |
| 172 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 110 | private function alterPreviewPlaceholder(array &$data): void { |
| 111 | $replacements = [ |
| 112 | [ |
| 113 | 'search' => ['plugin_id' => 'system_messages_block'], |
| 114 | 'new_value_title' => new TranslatableMarkup('[Placeholder] Block messages'), |
| 115 | ], |
| 116 | [ |
| 117 | 'search' => ['source_id' => 'page_title'], |
| 118 | 'new_value_title' => new TranslatableMarkup('[Placeholder] Page title'), |
| 119 | ], |
| 120 | [ |
| 121 | 'search' => ['source_id' => 'main_page_content'], |
| 122 | 'new_value_title' => new TranslatableMarkup('[Placeholder] Page content'), |
| 123 | 'new_value_class' => 'db-preview-placeholder-lg', |
| 124 | ], |
| 125 | [ |
| 126 | 'search' => ['source_id' => 'view_attachment_before'], |
| 127 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment before'), |
| 128 | 'new_value_class' => 'db-preview-placeholder-md', |
| 129 | ], |
| 130 | [ |
| 131 | 'search' => ['source_id' => 'view_exposed'], |
| 132 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View exposed form'), |
| 133 | 'new_value_class' => 'db-preview-placeholder-md', |
| 134 | ], |
| 135 | [ |
| 136 | 'search' => ['source_id' => 'view_header'], |
| 137 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View header'), |
| 138 | 'new_value_class' => 'db-preview-placeholder-md', |
| 139 | ], |
| 140 | [ |
| 141 | 'search' => ['source_id' => 'view_rows'], |
| 142 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View rows'), |
| 143 | 'new_value_class' => 'db-preview-placeholder-lg', |
| 144 | ], |
| 145 | [ |
| 146 | 'search' => ['source_id' => 'view_attachment_after'], |
| 147 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment after'), |
| 148 | 'new_value_class' => 'db-preview-placeholder-md', |
| 149 | ], |
| 150 | [ |
| 151 | 'search' => ['source_id' => 'view_pager'], |
| 152 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View pager'), |
| 153 | ], |
| 154 | [ |
| 155 | 'search' => ['source_id' => 'view_more'], |
| 156 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View more'), |
| 157 | ], |
| 158 | [ |
| 159 | 'search' => ['source_id' => 'view_footer'], |
| 160 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View footer'), |
| 161 | 'new_value_class' => 'db-preview-placeholder-md', |
| 162 | ], |
| 163 | [ |
| 164 | 'search' => ['source_id' => 'view_feed_icons'], |
| 165 | 'new_value_title' => new TranslatableMarkup('[Placeholder] View feed icons'), |
| 166 | ], |
| 167 | ]; |
| 168 | |
| 169 | DisplayBuilderHelpers::findAndReplaceInArray($data, $replacements); |
| 170 | } |
| 68 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 69 | if (empty($data)) { |
| 70 | return []; |
| 68 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 69 | if (empty($data)) { |
| 73 | $this->alterPreviewPlaceholder($data); |
| 74 | |
| 75 | $returned = []; |
| 76 | |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 94 | } |
| 95 | |
| 96 | return $returned; |
| 97 | } |
| 68 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 69 | if (empty($data)) { |
| 73 | $this->alterPreviewPlaceholder($data); |
| 74 | |
| 75 | $returned = []; |
| 76 | |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 94 | } |
| 95 | |
| 96 | return $returned; |
| 97 | } |
| 68 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 69 | if (empty($data)) { |
| 73 | $this->alterPreviewPlaceholder($data); |
| 74 | |
| 75 | $returned = []; |
| 76 | |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 94 | } |
| 95 | |
| 96 | return $returned; |
| 97 | } |
| 68 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 69 | if (empty($data)) { |
| 73 | $this->alterPreviewPlaceholder($data); |
| 74 | |
| 75 | $returned = []; |
| 76 | |
| 77 | foreach ($data as $slot) { |
| 77 | foreach ($data as $slot) { |
| 78 | $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []); |
| 79 | $build = $build['#slots']['content'][0] ?? []; |
| 80 | |
| 81 | // Some sources (e.g. a comment field's "Add comment" form) throw once |
| 82 | // actually rendered rather than producing empty markup - @see |
| 83 | // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike |
| 84 | // BuilderPanel, which already guards every source it builds the same |
| 85 | // way, nothing here previously checked this, so a throwing source |
| 86 | // shipped its #lazy_builder unresolved into the page, breaking Drupal |
| 87 | // core's own BigPipe processing of unrelated placeholders (e.g. the |
| 88 | // Navigation module's admin toolbar) later in the same request. |
| 89 | if ($this->isRenderEmptyOrFailing($this->renderer, $build)) { |
| 90 | $build = $this->buildPlaceholder($this->t('[Placeholder] No preview')); |
| 91 | } |
| 92 | |
| 93 | $returned[] = $build; |
| 94 | } |
| 95 | |
| 96 | return $returned; |
| 97 | } |
| 47 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 48 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 49 | $instance->componentElementBuilder = $container->get('ui_patterns.component_element_builder'); |
| 50 | $instance->renderer = $container->get('renderer'); |
| 51 | |
| 52 | return $instance; |
| 53 | } |
| 60 | 'key' => 'p', |
| 61 | 'help' => t('Show the preview'), |
| 62 | ]; |
| 63 | } |