devel_wizard-2.x-dev/src/Plugin/DevelWizard/Spell/LayoutSpell.php
src/Plugin/DevelWizard/Spell/LayoutSpell.php
<?php
declare(strict_types=1);
namespace Drupal\devel_wizard\Plugin\DevelWizard\Spell;
use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ProfileExtensionList;
use Drupal\Core\Extension\ThemeExtensionList;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\devel_wizard\Attribute\DevelWizardSpell;
use Drupal\devel_wizard\Utils;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
#[DevelWizardSpell(
id: 'devel_wizard_layout',
category: new TranslatableMarkup('Code'),
label: new TranslatableMarkup('Layout'),
description: new TranslatableMarkup('Generates a new Layout.'),
tags: [
'code' => new TranslatableMarkup('Code'),
'Layout' => new TranslatableMarkup('Layout'),
],
)]
class LayoutSpell extends SpellBase implements
PluginFormInterface,
ConfigurableInterface,
ContainerFactoryPluginInterface {
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('messenger'),
$container->get('logger.channel.devel_wizard_spell'),
$container->get('string_translation'),
$container->get('devel_wizard.utils'),
$container->get('config.factory'),
$container->get('extension.list.module'),
$container->get('extension.list.theme'),
$container->get('extension.list.profile'),
$container->get('twig'),
new Filesystem(),
);
}
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MessengerInterface $messenger,
LoggerInterface $logger,
TranslationInterface $stringTranslation,
Utils $utils,
ConfigFactoryInterface $configFactory,
protected ModuleExtensionList $moduleList,
protected ThemeExtensionList $themeList,
protected ProfileExtensionList $profileList,
protected TwigEnvironment $twig,
protected Filesystem $fs,
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$messenger,
$logger,
$stringTranslation,
$utils,
$configFactory,
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
return [
'extension' => [
'machineName' => '',
],
'layout' => [
'id' => '',
],
];
}
protected function populateCalculatedConfigurationValues(): static {
parent::populateCalculatedConfigurationValues();
if (empty($this->configuration['extension']['machineName'])) {
throw new \InvalidArgumentException(sprintf(
'%s can not be empty',
'extension.machineName',
));
}
if (empty($this->configuration['layout']['id'])) {
throw new \InvalidArgumentException(sprintf(
'%s can not be empty',
'layout.id',
));
}
$extensionType = $this->getExtensionType($this->configuration['extension']['machineName']);
$extension = match ($extensionType) {
'module' => $this->moduleList->get($this->configuration['extension']['machineName']),
'theme' => $this->themeList->get($this->configuration['extension']['machineName']),
'profile' => $this->profileList->get($this->configuration['extension']['machineName']),
default => NULL,
};
$this->configuration['baseDir'] = $extension->getPath();
$this->configuration['extension'] += $this->utils->stringVariants(
$this->configuration['extension']['machineName'],
'machineName',
);
$this->configuration['layout'] += $this->utils->stringVariants(
$this->configuration['layout']['id'],
'id',
);
$this->configuration['layout']['idShort'] = preg_replace(
'/^' . preg_quote($this->configuration['extension']['machineName'], '/') . '_/',
'',
$this->configuration['layout']['id'],
);
$this->configuration['layout'] += $this->utils->stringVariants(
$this->configuration['layout']['idShort'],
'idShort',
);
$this->configuration['template']['fileBaseName'] = sprintf(
'%s-layout-%s',
$this->configuration['extension']['machineNameLowerDash'],
$this->configuration['layout']['idShortLowerDash'],
);
$this->configuration['template']['filePath'] = Path::join(
$this->configuration['baseDir'],
'templates',
'layout',
"{$this->configuration['template']['fileBaseName']}.html.twig",
);
$this->configuration['class'] = [
'name' => $this->configuration['layout']['idShortUpperCamel'],
'namespace' => "Drupal\\{$this->configuration['extension']['machineName']}\\Plugin\\Layout",
];
$this->configuration['class'] += [
'fqn' => "{$this->configuration['class']['namespace']}\\{$this->configuration['class']['name']}",
'filePath' => Path::join(
$this->configuration['baseDir'],
'src',
'Plugin',
'Layout',
$this->configuration['class']['name'] . '.php',
),
];
$this->configuration['css'] = [
'filePathRelative' => Path::join(
'css',
'layout',
"{$this->configuration['layout']['idShort']}.css",
),
];
$this->configuration['css']['filePath'] = Path::join(
$this->configuration['baseDir'],
$this->configuration['css']['filePathRelative'],
);
return $this;
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
$conf = $this->getConfiguration();
$form['#attributes']['class'][] = 'dws--layout';
$form['#attached']['library'][] = 'devel_wizard/spell.layout';
$form['extension'] = [
'#type' => 'details',
'#tree' => TRUE,
'#title' => $this->t('Extension'),
'#open' => TRUE,
'machineName' => [
'#type' => 'textfield',
'#title' => $this->t('Machine-name'),
'#default_value' => $conf['extension']['machineName'],
'#description' => $this->t('Machine-name of the extension (module, theme, profile) to put the new code files into.'),
'#autocomplete_route_name' => 'devel_wizard.autocomplete.extension_all',
],
];
$form['layout'] = [
'#type' => 'details',
'#tree' => TRUE,
'#title' => $this->t('Layout'),
'#open' => TRUE,
'id' => [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Identifier of the layout'),
'#default_value' => $conf['layout']['id'],
'#field_prefix' => '<extension.machineName>_',
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
// @todo Implement validateConfigurationForm() method.
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {
$values = $form_state->getValue($form['#parents'], []);
$values['layout']['id'] = sprintf(
'%s_%s',
$values['extension']['machineName'],
$values['layout']['id'],
);
$this->setConfiguration($values);
}
protected function getExtensionType(string $machineName): ?string {
if ($this->moduleList->exists($machineName)) {
return 'module';
}
if ($this->themeList->exists($machineName)) {
return 'theme';
}
if ($this->profileList->exists($machineName)) {
return 'profile';
}
return NULL;
}
/**
* @throws \Twig\Error\Error
*/
protected function doIt(): static {
$this->doItCode();
drupal_flush_all_caches();
$this->batchContext['finished'] = 1.0;
return $this;
}
/**
* @throws \Twig\Error\Error
*/
protected function doItCode(): static {
$librariesFilePath = Path::join(
$this->configuration['baseDir'],
"{$this->configuration['extension']['machineName']}.libraries.yml",
);
$cssFilePathRelative = Path::join(
'css',
'layout',
"{$this->configuration['layout']['idShort']}.css",
);
$this->utils->ymlFileReplace(
$librariesFilePath,
[
"layout.{$this->configuration['layout']['idShort']}" => [
'css' => [
'layout' => [
$cssFilePathRelative => [],
],
],
],
],
);
$schemeFilePath = Path::join(
$this->configuration['baseDir'],
'config',
'scheme',
"{$this->configuration['extension']['machineName']}.scheme.yml",
);
$this->utils->ymlFileReplace(
$schemeFilePath,
[
"layout_plugin.settings.{$this->configuration['layout']['id']}" => [
'type' => 'layout_builder_multi_width',
],
],
);
foreach ($this->getTwigFiles() as $dstFilePath => $template) {
$dstFileContent = $this->twig->render(
$template,
$this->configuration,
);
$this->fs->mkdir(Path::getDirectory($dstFilePath));
$this->fs->dumpFile($dstFilePath, $dstFileContent);
$this->messageFilesystemEntryCreate($dstFilePath);
}
return $this;
}
protected function getTwigFiles(): array {
return [
$this->configuration['class']['filePath'] => '@devel_wizard/spell/layout/class.php.twig',
$this->configuration['css']['filePath'] => '@devel_wizard/spell/layout/library.css.twig',
$this->configuration['template']['filePath'] => '@devel_wizard/spell/layout/template.twig.twig',
];
}
}
