Skip to content

Instantly share code, notes, and snippets.

@anotherjames
Last active October 21, 2024 09:38
Show Gist options
  • Save anotherjames/bcb7ba55ec56359240b26d322fe2f5a5 to your computer and use it in GitHub Desktop.
Save anotherjames/bcb7ba55ec56359240b26d322fe2f5a5 to your computer and use it in GitHub Desktop.
Config form from schema
block.settings.mymodule_my_schema_form_block:
type: block_settings
label: 'MySchemaFormBlock block settings'
mapping:
svcid:
type: string
label: 'Service ID'
constraints:
Regex:
pattern: '/^[a-zA-Z0-9_\-]+$/'
message: "The Service ID can only contain simple letters, numbers, underscores or hyphens."
# The following two properties are custom.
default: 'abcde'
locked: true
envid:
type: string
label: 'Environment ID'
constraints:
Regex:
pattern: '/^[a-zA-Z0-9_\-]+$/'
message: "The Environment ID can only contain simple letters, numbers, underscores or hyphens."
# This is a custom property.
default: 'x-j9WsahRe_1An51DhErab-C'
<?php
namespace Drupal\MYMODULE\Plugin\Block;
use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a 'MySchemaFormBlock' embed widget block.
*/
#[Block(
id: "mymodule_my_schema_form_block",
admin_label: new TranslatableMarkup("MyBlock embed widget")
)]
class MySchemaFormBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfigManager;
/**
* Constructs a new MySchemaFormBlock.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
* The typed config manager.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
TypedConfigManagerInterface $typed_config_manager
) {
// Typed config manager must be set first, because the parent constructor
// will set configuration, which involves pulling defaults from the schema.
$this->typedConfigManager = $typed_config_manager;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('config.typed')
);
}
/**
* Return the configurable parameters for this donation widget.
*/
protected function getConfigurables(): array {
$specific_schema = $this->typedConfigManager->getDefinition('block.settings.' . $this->getBaseId());
$parent_schema = $this->typedConfigManager->getDefinition('block_settings');
$own_properties = array_diff_key(
$specific_schema['mapping'],
$parent_schema['mapping']
);
return $own_properties;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'label_display' => FALSE,
] + array_map(function ($schema_info) {
return $schema_info['default'] ?? NULL;
}, $this->getConfigurables());
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
foreach ($this->getConfigurables() as $key => $schema_info) {
$form[$key] = [
// Explicit support for certain types only. This is a bit like what
// config_translation_config_schema_info_alter() helps achieve.
'#type' => match ($schema_info['type']) {
'string', 'label' => 'textfield',
'text' => 'textarea',
'boolean' => 'checkbox',
'integer', 'float' => 'number',
'email' => 'email',
},
'#title' => $schema_info['label'],
'#default_value' => $this->configuration[$key],
'#required' => empty($schema_info['nullable']),
// The 'locked' property is not specified in core.
'#disabled' => $schema_info['locked'] ?? FALSE,
];
// Convert general regex in schema into whole-string regex for HTML.
if (!empty($schema_info['constraints']['Regex']['pattern'])) {
$pattern = $schema_info['constraints']['Regex']['pattern'];
// Extract the pattern within delimiters, ignore any regex modifiers,
// and strip off string start & end characters. The HTML pattern spec
// does not cater for any of these.
$delimiter = $pattern[0];
$last_delimiter_position = strrpos($pattern, $delimiter);
$pattern = substr($pattern, 1, $last_delimiter_position - 1);
if (str_starts_with($pattern, '^')) {
$pattern = substr($pattern, 1);
}
if (
str_ends_with($pattern, '$') &&
// Some basic detection of escaped dollar signs at the end.
(!str_ends_with($pattern, '\$') || str_ends_with($pattern, '\\\\\$'))
) {
$pattern = substr($pattern, 0, -1);
}
$form[$key]['#pattern'] = $pattern;
if (!empty($schema_info['constraints']['Regex']['message'])) {
$form[$key]['#attributes']['title'] = $schema_info['constraints']['Regex']['message'];
}
}
}
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
foreach (array_keys($this->getConfigurables()) as $key) {
$this->configuration[$key] = $form_state->getValue($key);
}
}
/**
* {@inheritdoc}
*/
public function build() {
foreach ($this->getConfigurables() as $key => $schema_info) {
$params[$key] = $this->configuration[$key] ?? $schema_info['default'] ?? '';
}
// Double-check parameters are safe (though this should have been enforced
// by the config schema).
$params = array_map(function ($value) {
return preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
}, $params);
$build = [
'placeholder' => [
'#type' => 'container',
],
'loader' => [
'#type' => 'html_tag',
'#tag' => 'script',
'#attributes' => [
'src' => 'https://example.com/embed-script-thing.js',
],
],
];
$imploded_params = "'" . implode("', '", $params) . "'";
$build['initialiser'] = [
'#type' => 'html_tag',
'#tag' => 'script',
'#value' => 'SomeGlobal.someMethod(' . $imploded_params . ')',
];
return $build;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment