Initial standalone configurable views pane module

This commit is contained in:
2026-03-04 15:06:41 +11:00
commit f4e6824181
5 changed files with 259 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# Configurable Views Pane
Standalone Drupal module that provides one block plugin for Page Manager / Panels variants:
- Plugin ID: `booking_configurable_views_pane`
- Form fields: `view_id`, `display_id`, `arguments`
It lets editors set view target and contextual arguments per block instance, similar to legacy Drupal 7 pane behavior.
## Install
1. Place this module at:
`web/modules/custom/configurable_views_pane`
2. Enable module:
`drush en configurable_views_pane -y`
3. Rebuild cache:
`drush cr`
## Use in Page Manager
Add block **Configurable Views Pane** and set:
- **View ID**: e.g. `audio_resource_embedded_player`
- **Display ID**: e.g. `default` or `block_1`
- **Contextual arguments**: e.g. `1770` (comma/pipe/newline separated)
## Notes
- This module intentionally keeps plugin id `booking_configurable_views_pane` for compatibility with any existing block config already using that id.

9
composer.json Normal file
View File

@@ -0,0 +1,9 @@
{
"name": "custom/configurable_views_pane",
"description": "Drupal module providing configurable views pane block for Page Manager.",
"type": "drupal-module",
"license": "proprietary",
"require": {
"drupal/core": "^10 || ^11"
}
}

View File

@@ -0,0 +1,7 @@
name: Configurable Views Pane
type: module
description: 'Reusable block plugin that lets editors configure view_id/display/arguments per Page Manager block instance.'
package: Custom
core_version_requirement: ^10 || ^11
dependencies:
- drupal:views

View File

@@ -0,0 +1,213 @@
<?php
namespace Drupal\configurable_views_pane\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\views\Views;
/**
* Configurable Views block for Page Manager / Panels variants.
*
* @Block(
* id = "booking_configurable_views_pane",
* admin_label = @Translation("Configurable Views Pane"),
* category = @Translation("Custom")
* )
*/
final class ConfigurableViewsPaneBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
return [
'view_id' => '',
'display_id' => '',
'arguments' => '',
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account): AccessResult {
$view_id = $this->normalizedViewId((string) ($this->configuration['view_id'] ?? ''));
$display_id = trim((string) ($this->configuration['display_id'] ?? ''));
if ($view_id === '' || $display_id === '') {
return AccessResult::forbidden();
}
$view = Views::getView($view_id);
if (!$view || !$this->displayExists($view_id, $display_id)) {
return AccessResult::forbidden();
}
$result = AccessResult::allowedIf($view->access($display_id, $account));
if ($view->storage) {
$result = $result->addCacheableDependency($view->storage);
}
return $result;
}
/**
* {@inheritdoc}
*/
public function build(): array {
$view_id = $this->normalizedViewId((string) ($this->configuration['view_id'] ?? ''));
$display_id = trim((string) ($this->configuration['display_id'] ?? ''));
if ($view_id === '' || $display_id === '') {
return [];
}
$view = Views::getView($view_id);
if (!$view || !$this->displayExists($view_id, $display_id)) {
return [];
}
$args = $this->parseArguments((string) ($this->configuration['arguments'] ?? ''));
$build = $view->buildRenderable($display_id, $args, FALSE);
if (!is_array($build)) {
return [];
}
$build['#attributes']['class'][] = 'configurable-views-pane-block';
return $build;
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state): array {
$form = parent::blockForm($form, $form_state);
$form['view_id'] = [
'#type' => 'textfield',
'#title' => $this->t('View ID'),
'#default_value' => (string) ($this->configuration['view_id'] ?? ''),
'#required' => TRUE,
'#description' => $this->t('Machine name of the view. Example: audio_resource_embedded_player'),
];
$form['display_id'] = [
'#type' => 'textfield',
'#title' => $this->t('Display ID'),
'#default_value' => (string) ($this->configuration['display_id'] ?? ''),
'#required' => TRUE,
'#description' => $this->t('Display machine name. Example: block_1, page_1, block_nid_1770'),
];
$form['arguments'] = [
'#type' => 'textarea',
'#title' => $this->t('Contextual arguments'),
'#default_value' => (string) ($this->configuration['arguments'] ?? ''),
'#rows' => 2,
'#description' => $this->t('Optional contextual arguments. Separate multiple values with comma, pipe, or newline. Example: 1770'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, FormStateInterface $form_state): void {
parent::blockValidate($form, $form_state);
$view_id = $this->normalizedViewId((string) $form_state->getValue('view_id'));
$display_id = trim((string) $form_state->getValue('display_id'));
if ($view_id === '') {
$form_state->setErrorByName('view_id', $this->t('View ID is required.'));
return;
}
if ($display_id === '') {
$form_state->setErrorByName('display_id', $this->t('Display ID is required.'));
return;
}
$view = Views::getView($view_id);
if (!$view) {
$form_state->setErrorByName('view_id', $this->t('View %view was not found.', ['%view' => $view_id]));
return;
}
if (!$this->displayExists($view_id, $display_id)) {
$displays = implode(', ', $this->displayIds($view_id));
$form_state->setErrorByName(
'display_id',
$this->t('Display %display does not exist on view %view. Available displays: %available', [
'%display' => $display_id,
'%view' => $view_id,
'%available' => $displays !== '' ? $displays : '<none>',
])
);
}
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state): void {
parent::blockSubmit($form, $form_state);
$this->configuration['view_id'] = $this->normalizedViewId((string) $form_state->getValue('view_id'));
$this->configuration['display_id'] = trim((string) $form_state->getValue('display_id'));
$this->configuration['arguments'] = trim((string) $form_state->getValue('arguments'));
}
/**
* Checks whether a display exists on a view.
*/
private function displayExists(string $view_id, string $display_id): bool {
return in_array($display_id, $this->displayIds($view_id), TRUE);
}
/**
* Returns known display IDs for a view.
*
* @return string[]
* Display IDs.
*/
private function displayIds(string $view_id): array {
$view = Views::getView($view_id);
if (!$view || !$view->storage) {
return [];
}
$definitions = (array) ($view->storage->get('display') ?? []);
return array_values(array_filter(array_map(static function ($id) {
return is_scalar($id) ? trim((string) $id) : '';
}, array_keys($definitions)), static function ($id) {
return $id !== '';
}));
}
/**
* Parses arguments from a config string.
*
* @return string[]
* Clean argument list.
*/
private function parseArguments(string $raw): array {
$raw = trim($raw);
if ($raw === '') {
return [];
}
$parts = preg_split('/[\r\n,\|]+/', $raw) ?: [];
return array_values(array_filter(array_map(static function ($item) {
return trim((string) $item);
}, $parts), static function ($item) {
return $item !== '';
}));
}
/**
* Normalizes view machine names.
*/
private function normalizedViewId(string $value): string {
$value = strtolower(trim($value));
return preg_replace('/[^a-z0-9_]+/', '_', $value) ?: '';
}
}