Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -0,0 +1,61 @@
<?php
namespace DrupalCodeGenerator;
use DrupalCodeGenerator\Helper\Dumper;
use DrupalCodeGenerator\Helper\InputHandler;
use DrupalCodeGenerator\Helper\OutputHandler;
use DrupalCodeGenerator\Helper\Renderer;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Filesystem\Filesystem;
/**
* DCG application factory.
*/
class ApplicationFactory {
/**
* Determines path to DCG root directory.
*
* @return string
* Path to DCG root directory.
*/
public static function getRoot() {
return dirname(__DIR__);
}
/**
* Creates an application.
*
* @return \Symfony\Component\Console\Application
* The initialized console application.
*/
public static function create() {
// This gets substituted with git version when DCG is packaged to PHAR file.
$version = '@git-version@';
// Fallback for composer installation.
if (!is_numeric($version[0])) {
$version = 'UNKNOWN';
}
$application = new Application('Drupal Code Generator', $version);
$helper_set = new HelperSet([
new QuestionHelper(),
new Dumper(new Filesystem()),
// We cannot reference the TwigEnvironment class with use statement
// because of a PHP bug.
// @see https://bugs.php.net/bug.php?id=66773
// @codingStandardsIgnoreStart
new Renderer(new \DrupalCodeGenerator\Twig\TwigEnvironment(new \Twig_Loader_Filesystem())),
// @codingStandardsIgnoreEnd
new InputHandler(),
new OutputHandler(),
]);
$application->setHelperSet($helper_set);
return $application;
}
}

View file

@ -0,0 +1,320 @@
<?php
namespace DrupalCodeGenerator;
use DrupalCodeGenerator\Helper\Renderer;
/**
* Simple data structure to represent an asset being generated.
*/
class Asset {
/**
* Asset path.
*
* @var string
*/
protected $path;
/**
* Asset content.
*
* @var string
*/
protected $content;
/**
* Twig template to render header.
*
* @var string
*/
protected $headerTemplate;
/**
* Twig template to render main content.
*
* @var string
*/
protected $template;
/**
* Template variables.
*
* @var array
*/
protected $vars = [];
/**
* Action.
*
* This defines an action to take if specified file already exists.
*
* @var string
*/
protected $action = 'replace';
/**
* Header size.
*
* @var int
*/
protected $headerSize = 0;
/**
* Asset mode.
*
* @var int
*/
protected $mode;
/**
* Asset type (file or directory).
*
* @var string
*/
protected $type = 'file';
/**
* Getter for asset path.
*
* @return string
* Asset path.
*/
public function getPath() {
return Utils::tokenReplace($this->path, $this->getVars());
}
/**
* Getter for asset content.
*
* @return string
* Asset content.
*/
public function getContent() {
return $this->content;
}
/**
* Getter for header template.
*
* @return string
* Asset header template.
*/
public function getHeaderTemplate() {
return $this->headerTemplate;
}
/**
* Getter for template.
*
* @return string
* Asset template.
*/
public function getTemplate() {
return $this->template;
}
/**
* Getter for asset vars.
*
* @return array
* Asset template variables.
*/
public function getVars() {
return $this->vars;
}
/**
* Getter for asset action.
*
* @return string
* Asset action.
*/
public function getAction() {
return $this->action;
}
/**
* Getter for asset header size.
*
* @return string
* Asset header size.
*/
public function getHeaderSize() {
return $this->headerSize;
}
/**
* Getter for asset mode.
*
* @return string
* Asset file mode.
*/
public function getMode() {
return $this->mode ?: ($this->isDirectory() ? 0755 : 0644);
}
/**
* Getter for asset type.
*
* @return string
* Asset type.
*/
public function getType() {
return $this->type;
}
/**
* Setter for asset path.
*
* @param string $path
* Asset path.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function path($path) {
$this->path = $path;
return $this;
}
/**
* Setter for asset content.
*
* @param string $content
* Asset content.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function content($content) {
$this->content = $content;
return $this;
}
/**
* Setter for asset header template.
*
* @param string $header_template
* Asset template.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function headerTemplate($header_template) {
$this->headerTemplate = $header_template;
return $this;
}
/**
* Setter for asset template.
*
* @param string $template
* Asset template.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function template($template) {
$this->template = $template;
return $this;
}
/**
* Setter for asset vars.
*
* @param array $vars
* Asset template variables.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function vars(array $vars) {
$this->vars = $vars;
return $this;
}
/**
* Setter for asset action.
*
* @param string $action
* Asset action.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function action($action) {
$this->action = $action;
return $this;
}
/**
* Setter for asset header size.
*
* @param int $header_size
* Asset header size.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function headerSize($header_size) {
$this->headerSize = $header_size;
return $this;
}
/**
* Setter for asset mode.
*
* @param string $mode
* Asset mode.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function mode($mode) {
$this->mode = $mode;
return $this;
}
/**
* Setter for asset type.
*
* @param string $type
* Asset type.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
public function type($type) {
$this->type = $type;
return $this;
}
/**
* Determines if the asset is a directory.
*
* @return bool
* True if the asset is a directory, false otherwise.
*/
public function isDirectory() {
return $this->getType() == 'directory';
}
/**
* Renders the asset template.
*
* @param \DrupalCodeGenerator\Helper\Renderer $renderer
* Renderer helper.
*/
public function render(Renderer $renderer) {
if (!$this->isDirectory() && is_null($this->getContent())) {
$content = '';
if ($header_template = $this->getHeaderTemplate()) {
$content .= $renderer->render($header_template, $this->getVars()) . "\n";
}
$content .= $renderer->render($this->getTemplate(), $this->getVars());
$this->content($content);
}
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace DrupalCodeGenerator\Box;
use Herrera\Box\Compactor\Compactor;
/**
* A PHP source code compactor.
*/
class PhpCompactor extends Compactor {
/**
* {@inheritdoc}
*/
protected $extensions = ['php'];
/**
* {@inheritdoc}
*/
public function compact($contents) {
// php_strip_whitespace() takes file name as argument so we have to save the
// contents to a temporary file.
$temp_file = tempnam(sys_get_temp_dir(), 'dcg-');
file_put_contents($temp_file, $contents);
$contents = php_strip_whitespace($temp_file);
unlink($temp_file);
return $contents;
}
}

View file

@ -0,0 +1,455 @@
<?php
namespace DrupalCodeGenerator\Command;
use DrupalCodeGenerator\ApplicationFactory;
use DrupalCodeGenerator\Asset;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Base class for all generators.
*/
abstract class BaseGenerator extends Command implements GeneratorInterface {
/**
* The command name.
*
* @var string
*/
protected $name;
/**
* The command description.
*
* @var string
*/
protected $description;
/**
* The command alias.
*
* @var string
*/
protected $alias;
/**
* Command label.
*
* @var string
*/
protected $label;
/**
* A path where templates are stored.
*
* @var string
*/
protected $templatePath;
/**
* The working directory.
*
* @var string
*/
protected $directory;
/**
* The destination.
*
* @var mixed
*/
protected $destination = 'modules/%';
/**
* Files to create.
*
* The key of the each item in the array is the path to the file and
* the value is the generated content of it.
*
* @var array
*
* @deprecated Use self::$assets.
*/
protected $files = [];
/**
* Assets to create.
*
* @var \DrupalCodeGenerator\Asset[]
*/
protected $assets = [];
/**
* Twig template variables.
*
* @var array
*/
protected $vars = [];
/**
* {@inheritdoc}
*/
protected function configure() {
$this
->setName($this->name)
->setDescription($this->description)
->addOption(
'directory',
'-d',
InputOption::VALUE_OPTIONAL,
'Working directory'
)
->addOption(
'answers',
'-a',
InputOption::VALUE_OPTIONAL,
'Default JSON formatted answers'
);
if ($this->alias) {
$this->setAliases([$this->alias]);
}
if (!$this->templatePath) {
$this->templatePath = ApplicationFactory::getRoot() . '/templates';
}
}
/**
* {@inheritdoc}
*/
protected function initialize(InputInterface $input, OutputInterface $output) {
$this->getHelperSet()->setCommand($this);
$this->getHelper('dcg_renderer')->addPath($this->templatePath);
$directory = $input->getOption('directory') ?: getcwd();
// No need to look up for extension root when generating an extension.
$extension_destinations = ['modules', 'profiles', 'themes'];
$is_extension = in_array($this->destination, $extension_destinations);
$this->directory = $is_extension
? $directory : (Utils::getExtensionRoot($directory) ?: $directory);
// Display welcome message.
$header = sprintf(
"\n Welcome to %s generator!",
$this->getName()
);
$output->writeln($header);
$header_length = strlen(trim(strip_tags($header)));
$output->writeln('<fg=cyan;options=bold>' . str_repeat('', $header_length) . '</>');
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output) {
// Render all assets.
$renderer = $this->getHelper('dcg_renderer');
foreach ($this->getAssets() as $asset) {
// Supply the asset with all collected variables if it has no local ones.
if (!$asset->getVars()) {
$asset->vars($this->vars);
}
$asset->render($renderer);
}
$dumped_files = $this->getHelper('dcg_dumper')->dump($input, $output);
$this->getHelper('dcg_output_handler')->printSummary($output, $dumped_files);
return 0;
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label;
}
/**
* Returns list of rendered files.
*
* @return array
* An associative array where each key is path to a file and value is
* rendered content.
*
* @deprecated.
*/
public function getFiles() {
return $this->files;
}
/**
* {@inheritdoc}
*/
public function getAssets() {
if ($this->files) {
// Convert files into assets for legacy commands.
$assets = [];
foreach ($this->getFiles() as $path => $file) {
$asset = new Asset();
$asset->path($path);
if (!is_array($file)) {
$file = ['content' => $file];
}
if (isset($file['content'])) {
$asset->content($file['content']);
}
else {
$asset->type('directory');
}
if (isset($file['action'])) {
$asset->action($file['action']);
}
if (isset($file['header_size'])) {
$asset->headerSize($file['header_size']);
}
if (isset($file['mode'])) {
$asset->mode($file['mode']);
}
$assets[] = $asset;
}
return array_merge($assets, $this->assets);
}
return $this->assets;
}
/**
* {@inheritdoc}
*/
public function setDirectory($directory) {
$this->directory = $directory;
}
/**
* {@inheritdoc}
*/
public function getDirectory() {
return $this->directory;
}
/**
* {@inheritdoc}
*/
public function setDestination($destination) {
$this->destination = $destination;
}
/**
* {@inheritdoc}
*/
public function getDestination() {
return $this->destination;
}
/**
* Renders a template.
*
* @param string $template
* Twig template.
* @param array $vars
* Template variables.
*
* @return string
* A string representing the rendered output.
*/
protected function render($template, array $vars) {
return $this->getHelper('dcg_renderer')->render($template, $vars);
}
/**
* Asks the user for template variables.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* Input instance.
* @param \Symfony\Component\Console\Output\OutputInterface $output
* Output instance.
* @param array $questions
* List of questions that the user should answer.
* @param array $vars
* Array of predefined template variables.
*
* @return array
* Template variables.
*
* @see \DrupalCodeGenerator\InputHandler::collectVars()
*/
protected function &collectVars(InputInterface $input, OutputInterface $output, array $questions, array $vars = []) {
$this->vars += $this->getHelper('dcg_input_handler')->collectVars($input, $output, $questions, $vars);
return $this->vars;
}
/**
* Asks the user a single question and returns the answer.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* Input instance.
* @param \Symfony\Component\Console\Output\OutputInterface $output
* Output instance.
* @param \Symfony\Component\Console\Question\Question $question
* A question to ask.
* @param array $vars
* Array of predefined template variables.
*
* @return string
* The answer.
*
* @see \DrupalCodeGenerator\InputHandler::collectVars()
*/
protected function ask(InputInterface $input, OutputInterface $output, Question $question, array $vars = []) {
$answers = $this->getHelper('dcg_input_handler')->collectVars($input, $output, ['key' => $question], $vars);
return $answers['key'];
}
/**
* Creates an asset.
*
* @param string $type
* Asset type.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
protected function addAsset($type) {
$asset = (new Asset())->type($type);
$this->assets[] = $asset;
return $asset;
}
/**
* Creates file asset.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
protected function addFile() {
return $this->addAsset('file');
}
/**
* Creates directory asset.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
protected function addDirectory() {
return $this->addAsset('directory');
}
/**
* Creates service file asset.
*
* @return \DrupalCodeGenerator\Asset
* The asset.
*/
protected function addServicesFile() {
return $this->addFile()
->path('{machine_name}.services.yml')
->action('append')
->headerSize(1);
}
/**
* Creates file asset.
*
* @param string $path
* Path to the file.
* @param string $template
* Twig template to render.
* @param array $vars
* Twig variables.
*
* @deprecated Use self::addFile() or self::addDirectory().
*/
protected function setFile($path = NULL, $template = NULL, array $vars = []) {
$this->addFile()
->path($path)
->template($template)
->vars($vars);
}
/**
* Creates service file asset.
*
* @param string $path
* Path to the file.
* @param string $template
* Twig template to render.
* @param array $vars
* Twig variables.
*
* @deprecated Use self::addServiceFile().
*/
protected function setServicesFile($path, $template, array $vars) {
$this->addServicesFile()
->path($path)
->template($template)
->vars($vars);
}
/**
* Collects services.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* Input instance.
* @param \Symfony\Component\Console\Output\OutputInterface $output
* Output instance.
*
* @return array
* List of collected services.
*/
protected function collectServices(InputInterface $input, OutputInterface $output) {
$service_definitions = self::getServiceDefinitions();
$service_ids = array_keys($service_definitions);
$services = [];
while (TRUE) {
$question = new Question('Type the service name or use arrows up/down. Press enter to continue');
$question->setValidator([Utils::class, 'validateServiceName']);
$question->setAutocompleterValues($service_ids);
$service = $this->ask($input, $output, $question);
if (!$service) {
break;
}
$services[] = $service;
}
$this->vars['services'] = [];
foreach (array_unique($services) as $service_id) {
if (isset($service_definitions[$service_id])) {
$definition = $service_definitions[$service_id];
}
else {
// Build the definition if the service is unknown.
$definition = [
'type' => 'Drupal\example\ExampleInterface',
'name' => str_replace('.', '_', $service_id),
'description' => "The $service_id service.",
];
}
$type_parts = explode('\\', $definition['type']);
$definition['short_type'] = end($type_parts);
$this->vars['services'][$service_id] = $definition;
}
return $this->vars['services'];
}
/**
* Gets service definitions.
*
* @return array
* List of service definitions keyed by service ID.
*/
protected static function getServiceDefinitions() {
$data_encoded = file_get_contents(ApplicationFactory::getRoot() . '/resources/service-definitions.json');
return json_decode($data_encoded, TRUE);
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7\CToolsPlugin;
/**
* Implements d7:ctools-plugin:access command.
*/
class Access extends BasePlugin {
protected $name = 'd7:ctools-plugin:access';
protected $description = 'Generates CTools access plugin';
protected $template = 'd7/ctools-plugin/access.twig';
protected $subDirectory = 'plugins/access';
}

View file

@ -0,0 +1,49 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7\CToolsPlugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Base class for d7:ctools-plugin commands.
*/
abstract class BasePlugin extends BaseGenerator {
protected $template;
protected $subDirectory;
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['plugin_name'] = new Question('Plugin name', 'Example');
$questions['plugin_name']->setValidator([Utils::class, 'validateRequired']);
$default_machine_name = function ($vars) {
return Utils::human2machine($vars['plugin_name']);
};
$questions['plugin_machine_name'] = new Question('Plugin machine name', $default_machine_name);
$questions['plugin_machine_name']->setValidator([Utils::class, 'validateMachineName']);
$questions['description'] = new Question('Plugin description', 'Plugin description.');
$questions['category'] = new Question('Category', 'Custom');
$questions['context'] = new ChoiceQuestion(
'Required context',
['-', 'Node', 'User', 'Term']
);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path($this->subDirectory . '/{plugin_machine_name}.inc')
->template($this->template);
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7\CToolsPlugin;
/**
* Implements d7:ctools-plugin:content-type command.
*/
class ContentType extends BasePlugin {
protected $name = 'd7:ctools-plugin:content-type';
protected $description = 'Generates CTools content type plugin';
protected $template = 'd7/ctools-plugin/content-type.twig';
protected $subDirectory = 'plugins/content_types';
}

View file

@ -0,0 +1,15 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7\CToolsPlugin;
/**
* Implements d7:ctools-plugin:relationship command.
*/
class Relationship extends BasePlugin {
protected $name = 'd7:ctools-plugin:relationship';
protected $description = 'Generates CTools relationship plugin';
protected $template = 'd7/ctools-plugin/relationship.twig';
protected $subDirectory = 'plugins/relationships';
}

View file

@ -0,0 +1,86 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:hook command.
*/
class Hook extends BaseGenerator {
protected $name = 'd7:hook';
protected $description = 'Generates a hook';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['hook_name'] = new Question('Hook name');
$questions['hook_name']->setValidator(function ($value) {
if (!in_array($value, $this->getSupportedHooks())) {
throw new \UnexpectedValueException('The value is not correct class name.');
}
return $value;
});
$questions['hook_name']->setAutocompleterValues($this->getSupportedHooks());
$vars = $this->collectVars($input, $output, $questions);
// Most Drupal hooks are situated in a module file but some are not.
$special_hooks = [
'install' => [
'install',
'uninstall',
'enable',
'disable',
'schema',
'schema_alter',
'field_schema',
'requirements',
'update_N',
'update_last_removed',
],
// See system_hook_info().
'tokens.inc' => [
'token_info',
'token_info_alter',
'tokens',
'tokens_alter',
],
];
$file_type = 'module';
foreach ($special_hooks as $group => $hooks) {
if (in_array($vars['hook_name'], $hooks)) {
$file_type = $group;
break;
}
}
$this->addFile()
->path("{machine_name}.$file_type")
->headerTemplate("d7/file-docs/$file_type.twig")
->template('d7/hook/' . $vars['hook_name'] . '.twig')
->action('append')
->headerSize(7);
}
/**
* Gets list of supported hooks.
*
* @return array
* List of supported hooks.
*/
protected function getSupportedHooks() {
return array_map(function ($file) {
return pathinfo($file, PATHINFO_FILENAME);
}, array_diff(scandir($this->templatePath . '/d7/hook'), ['.', '..']));
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d7:install-file command.
*/
class InstallFile extends BaseGenerator {
protected $name = 'd7:install-file';
protected $description = 'Generates Drupal 7 install file';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$this->collectVars($input, $output, Utils::defaultQuestions());
$this->addFile()
->path('{machine_name}.install')
->template('d7/install.twig');
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d7:javascript command.
*/
class Javascript extends BaseGenerator {
protected $name = 'd7:javascript';
protected $description = 'Generates Drupal 7 JavaScript file';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$vars = $this->collectVars($input, $output, Utils::defaultQuestions());
$this->addFile()
->path(str_replace('_', '-', $vars['machine_name']) . '.js')
->template('d7/javascript.twig');
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:module command.
*/
class Module extends BaseGenerator {
protected $name = 'd7:module';
protected $description = 'Generates Drupal 7 module';
protected $destination = 'modules';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['description'] = new Question('Module description', 'Module description.');
$questions['package'] = new Question('Package', 'Custom');
$vars = $this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}/{machine_name}.info')
->template('d7/module-info.twig');
$this->addFile()
->path('{machine_name}/{machine_name}.module')
->template('d7/module.twig');
$this->addFile()
->path('{machine_name}/{machine_name}.install')
->template('d7/install.twig');
$this->addFile()
->path('{machine_name}/{machine_name}.admin.inc')
->template('d7/admin.inc.twig');
$this->addFile()
->path('{machine_name}/{machine_name}.pages.inc')
->template('d7/pages.inc.twig');
$this->addFile()
->path('{machine_name}/' . str_replace('_', '-', $vars['machine_name']) . '.js')
->template('d7/javascript.twig');
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d7:module-file command.
*/
class ModuleFile extends BaseGenerator {
protected $name = 'd7:module-file';
protected $description = 'Generates Drupal 7 module file';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$this->collectVars($input, $output, Utils::defaultQuestions());
$this->addFile()
->path('{machine_name}.module')
->template('d7/module.twig');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:module-info command.
*/
class ModuleInfo extends BaseGenerator {
protected $name = 'd7:module-info';
protected $description = 'Generates Drupal 7 info file for a module';
protected $label = 'Info (module)';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['description'] = new Question('Module description', 'Module description.');
$questions['package'] = new Question('Package', 'Custom');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.info')
->template('d7/module-info.twig');
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:settings.php command.
*/
class Settings extends BaseGenerator {
protected $name = 'd7:settings.php';
protected $description = 'Generates Drupal 7 settings.php file';
protected $destination = 'sites/default';
protected $label = 'settings.php';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['db_driver'] = new Question('Database driver', 'mysql');
$questions['db_driver']->setAutocompleterValues(['mysql', 'pgsql', 'sqlite']);
$questions['db_name'] = new Question('Database name', 'drupal');
$questions['db_user'] = new Question('Database user', 'root');
$questions['db_password'] = new Question('Database password', '123');
$vars = &$this->collectVars($input, $output, $questions);
// @see: drupal_get_hash_salt()
$vars['hash_salt'] = hash('sha256', serialize($vars));
$this->addFile()
->path('settings.php')
->template('d7/settings.twig');
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:template.php command.
*/
class TemplatePhp extends BaseGenerator {
protected $name = 'd7:template.php';
protected $description = 'Generates Drupal 7 template.php file';
protected $alias = 'template.php';
protected $label = 'template.php';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['name']->setValidator([Utils::class, 'validateRequired']);
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('template.php')
->template('d7/template.php.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:test command.
*/
class Test extends BaseGenerator {
protected $name = 'd7:test';
protected $description = 'Generates Drupal 7 test case';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['machine_name']) . 'TestCase';
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.test')
->template('d7/test.twig');
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:theme command.
*/
class Theme extends BaseGenerator {
protected $name = 'd7:theme';
protected $description = 'Generates Drupal 7 theme';
protected $destination = 'themes';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['name']->setValidator([Utils::class, 'validateRequired']);
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$questions['description'] = new Question('Theme description', 'A simple Drupal 7 theme.');
$questions['base_theme'] = new Question('Base theme');
$vars = &$this->collectVars($input, $output, $questions);
$vars['asset_name'] = str_replace('_', '-', $vars['machine_name']);
$this->addFile()
->path('{machine_name}/{machine_name}.info')
->template('d7/theme-info.twig');
$this->addFile()
->path('{machine_name}/template.php')
->template('d7/template.php.twig');
$this->addFile()
->path('{machine_name}/js/{asset_name}.js')
->template('d7/javascript.twig');
$this->addFile()
->path('{machine_name}/css/{asset_name}.css')
->template('d7/theme-css.twig');
$this->addDirectory()
->path('{machine_name}/templates');
$this->addDirectory()
->path('{machine_name}/images');
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:theme-info command.
*/
class ThemeInfo extends BaseGenerator {
protected $name = 'd7:theme-info';
protected $description = 'Generates info file for a Drupal 7 theme';
protected $label = 'Info (theme)';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['name']->setValidator([Utils::class, 'validateRequired']);
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$questions['description'] = new Question('Theme description', 'A simple Drupal 7 theme.');
$questions['base_theme'] = new Question('Base theme');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.info')
->template('d7/theme-info.twig');
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_7\ViewsPlugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d7:views-plugin:argument-default command.
*/
class ArgumentDefault extends BaseGenerator {
protected $name = 'd7:views-plugin:argument-default';
protected $description = 'Generates Drupal 7 argument default views plugin';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['plugin_name'] = new Question('Plugin name', 'Example');
$default_machine_name = function ($vars) {
return Utils::human2machine($vars['plugin_name']);
};
$questions['plugin_machine_name'] = new Question('Plugin machine name', $default_machine_name);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.module')
->template('d7/views-plugin/argument-default.module.twig')
->action('append')
->headerSize(7);
$this->addFile()
->path('views/{machine_name}.views.inc')
->template('d7/views-plugin/argument-default-views.inc.twig')
->action('append')
->headerSize(7);
$this->addFile()
->path('views/views_plugin_argument_{plugin_machine_name}.inc')
->template('d7/views-plugin/argument-default.twig');
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:composer command.
*/
class Composer extends BaseGenerator {
protected $name = 'd8:composer';
protected $description = 'Generates a composer.json file';
protected $alias = 'composer.json';
protected $label = 'composer.json';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Project machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$questions['description'] = new Question('Description');
$questions['type'] = new Question('Type', 'drupal-module');
$questions['type']->setValidator([Utils::class, 'validateRequired']);
$questions['type']->setAutocompleterValues([
'drupal-module',
'drupal-theme',
'drupal-library',
'drupal-profile',
'drupal-drush',
]);
$questions['drupal_org'] = new ConfirmationQuestion('Is this project hosted on drupal.org?', FALSE);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('composer.json')
->template('d8/composer.twig');
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:controller command.
*/
class Controller extends BaseGenerator {
protected $name = 'd8:controller';
protected $description = 'Generates a controller';
protected $alias = 'controller';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['name']) . 'Controller';
};
$questions['class'] = new Question('Class', $default_class);
$vars = $this->collectVars($input, $output, $questions);
$di_question = new ConfirmationQuestion('Would you like to inject dependencies?', FALSE);
if ($this->ask($input, $output, $di_question)) {
$this->collectServices($input, $output);
}
$route_question = new ConfirmationQuestion('Would you like to create a route for this controller?');
if ($this->ask($input, $output, $route_question)) {
$route_path = '/' . str_replace('_', '-', $vars['machine_name']) . '/example';
$route_questions['route_name'] = new Question('Route name', '{machine_name}.example');
$route_questions['route_path'] = new Question('Route path', $route_path);
$route_questions['route_title'] = new Question('Route title', 'Example');
$route_questions['route_permission'] = new Question('Route permission', 'access content');
$this->collectVars($input, $output, $route_questions, $vars);
$this->addFile()
->path('{machine_name}.routing.yml')
->template('d8/controller-route.twig')
->action('append');
}
$this->addFile()
->path('src/Controller/{class}.php')
->template('d8/controller.twig');
}
}

View file

@ -0,0 +1,320 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:field command.
*/
class Field extends BaseGenerator {
protected $name = 'd8:field';
protected $description = 'Generates a field';
protected $alias = 'field';
/**
* Field sub-types.
*
* @var array
*/
protected $subTypes = [
'boolean' => [
'label' => 'Boolean',
'list' => FALSE,
'random' => FALSE,
'inline' => FALSE,
'link' => FALSE,
'data_type' => 'boolean',
],
'string' => [
'label' => 'Text',
'list' => TRUE,
'random' => TRUE,
'inline' => TRUE,
'link' => FALSE,
'data_type' => 'string',
],
'text' => [
'label' => 'Text (long)',
'list' => FALSE,
'random' => TRUE,
'inline' => FALSE,
'link' => FALSE,
'data_type' => 'string',
],
'integer' => [
'label' => 'Integer',
'list' => TRUE,
'random' => FALSE,
'inline' => TRUE,
'link' => FALSE,
'data_type' => 'integer',
],
'float' => [
'label' => 'Float',
'list' => TRUE,
'random' => FALSE,
'inline' => TRUE,
'link' => FALSE,
'data_type' => 'float',
],
'numeric' => [
'label' => 'Numeric',
'list' => TRUE,
'random' => FALSE,
'inline' => TRUE,
'link' => FALSE,
'data_type' => 'float',
],
'email' => [
'label' => 'Email',
'list' => TRUE,
'random' => TRUE,
'inline' => TRUE,
'link' => TRUE,
'data_type' => 'email',
],
'telephone' => [
'label' => 'Telephone',
'list' => TRUE,
'random' => FALSE,
'inline' => TRUE,
'link' => TRUE,
'data_type' => 'string',
],
'uri' => [
'label' => 'Url',
'list' => TRUE,
'random' => TRUE,
'inline' => TRUE,
'link' => TRUE,
'data_type' => 'uri',
],
'datetime' => [
'label' => 'Date',
'list' => TRUE,
'random' => FALSE,
'inline' => FALSE,
'link' => FALSE,
'data_type' => 'datetime_iso8601',
],
];
/**
* Date types.
*
* @var array
*/
protected $dateTypes = [
'date' => 'Date only',
'datetime' => 'Date and time',
];
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['field_label'] = new Question('Field label', 'Example');
$questions['field_label']->setValidator([Utils::class, 'validateRequired']);
$default_field_id = function (array $vars) {
return $vars['machine_name'] . '_' . Utils::human2machine($vars['field_label']);
};
$questions['field_id'] = new Question('Field ID', $default_field_id);
$questions['field_id']->setValidator([Utils::class, 'validateMachineName']);
$questions['subfield_count'] = new Question('How many sub-fields would you like to create?', 3);
$subfield_count_validator = function ($value) {
if (!is_numeric($value) || intval($value) != $value || $value <= 0) {
throw new \UnexpectedValueException('The value should be greater than zero.');
}
return $value;
};
$questions['subfield_count']->setValidator($subfield_count_validator);
$vars = &$this->collectVars($input, $output, $questions);
$type_choices = array_column($this->subTypes, 'label');
$type_choices = Utils::prepareChoices($type_choices);
// Indicates that at least one of sub-fields needs Random component.
$vars['random'] = FALSE;
// Indicates that all sub-fields can be rendered inline.
$vars['inline'] = TRUE;
// Indicates that at least one of sub-files has limited allowed values.
$vars['list'] = FALSE;
// Indicates that at least one of sub-fields is required.
$vars['required'] = FALSE;
// Indicates that at least one of sub-fields is of email type.
$vars['email'] = FALSE;
// Indicates that at least one of sub-fields can be rendered as a link.
$vars['link'] = FALSE;
// Indicates that at least one of sub-fields is of datetime type.
$vars['datetime'] = FALSE;
for ($i = 1; $i <= $vars['subfield_count']; $i++) {
if (!$input->getOption('answers')) {
$output->writeln('<fg=green></>');
}
$subfield_questions = [];
$subfield_questions['name_' . $i] = new Question("Label for sub-field #$i", "Value $i");
$default_machine_name = function (array $vars) use ($i) {
return Utils::human2machine($vars['name_' . $i]);
};
$subfield_questions['machine_name_' . $i] = new Question("Machine name for sub-field #$i", $default_machine_name);
$subfield_questions['type_' . $i] = new ChoiceQuestion("Type of sub-field #$i", $type_choices, 'Text');
$this->collectVars($input, $output, $subfield_questions);
$vars['type_class'] = Utils::camelize($vars['field_label']) . 'Item';
$vars['widget_class'] = Utils::camelize($vars['field_label']) . 'Widget';
$vars['formatter_class'] = Utils::camelize($vars['field_label']) . 'DefaultFormatter';
// Reset previous questions since we already collected their answers.
$subfield_questions = [];
// Determine the type ID by its label.
foreach ($this->subTypes as $type => $definition) {
if ($vars['type_' . $i] == $definition['label']) {
break;
}
}
if ($type == 'datetime') {
$subfield_questions['date_type_' . $i] = new ChoiceQuestion(
"Date type for sub-field #$i",
Utils::prepareChoices($this->dateTypes),
'Date only'
);
}
if ($definition['list']) {
$subfield_questions['list_' . $i] = new ConfirmationQuestion("Limit allowed values for sub-field #$i?", FALSE);
}
$subfield_questions['required_' . $i] = new ConfirmationQuestion("Make sub-field #$i required?", FALSE);
$this->collectVars($input, $output, $subfield_questions);
$machine_name = $vars['machine_name_' . $i];
// Group sub-field vars.
$vars['subfields'][$i] = [
'name' => $vars['name_' . $i],
'machine_name' => $machine_name,
'type' => $type,
'data_type' => $definition['data_type'],
'list' => !empty($vars['list_' . $i]),
'allowed_values_method' => 'allowed' . Utils::camelize($vars['name_' . $i], TRUE) . 'Values',
'required' => $vars['required_' . $i],
'link' => $definition['link'],
];
if (isset($vars['date_type_' . $i])) {
$date_type = array_search($vars['date_type_' . $i], $this->dateTypes);
$vars['subfields'][$i]['date_type'] = $date_type;
// Back to date type ID.
$vars['subfields'][$i]['date_storage_format'] = $date_type == 'date' ? 'Y-m-d' : 'Y-m-d\TH:i:s';
}
unset($vars['name_' . $i], $vars['machine_name_' . $i], $vars['type_' . $i], $vars['list_' . $i], $vars['required_' . $i], $vars['date_type_' . $i]);
if ($definition['random']) {
$vars['random'] = TRUE;
}
if (!$definition['inline']) {
$vars['inline'] = FALSE;
}
if ($vars['subfields'][$i]['list']) {
$vars['list'] = TRUE;
}
if ($vars['subfields'][$i]['required']) {
$vars['required'] = TRUE;
}
if ($type == 'email') {
$vars['email'] = TRUE;
}
if ($definition['link']) {
$vars['link'] = TRUE;
}
if ($type == 'datetime') {
$vars['datetime'] = TRUE;
}
}
if (!$input->getOption('answers')) {
$output->writeln('<fg=green></>');
}
$questions = [];
$questions['storage_settings'] = new ConfirmationQuestion('Would you like to create field storage settings form?', FALSE);
$questions['instance_settings'] = new ConfirmationQuestion('Would you like to create field instance settings form?', FALSE);
$questions['widget_settings'] = new ConfirmationQuestion('Would you like to create field widget settings form?', FALSE);
$questions['formatter_settings'] = new ConfirmationQuestion('Would you like to create field formatter settings form?', FALSE);
$questions['table_formatter'] = new ConfirmationQuestion('Would you like to create table formatter?', FALSE);
$questions['key_value_formatter'] = new ConfirmationQuestion('Would you like to create key-value formatter?', FALSE);
$vars += $this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Plugin/Field/FieldType/{type_class}.php')
->template('d8/_field/type.twig');
$this->addFile()
->path('src/Plugin/Field/FieldWidget/{widget_class}.php')
->template('d8/_field/widget.twig');
$this->addFile()
->path('src/Plugin/Field/FieldFormatter/{formatter_class}.php')
->template('d8/_field/default-formatter.twig');
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/_field/schema.twig')
->action('append');
$this->addFile()
->path('{machine_name}.libraries.yml')
->template('d8/_field/libraries.twig')
->action('append');
$this->addFile()
->path('css/' . str_replace('_', '-', $vars['field_id']) . '-widget.css')
->template('d8/_field/widget-css.twig');
if ($vars['table_formatter']) {
$vars['table_formatter_class'] = Utils::camelize($vars['field_label']) . 'TableFormatter';
$this->addFile()
->path('src/Plugin/Field/FieldFormatter/{table_formatter_class}.php')
->template('d8/_field/table-formatter.twig');
}
if ($vars['key_value_formatter']) {
$vars['key_value_formatter_class'] = Utils::camelize($vars['field_label']) . 'KeyValueFormatter';
$this->addFile()
->path('src/Plugin/Field/FieldFormatter/{key_value_formatter_class}.php')
->template('d8/_field/key-value-formatter.twig');
}
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Form;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Base class for Drupal 8 form generators.
*/
abstract class Base extends BaseGenerator {
/**
* {@inheritdoc}
*/
protected function doInteract(InputInterface $input, OutputInterface $output, $options) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', $options['default_class']);
$questions['route'] = new ConfirmationQuestion('Would you like to create a route for this form?');
$vars = &$this->collectVars($input, $output, $questions);
$raw_form_id = preg_replace('/_form/', '', Utils::camel2machine($vars['class']));
$vars['form_id'] = $vars['machine_name'] . '_' . $raw_form_id;
if ($vars['route']) {
$default_path_prefix = isset($options['default_path_prefix']) ?
$options['default_path_prefix'] : '/' . $vars['machine_name'];
$route_path = str_replace('_', '-', $default_path_prefix . '/' . $raw_form_id);
$route_questions['route_name'] = new Question('Route name', '{machine_name}.' . $raw_form_id);
$route_questions['route_path'] = new Question('Route path', $route_path);
$route_questions['route_title'] = new Question('Route title', Utils::machine2human($raw_form_id));
$route_questions['route_permission'] = new Question('Route permission', $options['default_permission']);
$this->collectVars($input, $output, $route_questions, $vars);
$this->addFile()
->path('{machine_name}.routing.yml')
->template('d8/form/route.twig')
->action('append');
}
$this->addFile()
->path('src/Form/{class}.php')
->template($options['template']);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Form;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:form:config command.
*/
class Config extends Base {
protected $name = 'd8:form:config';
protected $description = 'Generates a configuration form';
protected $alias = 'config-form';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$options = [
'default_class' => 'SettingsForm',
'default_permission' => 'administer site configuration',
'default_path_prefix' => '/admin',
'template' => 'd8/form/config.twig',
];
$this->doInteract($input, $output, $options);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Form;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:form:confirm command.
*/
class Confirm extends Base {
protected $name = 'd8:form:confirm';
protected $description = 'Generates a confirmation form';
protected $alias = 'confirm-form';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$options = [
'default_class' => 'ExampleConfirmForm',
'default_permission' => 'administer site configuration',
'default_path_prefix' => '/admin',
'template' => 'd8/form/confirm.twig',
];
$this->doInteract($input, $output, $options);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Form;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:form:simple command.
*/
class Simple extends Base {
protected $name = 'd8:form:simple';
protected $description = 'Generates simple form';
protected $alias = 'form-simple';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$options = [
'default_class' => 'ExampleForm',
'default_permission' => 'access content',
'template' => 'd8/form/simple.twig',
];
$this->doInteract($input, $output, $options);
}
}

View file

@ -0,0 +1,124 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:hook command.
*/
class Hook extends BaseGenerator {
protected $name = 'd8:hook';
protected $description = 'Generates a hook';
protected $alias = 'hook';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['hook_name'] = new Question('Hook name');
$questions['hook_name']->setValidator(function ($value) {
if (!in_array($value, $this->supportedHooks())) {
throw new \UnexpectedValueException('The value is not correct class name.');
}
return $value;
});
$questions['hook_name']->setAutocompleterValues($this->supportedHooks());
$vars = $this->collectVars($input, $output, $questions);
// Most Drupal hooks are situated in a module file but some are not.
$special_hooks = [
'install' => [
'install',
'uninstall',
'schema',
'requirements',
'update_N',
'update_last_removed',
],
// See views_hook_info().
'views.inc' => [
'views_data',
'views_data_alter',
'views_analyze',
'views_invalidate_cache',
'field_views_data',
'field_views_data_alter',
// See \Drupal\views\views::$plugins.
'views_plugins_access_alter',
'views_plugins_area_alter',
'views_plugins_argument_alter',
'views_plugins_argument_default_alter',
'views_plugins_argument_validator_alter',
'views_plugins_cache_alter',
'views_plugins_display_extender_alter',
'views_plugins_display_alter',
'views_plugins_exposed_form_alter',
'views_plugins_field_alter',
'views_plugins_filter_alter',
'views_plugins_join_alter',
'views_plugins_pager_alter',
'views_plugins_query_alter',
'views_plugins_relationship_alter',
'views_plugins_row_alter',
'views_plugins_sort_alter',
'views_plugins_style_alter',
'views_plugins_wizard_alter',
],
'views_execution.inc' => [
'views_query_substitutions',
'views_form_substitutions',
'views_pre_view',
'views_pre_build',
'views_post_build',
'views_pre_execute',
'views_post_execute',
'views_pre_render',
'views_post_render',
'views_query_alter',
],
// See system_hook_info().
'tokens.inc' => [
'token_info',
'token_info_alter',
'tokens',
'tokens_alter',
],
'post_update.php' => [
'post_update_N',
],
];
$file_type = 'module';
foreach ($special_hooks as $group => $hooks) {
if (in_array($vars['hook_name'], $hooks)) {
$file_type = $group;
break;
}
}
$this->addFile()
->path('{machine_name}.' . $file_type)
->headerTemplate("d8/file-docs/$file_type.twig")
->template('d8/hook/' . $vars['hook_name'] . '.twig')
->action('append')
->headerSize(7);
}
/**
* Returns list of supported hooks.
*/
protected function supportedHooks() {
return array_map(function ($file) {
return pathinfo($file, PATHINFO_FILENAME);
}, array_diff(scandir($this->templatePath . '/d8/hook'), ['.', '..']));
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:install-file command.
*/
class InstallFile extends BaseGenerator {
protected $name = 'd8:install-file';
protected $description = 'Generates an install file';
protected $alias = 'install-file';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$this->collectVars($input, $output, Utils::defaultQuestions());
$this->addFile()
->path('{machine_name}.install')
->template('d8/install.twig');
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:javascript command.
*/
class Javascript extends BaseGenerator {
protected $name = 'd8:javascript';
protected $description = 'Generates Drupal 8 JavaScript file';
protected $alias = 'javascript';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$vars = $this->collectVars($input, $output, Utils::defaultQuestions());
$this->addFile()
->path('js/' . str_replace('_', '-', $vars['machine_name']) . '.js')
->template('d8/javascript.twig');
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:layout command.
*/
class Layout extends BaseGenerator {
protected $name = 'd8:layout';
protected $description = 'Generates a layout';
protected $alias = 'layout';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Extension machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$questions['layout_name'] = new Question('Layout name', 'Example');
$questions['layout_machine_name'] = new Question('Layout machine name', function ($vars) {
return Utils::human2machine($vars['layout_name']);
});
$questions['category'] = new Question('Category', 'My layouts');
$questions['js'] = new ConfirmationQuestion('Would you like to create JavaScript file for this layout?', FALSE);
$questions['css'] = new ConfirmationQuestion('Would you like to create CSS file for this layout?', FALSE);
$vars = &$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.layouts.yml')
->template('d8/_layout/layouts.twig')
->action('append');
if ($vars['js'] || $vars['css']) {
$this->addFile()
->path('{machine_name}.libraries.yml')
->template('d8/_layout/libraries.twig')
->action('append');
}
$vars['layout_asset_name'] = str_replace('_', '-', $vars['layout_machine_name']);
$this->addFile()
->path('layouts/{layout_machine_name}/{layout_asset_name}.html.twig')
->template('d8/_layout/template.twig');
if ($vars['js']) {
$this->addFile()
->path('layouts/{layout_machine_name}/{layout_asset_name}.js')
->template('d8/_layout/javascript.twig');
}
if ($vars['css']) {
$this->addFile()
->path('layouts/{layout_machine_name}/{layout_asset_name}.css')
->template('d8/_layout/styles.twig');
}
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Module;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:module:configuration-entity command.
*/
class ConfigurationEntity extends BaseGenerator {
protected $name = 'd8:module:configuration-entity';
protected $description = 'Generates configuration entity module';
protected $alias = 'configuration-entity';
protected $destination = 'modules';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['package'] = new Question('Package', 'Custom');
$questions['dependencies'] = new Question('Dependencies (comma separated)');
$questions['entity_type_label'] = new Question('Entity type label', '{name}');
$questions['entity_type_id'] = new Question(
'Entity type ID',
function ($vars) {
return Utils::human2machine($vars['entity_type_label']);
}
);
$vars = &$this->collectVars($input, $output, $questions);
if ($vars['dependencies']) {
$vars['dependencies'] = array_map('trim', explode(',', strtolower($vars['dependencies'])));
}
$vars['class_prefix'] = Utils::camelize($vars['entity_type_label']);
$templates = [
'model.info.yml.twig',
'src/ExampleListBuilder.php.twig',
'src/Form/ExampleForm.php.twig',
'src/ExampleInterface.php.twig',
'src/Entity/Example.php.twig',
'model.routing.yml.twig',
'model.links.action.yml.twig',
'model.links.menu.yml.twig',
'model.permissions.yml.twig',
'config/schema/model.schema.yml.twig',
];
$templates_path = 'd8/module/configuration-entity/';
$path_placeholders = ['model', 'Example', '.twig'];
$path_replacements = [$vars['machine_name'], $vars['class_prefix'], ''];
foreach ($templates as $template) {
$this->addFile()
->path('{machine_name}/' . str_replace($path_placeholders, $path_replacements, $template))
->template($templates_path . $template);
}
}
}

View file

@ -0,0 +1,146 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Module;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:module:content-entity command.
*/
class ContentEntity extends BaseGenerator {
protected $name = 'd8:module:content-entity';
protected $description = 'Generates content entity module';
protected $alias = 'content-entity';
protected $destination = 'modules';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['package'] = new Question('Package', 'Custom');
$questions['dependencies'] = new Question('Dependencies (comma separated)');
$questions['entity_type_label'] = new Question('Entity type label', '{name}');
$questions['entity_type_id'] = new Question(
'Entity type ID',
function ($vars) {
return Utils::human2machine($vars['entity_type_label']);
}
);
$questions['entity_base_path'] = new Question(
'Entity base path',
function ($vars) {
return '/admin/content/' . str_replace('_', '-', $vars['entity_type_id']);
}
);
$questions['fieldable'] = new ConfirmationQuestion('Make the entity type fieldable?', TRUE);
$questions['revisionable'] = new ConfirmationQuestion('Make the entity type revisionable?', FALSE);
$questions['translatable'] = new ConfirmationQuestion('Make the entity type translatable?', FALSE);
$questions['bundle'] = new ConfirmationQuestion('The entity type has bundle?', FALSE);
$questions['template'] = new ConfirmationQuestion('Create entity template?', TRUE);
$questions['access_controller'] = new ConfirmationQuestion('Create CRUD permissions?', FALSE);
$questions['title_base_field'] = new ConfirmationQuestion('Add "title" base field?', TRUE);
$questions['status_base_field'] = new ConfirmationQuestion('Add "status" base field?', TRUE);
$questions['created_base_field'] = new ConfirmationQuestion('Add "created" base field?', TRUE);
$questions['changed_base_field'] = new ConfirmationQuestion('Add "changed" base field?', TRUE);
$questions['author_base_field'] = new ConfirmationQuestion('Add "author" base field?', TRUE);
$questions['description_base_field'] = new ConfirmationQuestion('Add "description" base field?', TRUE);
$questions['rest_configuration'] = new ConfirmationQuestion('Create REST configuration for the entity?', FALSE);
$vars = &$this->collectVars($input, $output, $questions);
if ($vars['dependencies']) {
$vars['dependencies'] = array_map('trim', explode(',', strtolower($vars['dependencies'])));
}
if ($vars['entity_base_path'][0] != '/') {
$vars['entity_base_path'] = '/' . $vars['entity_base_path'];
}
if (($vars['fieldable_no_bundle'] = $vars['fieldable'] && !$vars['bundle'])) {
$vars['configure'] = 'entity.' . $vars['entity_type_id'] . '.settings';
}
elseif ($vars['bundle']) {
$vars['configure'] = 'entity.' . $vars['entity_type_id'] . '_type.collection';
}
$vars['class_prefix'] = Utils::camelize($vars['entity_type_label']);
$templates = [
'model.info.yml.twig',
'model.links.action.yml.twig',
'model.links.menu.yml.twig',
'model.links.task.yml.twig',
'model.permissions.yml.twig',
'src/Entity/Example.php.twig',
'src/ExampleInterface.php.twig',
'src/ExampleListBuilder.php.twig',
'src/Form/ExampleForm.php.twig',
];
if ($vars['fieldable_no_bundle']) {
$templates[] = 'model.routing.yml.twig';
$templates[] = 'src/Form/ExampleSettingsForm.php.twig';
}
if ($vars['template']) {
$templates[] = 'templates/model-example.html.twig.twig';
$templates[] = 'model.module.twig';
}
else {
$templates[] = 'src/ExampleViewBuilder.php.twig';
}
if ($vars['access_controller']) {
$templates[] = 'src/ExampleAccessControlHandler.php.twig';
}
if ($vars['rest_configuration']) {
$templates[] = 'config/optional/rest.resource.entity.example.yml.twig';
}
if ($vars['bundle']) {
$templates[] = 'config/optional/views.view.example.yml.twig';
$templates[] = 'config/schema/model.entity_type.schema.yml.twig';
$templates[] = 'src/ExampleTypeListBuilder.php.twig';
$templates[] = 'src/Entity/ExampleType.php.twig';
$templates[] = 'src/Form/ExampleTypeForm.php.twig';
}
$templates_path = 'd8/module/content-entity/';
$vars['template_name'] = str_replace('_', '-', $vars['entity_type_id']) . '.html.twig';
$path_placeholders = [
'model-example.html.twig',
'model',
'Example',
'rest.resource.entity.example',
'views.view.example',
];
$path_replacements = [
$vars['template_name'],
$vars['machine_name'],
$vars['class_prefix'],
'rest.resource.entity.' . $vars['entity_type_id'],
'views.view.' . $vars['entity_type_id'],
];
foreach ($templates as $template) {
$path = $vars['machine_name'] . '/' . str_replace($path_placeholders, $path_replacements, $template);
$this->addFile()
->path(preg_replace('#\.twig$#', '', $path))
->template($templates_path . $template);
}
}
}

View file

@ -0,0 +1,160 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Module;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:module:standard command.
*/
class Standard extends BaseGenerator {
protected $name = 'd8:module:standard';
protected $description = 'Generates standard Drupal 8 module';
protected $alias = 'module';
protected $destination = 'modules';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['description'] = new Question('Module description', 'The description.');
$questions['package'] = new Question('Package', 'Custom');
$questions['dependencies'] = new Question('Dependencies (comma separated)');
$vars = &$this->collectVars($input, $output, $questions);
if ($vars['dependencies']) {
$vars['dependencies'] = array_map('trim', explode(',', strtolower($vars['dependencies'])));
}
$prefix = $vars['machine_name'] . '/' . $vars['machine_name'];
$this->addFile()
->path($prefix . '.info.yml')
->template('d8/yml/module-info.twig');
$this->addFile()
->path($prefix . '.module')
->template('d8/module.twig');
$class_prefix = Utils::camelize($vars['name']);
// Additional files.
$option_questions['install_file'] = new ConfirmationQuestion('Would you like to create install file?', TRUE);
$option_questions['libraries.yml'] = new ConfirmationQuestion('Would you like to create libraries.yml file?', TRUE);
$option_questions['permissions.yml'] = new ConfirmationQuestion('Would you like to create permissions.yml file?', TRUE);
$option_questions['event_subscriber'] = new ConfirmationQuestion('Would you like to create event subscriber?', TRUE);
$option_questions['block_plugin'] = new ConfirmationQuestion('Would you like to create block plugin?', TRUE);
$option_questions['controller'] = new ConfirmationQuestion('Would you like to create a controller?', TRUE);
$option_questions['settings_form'] = new ConfirmationQuestion('Would you like to create settings form?', TRUE);
$options = $this->collectVars($input, $output, $option_questions);
if ($options['install_file']) {
$this->addFile()
->path($prefix . '.install')
->template('d8/install.twig');
}
if ($options['libraries.yml']) {
$this->addFile()
->path($prefix . '.libraries.yml')
->template('d8/yml/module-libraries.twig');
}
if ($options['permissions.yml']) {
$this->addFile()
->path($prefix . '.permissions.yml')
->template('d8/yml/permissions.twig');
}
if ($options['event_subscriber']) {
$subscriber_class = $class_prefix . 'Subscriber';
$this->addFile()
->path("{machine_name}/src/EventSubscriber/$subscriber_class.php")
->template('d8/service/event-subscriber.twig')
->vars($vars + ['class' => $subscriber_class]);
$this->addFile()
->path($prefix . '.services.yml')
->template('d8/service/event-subscriber.services.twig')
->vars($vars + ['class' => $subscriber_class]);
}
if ($options['block_plugin']) {
$block_vars['plugin_label'] = 'Example';
$block_vars['plugin_id'] = $vars['machine_name'] . '_' . Utils::human2machine($block_vars['plugin_label']);
$block_vars['category'] = $vars['name'];
$block_vars['class'] = 'ExampleBlock';
$this->addFile()
->path('{machine_name}/src/Plugin/Block/' . $block_vars['class'] . '.php')
->template('d8/plugin/block.twig')
->vars($block_vars + $vars);
}
if ($options['controller']) {
$controller_class = $class_prefix . 'Controller';
$controller_vars = [
'class' => $controller_class,
'services' => [],
];
$this->addFile()
->path("{machine_name}/src/Controller/$controller_class.php")
->template('d8/controller.twig')
->vars($controller_vars + $vars);
$routing_vars = [
'route_name' => $vars['machine_name'] . '.example',
'route_path' => '/' . str_replace('_', '-', $vars['machine_name']) . '/example',
'route_title' => 'Example',
'route_permission' => 'access content',
'class' => $controller_class,
];
$this->addFile()
->path($prefix . '.routing.yml')
->template('d8/controller-route.twig')
->vars($routing_vars + $vars)
->action('append');
}
if ($options['settings_form']) {
$form_class = 'SettingsForm';
$form_vars = [
'form_id' => $vars['machine_name'] . '_settings',
'class' => $form_class,
];
$this->addFile()
->path('{machine_name}/src/Form/SettingsForm.php')
->template('d8/form/config.twig')
->vars($form_vars + $vars);
$routing_vars = [
'route_name' => $vars['machine_name'] . '.settings_form',
'route_path' => '/admin/config/system/' . str_replace('_', '-', $vars['machine_name']),
'route_title' => $vars['name'] . ' settings',
'route_permission' => 'administer ' . $vars['machine_name'] . ' configuration',
'class' => $form_class,
];
$this->addFile()
->path($prefix . '.routing.yml')
->template('d8/form/route.twig')
->vars($routing_vars + $vars)
->action('append');
}
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:module-file command.
*/
class ModuleFile extends BaseGenerator {
protected $name = 'd8:module-file';
protected $description = 'Generates a module file';
protected $alias = 'module-file';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$this->collectVars($input, $output, Utils::defaultQuestions());
$this->addFile()
->path('{machine_name}.module')
->template('d8/module.twig');
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin:action command.
*/
class Action extends BaseGenerator {
protected $name = 'd8:plugin:action';
protected $description = 'Generates action plugin';
protected $alias = 'action';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions() + [
'category' => new Question('Action category', 'Custom'),
'configurable' => new ConfirmationQuestion('Make the action configurable?', FALSE),
];
// Plugin label should declare an action.
$questions['plugin_label'] = new Question('Action label', 'Update node title');
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$this->addFile()
->path('src/Plugin/Action/{class}.php')
->template('d8/plugin/action.twig');
if ($vars['configurable']) {
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/action-schema.twig')
->action('append');
}
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin:block command.
*/
class Block extends BaseGenerator {
protected $name = 'd8:plugin:block';
protected $description = 'Generates block plugin';
protected $alias = 'block';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$questions['plugin_label'] = new Question('Block admin label', 'Example');
$questions['plugin_label']->setValidator([Utils::class, 'validateRequired']);
$questions['category'] = new Question('Block category', 'Custom');
$questions['configurable'] = new ConfirmationQuestion('Make the block configurable?', FALSE);
$this->collectVars($input, $output, $questions);
$di_question = new ConfirmationQuestion('Would you like to inject dependencies?', FALSE);
if ($this->ask($input, $output, $di_question)) {
$this->collectServices($input, $output);
}
$access_question = new ConfirmationQuestion('Create access callback?', FALSE);
$vars = &$this->collectVars($input, $output, ['access' => $access_question]);
$vars['class'] = Utils::camelize($vars['plugin_label']) . 'Block';
$this->addFile()
->path('src/Plugin/Block/{class}.php')
->template('d8/plugin/block.twig');
if ($vars['configurable']) {
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/block-schema.twig')
->action('append');
}
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:plugin:ckeditor command.
*/
class CKEditor extends BaseGenerator {
protected $name = 'd8:plugin:ckeditor';
protected $description = 'Generates CKEditor plugin';
protected $alias = 'ckeditor';
protected $label = 'CKEditor';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$unprefixed_plugin_id = preg_replace('/^' . $vars['machine_name'] . '_/', '', $vars['plugin_id']);
// Convert plugin ID to hyphen case.
$vars['short_plugin_id'] = str_replace('_', '-', $unprefixed_plugin_id);
$vars['command_name'] = Utils::camelize($unprefixed_plugin_id, FALSE);
$this->addFile()
->path('src/Plugin/CKEditorPlugin/{class}.php')
->template('d8/plugin/_ckeditor/ckeditor.twig');
$this->addFile()
->path('js/plugins/{short_plugin_id}/plugin.js')
->template('d8/plugin/_ckeditor/plugin.twig');
$this->addFile()
->path('js/plugins/{short_plugin_id}/dialogs/{short_plugin_id}.js')
->template('d8/plugin/_ckeditor/dialog.twig');
$this->addFile()
->path('js/plugins/{short_plugin_id}/icons/{short_plugin_id}.png')
->content(file_get_contents($this->templatePath . '/d8/plugin/_ckeditor/icon.png'))
->action('replace');
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:plugin:condition command.
*/
class Condition extends BaseGenerator {
protected $name = 'd8:plugin:condition';
protected $description = 'Generates condition plugin';
protected $alias = 'condition';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$this->addFile()
->path('src/Plugin/Condition/{class}.php')
->template('d8/plugin/condition.twig');
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/condition-schema.twig')
->action('append');
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin:constraint command.
*/
class Constraint extends BaseGenerator {
protected $name = 'd8:plugin:constraint';
protected $description = 'Generates constraint plugin';
protected $alias = 'constraint';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$default_plugin_id = function (array $vars) {
// Unlike other plugin types. Constraint IDs use camel case.
return Utils::camelize($vars['name'] . $vars['plugin_label']);
};
$questions['plugin_id'] = new Question('Constraint ID', $default_plugin_id);
$plugin_id_validator = function ($value) {
if (!preg_match('/^[a-z][a-z0-9_]*[a-z0-9]$/i', $value)) {
throw new \UnexpectedValueException('The value is not correct machine name.');
}
return $value;
};
$questions['plugin_id']->setValidator($plugin_id_validator);
$input_types = [
'entity' => 'Entity',
'item_list' => 'Item list',
'item' => 'Item',
'raw_value' => 'Raw value',
];
$type_choices = Utils::prepareChoices($input_types);
$questions['input_type'] = new ChoiceQuestion('Type of data to validate', $type_choices, 'Item list');
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']) . 'Constraint';
$vars['input_type'] = array_search($vars['input_type'], $input_types);
$this->addFile()
->path('src/Plugin/Validation/Constraint/{class}.php')
->template('d8/plugin/constraint.twig');
$this->addFile()
->path('src/Plugin/Validation/Constraint/{class}Validator.php')
->template('d8/plugin/constraint-validator.twig');
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin:entity-reference-selection command.
*/
class EntityReferenceSelection extends BaseGenerator {
protected $name = 'd8:plugin:entity-reference-selection';
protected $description = 'Generates entity reference selection plugin';
protected $alias = 'entity-reference-selection';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$base_classes = [
'comment' => 'Drupal\comment\Plugin\EntityReferenceSelection\CommentSelection',
'file' => 'Drupal\file\Plugin\EntityReferenceSelection\FileSelection',
'node' => 'Drupal\node\Plugin\EntityReferenceSelection\NodeSelection',
'taxonomy_term' => 'Drupal\taxonomy\Plugin\EntityReferenceSelection\TermSelection',
'user' => 'Drupal\user\Plugin\EntityReferenceSelection\UserSelection',
];
$questions = Utils::defaultQuestions();
$questions['entity_type'] = new Question('Entity type that can be referenced by this plugin', 'node');
$questions['entity_type']->setValidator([Utils::class, 'validateMachineName']);
$questions['entity_type']->setAutocompleterValues(array_keys($base_classes));
$questions['plugin_label'] = new Question('Plugin label', 'Advanced {entity_type} selection');
$questions['plugin_label']->setValidator([Utils::class, 'validateRequired']);
$questions['plugin_id'] = new Question('Plugin ID', [Utils::class, 'defaultPluginId']);
$questions['plugin_id']->setValidator([Utils::class, 'validateMachineName']);
$questions['configurable'] = new ConfirmationQuestion('Provide additional plugin configuration?', FALSE);
$default_class = function ($vars) {
return Utils::camelize($vars['machine_name'] . '_' . $vars['entity_type']) . 'Selection';
};
$questions['class'] = new Question('Class', $default_class);
$vars = &$this->collectVars($input, $output, $questions);
if (isset($base_classes[$vars['entity_type']])) {
$vars['base_class_full'] = $base_classes[$vars['entity_type']];
}
else {
$vars['base_class_full'] = 'Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection';
}
$vars['base_class'] = explode('EntityReferenceSelection\\', $vars['base_class_full'])[1];
$this->addFile()
->path('src/Plugin/EntityReferenceSelection/{class}.php')
->template('d8/plugin/entity-reference-selection.twig');
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/entity-reference-selection-schema.twig')
->action('append');
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Field;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Implements d8:plugin:field:formatter command.
*/
class Formatter extends BaseGenerator {
protected $name = 'd8:plugin:field:formatter';
protected $description = 'Generates field formatter plugin';
protected $alias = 'field-formatter';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$questions['configurable'] = new ConfirmationQuestion('Make the formatter configurable?', FALSE);
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']) . 'Formatter';
$this->addFile()
->path('src/Plugin/Field/FieldFormatter/{class}.php')
->template('d8/plugin/field/formatter.twig');
if ($vars['configurable']) {
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/field/formatter-schema.twig')
->action('append');
}
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Field;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Implements d8:plugin:field:type command.
*/
class Type extends BaseGenerator {
protected $name = 'd8:plugin:field:type';
protected $description = 'Generates field type plugin';
protected $alias = 'field-type';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$questions['configurable_storage'] = new ConfirmationQuestion('Make the field storage configurable?', FALSE);
$questions['configurable_instance'] = new ConfirmationQuestion('Make the field instance configurable?', FALSE);
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']) . 'Item';
$this->addFile()
->path('src/Plugin/Field/FieldType/{class}.php')
->template('d8/plugin/field/type.twig');
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/field/type-schema.twig')
->action('append');
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Field;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Implements d8:plugin:field:widget command.
*/
class Widget extends BaseGenerator {
protected $name = 'd8:plugin:field:widget';
protected $description = 'Generates field widget plugin';
protected $alias = 'field-widget';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$questions['configurable'] = new ConfirmationQuestion('Make the widget configurable?', FALSE);
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']) . 'Widget';
$this->addFile()
->path('src/Plugin/Field/FieldWidget/{class}.php')
->template('d8/plugin/field/widget.twig');
if ($vars['configurable']) {
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/field/widget-schema.twig')
->action('append');
}
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
/**
* Implements d8:plugin:filter command.
*/
class Filter extends BaseGenerator {
protected $name = 'd8:plugin:filter';
protected $description = 'Generates filter plugin';
protected $alias = 'filter';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$filter_types = [
'TYPE_HTML_RESTRICTOR' => 'HTML restrictor',
'TYPE_MARKUP_LANGUAGE' => 'Markup language',
'TYPE_TRANSFORM_IRREVERSIBLE' => 'Irreversible transformation',
'TYPE_TRANSFORM_REVERSIBLE' => 'Reversible transformation',
];
$choices = Utils::prepareChoices($filter_types);
$questions['filter_type'] = new ChoiceQuestion('Filter type', $choices);
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$vars['filter_type'] = array_search($vars['filter_type'], $filter_types);
$this->addFile()
->path('src/Plugin/Filter/{class}.php')
->template('d8/plugin/filter.twig');
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/filter-schema.twig')
->action('append');
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin:menu-link command.
*/
class MenuLink extends BaseGenerator {
protected $name = 'd8:plugin:menu-link';
protected $description = 'Generates menu-link plugin';
protected $alias = 'menu-link';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['name']) . 'MenuLink';
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Plugin/Menu/{class}.php')
->template('d8/plugin/menu-link.twig');
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Migrate;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin:migrate:process command.
*/
class Process extends BaseGenerator {
protected $name = 'd8:plugin:migrate:process';
protected $description = 'Generates migrate process plugin';
protected $alias = 'migrate-process';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['plugin_id'] = new Question('Plugin ID', '{machine_name}_example');
$questions['plugin_id']->setValidator([Utils::class, 'validateMachineName']);
$vars = &$this->collectVars($input, $output, $questions);
$unprefixed_plugin_id = preg_replace('/^' . $vars['machine_name'] . '_/', '', $vars['plugin_id']);
$vars['class'] = Utils::camelize($unprefixed_plugin_id);
$this->addFile()
->path('src/Plugin/migrate/process/{class}.php')
->template('d8/plugin/migrate/process.twig');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:plugin:rest-resource command.
*/
class RestResource extends BaseGenerator {
protected $name = 'd8:plugin:rest-resource';
protected $description = 'Generates rest resource plugin';
protected $alias = 'rest-resource';
protected $label = 'REST resource';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']) . 'Resource';
$this->addFile()
->path('src/Plugin/rest/resource/{class}.php')
->template('d8/plugin/rest-resource.twig');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Views;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:plugin:views:argument-default command.
*/
class ArgumentDefault extends BaseGenerator {
protected $name = 'd8:plugin:views:argument-default';
protected $description = 'Generates views default argument plugin';
protected $alias = 'views-argument-default';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$this->addFile()
->path('src/Plugin/views/argument_default/{class}.php')
->template('d8/plugin/views/argument-default.twig');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Views;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:plugin:views:field command.
*/
class Field extends BaseGenerator {
protected $name = 'd8:plugin:views:field';
protected $description = 'Generates views field plugin';
protected $alias = 'views-field';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$this->addFile()
->path('src/Plugin/views/field/{class}.php')
->template('d8/plugin/views/field.twig');
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Plugin\Views;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:plugin:views:style command.
*/
class Style extends BaseGenerator {
protected $name = 'd8:plugin:views:style';
protected $description = 'Generates views style plugin';
protected $alias = 'views-style';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultPluginQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['plugin_label']);
$this->addFile()
->path('src/Plugin/views/style/{class}.php')
->template('d8/plugin/views/style-plugin.twig');
$this->addFile()
->path('templates/views-style-' . str_replace('_', '-', $vars['plugin_id']) . '.html.twig')
->template('d8/plugin/views/style-template.twig');
$this->addFile()
->path('{machine_name}.module')
->headerTemplate('d8/file-docs/module.twig')
->template('d8/plugin/views/style-preprocess.twig')
->action('append')
->headerSize(7);
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/plugin/views/style-schema.twig')
->action('append');
}
}

View file

@ -0,0 +1,111 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:plugin-manager command.
*/
class PluginManager extends BaseGenerator {
protected $name = 'd8:plugin-manager';
protected $description = 'Generates plugin manager';
protected $alias = 'plugin-manager';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_plugin_type = function ($vars) {
return $vars['machine_name'];
};
$questions['plugin_type'] = new Question('Plugin type', $default_plugin_type);
// Utils::validateMachineName does not allow dots. But they can appear in
// plugin types (field.widget, views.argument, etc).
$questions['plugin_type']->setValidator(function ($value) {
if (!preg_match('/^[a-z][a-z0-9_\.]*[a-z0-9]$/', $value)) {
throw new \UnexpectedValueException('The value is not correct machine name.');
}
return $value;
});
$discovery_types = [
'annotation' => 'Annotation',
'yaml' => 'YAML',
'hook' => 'Hook',
];
$choices = Utils::prepareChoices($discovery_types);
$questions['discovery'] = new ChoiceQuestion('Discovery type', $choices, 'Annotation');
$vars = &$this->collectVars($input, $output, $questions);
$vars['class_prefix'] = Utils::camelize($vars['plugin_type']);
$vars['discovery'] = array_search($vars['discovery'], $discovery_types);
$common_files = [
'model.services.yml',
'src/ExampleInterface.php',
'src/ExamplePluginManager.php',
];
$files = [];
switch ($vars['discovery']) {
case 'annotation':
$files = [
'src/Annotation/Example.php',
'src/ExamplePluginBase.php',
'src/Plugin/Example/Foo.php',
];
break;
case 'yaml':
$files = [
'model.examples.yml',
'src/ExampleDefault.php',
];
break;
case 'hook':
$files = [
'model.module',
'src/ExampleDefault.php',
];
break;
}
$files = array_merge($common_files, $files);
$templates_path = 'd8/plugin-manager/' . $vars['discovery'] . '/';
$path_placeholders = ['model', 'Example', 'examples'];
$path_replacements = [
$vars['machine_name'],
$vars['class_prefix'],
Utils::pluralize($vars['plugin_type']),
];
foreach ($files as $file) {
$asset = $this->addFile()
->path(str_replace($path_placeholders, $path_replacements, $file))
->template($templates_path . $file . '.twig');
if ($file === 'model.services.yml') {
$asset->action('append')->headerSize(1);
}
elseif ($file == 'model.module') {
$asset
->action('append')
->headerTemplate('d8/file-docs/module.twig')
->headerSize(7);
}
}
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:render-element command.
*/
class RenderElement extends BaseGenerator {
protected $name = 'd8:render-element';
protected $description = 'Generates Drupal 8 render element';
protected $alias = 'render-element';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Element/Entity.php')
->template('d8/render-element.twig');
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:access-checker command.
*/
class AccessChecker extends BaseGenerator {
protected $name = 'd8:service:access-checker';
protected $description = 'Generates an access checker service';
protected $alias = 'access-checker';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['applies_to'] = new Question('Applies to', '_foo');
$questions['applies_to']->setValidator(function ($value) {
if (!preg_match('/^_[a-z0-9_]*[a-z0-9]$/', $value)) {
throw new \UnexpectedValueException('The value is not correct name for "applies_to" property.');
}
return $value;
});
$default_class = function ($vars) {
return Utils::camelize($vars['applies_to']) . 'AccessChecker';
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Access/{class}.php')
->template('d8/service/access-checker.twig');
$this->addServicesFile()
->template('d8/service/access-checker.services.twig');
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:breadcrumb-builder command.
*/
class BreadcrumbBuilder extends BaseGenerator {
protected $name = 'd8:service:breadcrumb-builder';
protected $description = 'Generates a breadcrumb builder service';
protected $alias = 'breadcrumb-builder';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['name']) . 'BreadcrumbBuilder';
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/{class}.php')
->template('d8/service/breadcrumb-builder.twig');
$this->addServicesFile()
->path('{machine_name}.services.yml')
->template('d8/service/breadcrumb-builder.services.twig');
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:cache-context command.
*/
class CacheContext extends BaseGenerator {
protected $name = 'd8:service:cache-context';
protected $description = 'Generates a cache context service';
protected $alias = 'cache-context';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['context_id'] = new Question('Context ID', 'example');
$default_class = function ($vars) {
return Utils::camelize($vars['context_id']) . 'CacheContext';
};
$questions['class'] = new Question('Class', $default_class);
$base_class_choices = [
'-',
'RequestStackCacheContextBase',
'UserCacheContextBase',
];
$questions['base_class'] = new ChoiceQuestion('Base class', $base_class_choices);
$questions['calculated'] = new ConfirmationQuestion('Make the context calculated?', FALSE);
$vars = &$this->collectVars($input, $output, $questions);
$vars['context_label'] = Utils::machine2human($vars['context_id']);
$vars['interface'] = $vars['calculated'] ?
'CalculatedCacheContextInterface' : 'CacheContextInterface';
if ($vars['base_class'] == '-') {
$vars['base_class'] = FALSE;
}
$this->addFile()
->path('src/Cache/Context/{class}.php')
->template('d8/service/cache-context.twig');
$this->addServicesFile()
->template('d8/service/cache-context.services.twig');
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:custom command.
*/
class Custom extends BaseGenerator {
protected $name = 'd8:service:custom';
protected $description = 'Generates a custom Drupal service';
protected $alias = 'custom-service';
protected $label = 'Custom service';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['service_name'] = new Question('Service name', '{machine_name}.example');
$questions['service_name']->setValidator([Utils::class, 'validateServiceName']);
$default_class = function ($vars) {
$service = preg_replace('/^' . $vars['machine_name'] . '/', '', $vars['service_name']);
return Utils::camelize($service);
};
$questions['class'] = new Question('Class', $default_class);
$questions['class']->setValidator([Utils::class, 'validateClassName']);
$this->collectVars($input, $output, $questions);
$di_question = new ConfirmationQuestion('Would you like to inject dependencies?');
if ($this->ask($input, $output, $di_question)) {
$this->collectServices($input, $output);
}
$this->addFile()
->path('src/{class}.php')
->template('d8/service/custom.twig');
$this->addServicesFile()
->template('d8/service/custom.services.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:service:event-subscriber command.
*/
class EventSubscriber extends BaseGenerator {
protected $name = 'd8:service:event-subscriber';
protected $description = 'Generates an event subscriber';
protected $alias = 'event-subscriber';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['name']) . 'Subscriber';
$this->addFile()
->path('src/EventSubscriber/{class}.php')
->template('d8/service/event-subscriber.twig');
$this->addServicesFile()
->template('d8/service/event-subscriber.services.twig');
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:logger command.
*/
class Logger extends BaseGenerator {
protected $name = 'd8:service:logger';
protected $description = 'Generates a logger service';
protected $alias = 'logger';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'FileLog');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Logger/{class}.php')
->template('d8/service/logger.twig');
$this->addServicesFile()
->template('d8/service/logger.services.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:service:middleware command.
*/
class Middleware extends BaseGenerator {
protected $name = 'd8:service:middleware';
protected $description = 'Generates a middleware';
protected $alias = 'middleware';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['name']) . 'Middleware';
$this->addFile()
->path('src/{class}.php')
->template('d8/service/middleware.twig');
$this->addServicesFile()
->path('{machine_name}.services.yml')
->template('d8/service/middleware.services.twig');
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:param-converter command.
*/
class ParamConverter extends BaseGenerator {
protected $name = 'd8:service:param-converter';
protected $description = 'Generates a param converter service';
protected $alias = 'param-converter';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['parameter_type'] = new Question('Parameter type', 'example');
$default_class = function ($vars) {
return Utils::camelize($vars['parameter_type']) . 'ParamConverter';
};
$questions['class'] = new Question('Class', $default_class);
$vars = &$this->collectVars($input, $output, $questions);
$vars['controller_class'] = Utils::camelize($vars['machine_name']) . 'Controller';
$this->addFile()
->path('src/{class}.php')
->template('d8/service/param-converter.twig');
$this->addServicesFile()
->path('{machine_name}.services.yml')
->template('d8/service/param-converter.services.twig');
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:path-processor command.
*/
class PathProcessor extends BaseGenerator {
protected $name = 'd8:service:path-processor';
protected $description = 'Generates a path processor service';
protected $alias = 'path-processor';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return 'PathProcessor' . Utils::camelize($vars['name']);
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/PathProcessor/{class}.php')
->template('d8/service/path-processor.twig');
$this->addServicesFile()
->template('d8/service/path-processor.services.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:request-policy command.
*/
class RequestPolicy extends BaseGenerator {
protected $name = 'd8:service:request-policy';
protected $description = 'Generates a request policy service';
protected $alias = 'request-policy';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'Example');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/PageCache/{class}.php')
->template('d8/service/request-policy.twig');
$this->addServicesFile()
->template('d8/service/request-policy.services.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:response-policy command.
*/
class ResponsePolicy extends BaseGenerator {
protected $name = 'd8:service:response-policy';
protected $description = 'Generates a response policy service';
protected $alias = 'response-policy';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'Example');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/PageCache/{class}.php')
->template('d8/service/response-policy.twig');
$this->addServicesFile()
->template('d8/service/response-policy.services.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:service:route-subscriber command.
*/
class RouteSubscriber extends BaseGenerator {
protected $name = 'd8:service:route-subscriber';
protected $description = 'Generates a route subscriber';
protected $alias = 'route-subscriber';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['name']) . 'RouteSubscriber';
$this->addFile()
->path('src/EventSubscriber/{class}.php')
->template('d8/service/route-subscriber.twig');
$this->addServicesFile()
->template('d8/service/route-subscriber.services.twig');
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:theme-negotiator command.
*/
class ThemeNegotiator extends BaseGenerator {
protected $name = 'd8:service:theme-negotiator';
protected $description = 'Generates a theme negotiator';
protected $alias = 'theme-negotiator';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['name']) . 'Negotiator';
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Theme/{class}.php')
->template('d8/service/theme-negotiator.twig');
$this->addServicesFile()
->template('d8/service/theme-negotiator.services.twig');
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:twig-extension command.
*/
class TwigExtension extends BaseGenerator {
protected $name = 'd8:service:twig-extension';
protected $description = 'Generates Twig extension service';
protected $alias = 'twig-extension';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['name']) . 'TwigExtension';
};
$questions['class'] = new Question('Class', $default_class);
$questions['di'] = new ConfirmationQuestion('Would you like to inject dependencies?', FALSE);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/{class}.php')
->template('d8/service/twig-extension.twig');
$this->addServicesFile()
->template('d8/service/twig-extension.services.twig');
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Service;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:service:uninstall-validator command.
*/
class UninstallValidator extends BaseGenerator {
protected $name = 'd8:service:uninstall-validator';
protected $description = 'Generates a uninstall validator service';
protected $alias = 'uninstall-validator';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$default_class = function ($vars) {
return Utils::camelize($vars['name']) . 'UninstallValidator';
};
$questions['class'] = new Question('Class', $default_class);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/{class}.php')
->template('d8/service/uninstall-validator.twig');
$this->addServicesFile()
->template('d8/service/uninstall-validator.services.twig');
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:service-provider command.
*/
class ServiceProvider extends BaseGenerator {
protected $name = 'd8:service-provider';
protected $description = 'Generates a service provider';
protected $alias = 'service-provider';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$vars = &$this->collectVars($input, $output, Utils::defaultQuestions());
$vars['class'] = Utils::camelize($vars['name']) . 'ServiceProvider';
$this->addFile()
->path('src/{class}.php')
->template('d8/service-provider.twig');
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:settings-local command.
*/
class SettingsLocal extends BaseGenerator {
protected $name = 'd8:settings-local';
protected $description = 'Generates Drupal 8 settings.local.php file';
protected $alias = 'settings.local.php';
protected $destination = 'sites/default';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['db_override'] = new ConfirmationQuestion('Override database configuration?', FALSE);
$vars = $this->collectVars($input, $output, $questions);
if ($vars['db_override']) {
$questions = [
'database' => new Question('Database name', 'drupal_local'),
'username' => new Question('Database username', 'root'),
'password' => new Question('Database password'),
'host' => new Question('Database host', 'localhost'),
'driver' => new Question('Database type', 'mysql'),
];
array_walk($questions, function (Question $question) {
$question->setValidator([Utils::class, 'validateRequired']);
});
$this->collectVars($input, $output, $questions);
}
$this->addFile()
->path('settings.local.php')
->template('d8/settings.local.twig');
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:template command.
*/
class Template extends BaseGenerator {
protected $name = 'd8:template';
protected $description = 'Generates a template';
protected $alias = 'template';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['template_name'] = new Question('Template name', 'example');
$questions['create_theme'] = new ConfirmationQuestion('Create theme hook?', TRUE);
$questions['create_preprocess'] = new ConfirmationQuestion('Create preprocess hook?', TRUE);
$vars = $this->collectVars($input, $output, $questions);
$this->addFile()
->path('templates/{template_name}.html.twig')
->template('d8/template-template.twig');
if ($vars['create_theme'] || $vars['create_preprocess']) {
$this->addFile()
->path('{machine_name}.module')
->template('d8/template-module.twig')
->action('append')
->headerSize(7);
}
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Test;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:test:browser command.
*/
class Browser extends BaseGenerator {
protected $name = 'd8:test:browser';
protected $description = 'Generates a browser based test';
protected $alias = 'browser-test';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'ExampleTest');
$questions['class']->setValidator([Utils::class, 'validateClassName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('tests/src/Functional/{class}.php')
->template('d8/test/browser.twig');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Test;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:test:kernel command.
*/
class Kernel extends BaseGenerator {
protected $name = 'd8:test:kernel';
protected $description = 'Generates a kernel based test';
protected $alias = 'kernel-test';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'ExampleTest');
$questions['class']->setValidator([Utils::class, 'validateClassName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('tests/src/Kernel/{class}.php')
->template('d8/test/kernel.twig');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Test;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:test:nightwatch command.
*/
class Nightwatch extends BaseGenerator {
protected $name = 'd8:test:nightwatch';
protected $description = 'Generates a nightwatch test';
protected $alias = 'nightwatch-test';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['test_name'] = new Question('Test name', 'example');
$vars = &$this->collectVars($input, $output, $questions);
$vars['test_name'] = Utils::camelize($vars['test_name'], FALSE);
$this->addFile()
->path('tests/src/Nightwatch/{test_name}Test.js')
->template('d8/test/nightwatch.twig');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Test;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:test:unit command.
*/
class Unit extends BaseGenerator {
protected $name = 'd8:test:unit';
protected $description = 'Generates a unit test';
protected $alias = 'unit-test';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'ExampleTest');
$questions['class']->setValidator([Utils::class, 'validateClassName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('tests/src/Unit/{class}.php')
->template('d8/test/unit.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Test;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:test:web command.
*/
class Web extends BaseGenerator {
protected $name = 'd8:test:web';
protected $description = 'Generates a web based test';
protected $alias = 'web-test';
protected $label = 'Web (simpletest)';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'ExampleTest');
$questions['class']->setValidator([Utils::class, 'validateClassName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('src/Tests/{class}.php')
->template('d8/test/web.twig');
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Test;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:test:webdriver command.
*/
class WebDriver extends BaseGenerator {
protected $name = 'd8:test:webdriver';
protected $description = 'Generates a test that supports JavaScript';
protected $alias = 'webdriver-test';
protected $label = 'WebDriver';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['class'] = new Question('Class', 'ExampleTest');
$questions['class']->setValidator([Utils::class, 'validateClassName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('tests/src/FunctionalJavascript/{class}.php')
->template('d8/test/webdriver.twig');
}
}

View file

@ -0,0 +1,138 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:theme command.
*
* @TODO: Create a SUT test for this.
*/
class Theme extends BaseGenerator {
protected $name = 'd8:theme';
protected $description = 'Generates Drupal 8 theme';
protected $alias = 'theme';
protected $destination = 'themes';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['machine_name'] = new Question('Theme machine name');
$questions['base_theme'] = new Question('Base theme', 'classy');
$questions['description'] = new Question('Description', 'A flexible theme with a responsive, mobile-first layout.');
$questions['package'] = new Question('Package', 'Custom');
$questions['sass'] = new ConfirmationQuestion('Would you like to use SASS to compile style sheets?', FALSE);
$questions['breakpoints'] = new ConfirmationQuestion('Would you like to create breakpoints?', FALSE);
$questions['theme_settings'] = new ConfirmationQuestion('Would you like to create theme settings form?', FALSE);
$vars = $this->collectVars($input, $output, $questions);
$vars['base_theme'] = Utils::human2machine($vars['base_theme']);
$prefix = $vars['machine_name'] . '/' . $vars['machine_name'];
$this->addFile()
->path($prefix . '.info.yml')
->template('d8/yml/theme-info.twig');
$this->addFile()
->path($prefix . '.libraries.yml')
->template('d8/yml/theme-libraries.twig');
$this->addFile()
->path($prefix . '.theme')
->template('d8/theme.twig');
$this->addFile()
->path('{machine_name}/js/' . str_replace('_', '-', $vars['machine_name']) . '.js')
->template('d8/javascript.twig');
if ($vars['breakpoints']) {
$this->addFile()
->path($prefix . '.breakpoints.yml')
->template('d8/yml/breakpoints.twig');
}
if ($vars['theme_settings']) {
$this->addFile()
->path('{machine_name}/theme-settings.php')
->template('d8/theme-settings-form.twig');
$this->addFile()
->path('{machine_name}/config/install/{machine_name}.settings.yml')
->template('d8/theme-settings-config.twig');
$this->addFile()
->path('{machine_name}/config/schema/{machine_name}.schema.yml')
->template('d8/theme-settings-schema.twig');
}
$this->addFile()
->path('{machine_name}/logo.svg')
->template('d8/theme-logo.twig');
// Templates directory structure.
$this->addDirectory()
->path('{machine_name}/templates/page');
$this->addDirectory()
->path('{machine_name}/templates/node');
$this->addDirectory()
->path('{machine_name}/templates/field');
$this->addDirectory()
->path('{machine_name}/templates/view');
$this->addDirectory()
->path('{machine_name}/templates/block');
$this->addDirectory()
->path('{machine_name}/templates/menu');
$this->addDirectory()
->path('{machine_name}/images');
$this->addFile()
->path('{machine_name}/package.json')
->template('d8/theme-package.json.twig');
// Style sheets directory structure.
$this->addDirectory()
->path('{machine_name}/css');
$style_sheets = [
'base/elements',
'components/block',
'components/breadcrumb',
'components/field',
'components/form',
'components/header',
'components/menu',
'components/messages',
'components/node',
'components/sidebar',
'components/table',
'components/tabs',
'components/buttons',
'layouts/layout',
'theme/print',
];
foreach ($style_sheets as $file) {
$this->addFile()
->path('{machine_name}/' . ($vars['sass'] ? "scss/$file.scss" : "css/$file.css"))
->content('');
}
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:theme-file command.
*/
class ThemeFile extends BaseGenerator {
protected $name = 'd8:theme-file';
protected $description = 'Generates a theme file';
protected $alias = 'theme-file';
protected $destination = 'themes/%';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['name']->setValidator([Utils::class, 'validateRequired']);
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.theme')
->template('d8/theme.twig');
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:theme-settings command.
*/
class ThemeSettings extends BaseGenerator {
protected $name = 'd8:theme-settings';
protected $description = 'Generates Drupal 8 theme-settings.php file';
protected $alias = 'theme-settings';
protected $destination = 'themes/%';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['name']->setValidator([Utils::class, 'validateRequired']);
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('theme-settings.php')
->template('d8/theme-settings-form.twig');
$this->addFile()
->path('config/install/{machine_name}.settings.yml')
->template('d8/theme-settings-config.twig');
$this->addFile()
->path('config/schema/{machine_name}.schema.yml')
->template('d8/theme-settings-schema.twig');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:breakpoints command.
*/
class Breakpoints extends BaseGenerator {
protected $name = 'd8:yml:breakpoints';
protected $description = 'Generates a breakpoints yml file';
protected $alias = 'breakpoints';
protected $destination = 'themes/%';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.breakpoints.yml')
->template('d8/yml/breakpoints.twig');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml\Links;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:links:action command.
*/
class Action extends BaseGenerator {
protected $name = 'd8:yml:links:action';
protected $description = 'Generates a links.action yml file';
protected $alias = 'action-links';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.links.action.yml')
->template('d8/yml/links.action.twig');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml\Links;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:links:contextual command.
*/
class Contextual extends BaseGenerator {
protected $name = 'd8:yml:links:contextual';
protected $description = 'Generates links.contextual yml file';
protected $alias = 'contextual-links';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.links.contextual.yml')
->template('d8/yml/links.contextual.twig');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml\Links;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:links:menu command.
*/
class Menu extends BaseGenerator {
protected $name = 'd8:yml:links:menu';
protected $description = 'Generates a links.menu yml file';
protected $alias = 'menu-links';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.links.menu.yml')
->template('d8/yml/links.menu.twig');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml\Links;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:links:task command.
*/
class Task extends BaseGenerator {
protected $name = 'd8:yml:links:task';
protected $description = 'Generates a links.task yml file';
protected $alias = 'task-links';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.links.task.yml')
->template('d8/yml/links.task.twig');
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:module-info command.
*/
class ModuleInfo extends BaseGenerator {
protected $name = 'd8:yml:module-info';
protected $description = 'Generates a module info yml file';
protected $alias = 'module-info';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$questions['description'] = new Question('Description', 'Module description.');
$questions['package'] = new Question('Package', 'Custom');
$questions['configure'] = new Question('Configuration page (route name)');
$questions['dependencies'] = new Question('Dependencies (comma separated)');
$vars = &$this->collectVars($input, $output, $questions);
if ($vars['dependencies']) {
$vars['dependencies'] = array_map('trim', explode(',', strtolower($vars['dependencies'])));
}
$this->addFile()
->path('{machine_name}.info.yml')
->template('d8/yml/module-info.twig');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:module-libraries command.
*/
class ModuleLibraries extends BaseGenerator {
protected $name = 'd8:yml:module-libraries';
protected $description = 'Generates module libraries yml file';
protected $alias = 'module-libraries';
protected $label = 'Libraries (module)';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.libraries.yml')
->template('d8/yml/module-libraries.twig');
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:permissions command.
*/
class Permissions extends BaseGenerator {
protected $name = 'd8:yml:permissions';
protected $description = 'Generates a permissions yml file';
protected $alias = 'permissions';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Module machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.permissions.yml')
->template('d8/yml/permissions.twig');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:yml:routing command.
*/
class Routing extends BaseGenerator {
protected $name = 'd8:yml:routing';
protected $description = 'Generates a routing yml file';
protected $alias = 'routing';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['machine_name']) . 'Controller';
$this->addFile()
->path('{machine_name}.routing.yml')
->template('d8/yml/routing.twig');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Implements d8:yml:services command.
*/
class Services extends BaseGenerator {
protected $name = 'd8:yml:services';
protected $description = 'Generates a services yml file';
protected $alias = 'services';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions();
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize($vars['name']);
$this->addFile()
->path('{machine_name}.services.yml')
->template('d8/yml/services.twig');
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:theme-info command.
*/
class ThemeInfo extends BaseGenerator {
protected $name = 'd8:yml:theme-info';
protected $description = 'Generates a theme info yml file';
protected $alias = 'theme-info';
protected $destination = 'themes/%';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['name'] = new Question('Theme name');
$questions['name']->setValidator([Utils::class, 'validateRequired']);
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$questions['base_theme'] = new Question('Base theme', 'classy');
$questions['base_theme']->setValidator([Utils::class, 'validateMachineName']);
$questions['description'] = new Question('Description', 'A flexible theme with a responsive, mobile-first layout.');
$questions['package'] = new Question('Package', 'Custom');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.info.yml')
->template('d8/yml/theme-info.twig');
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace DrupalCodeGenerator\Command\Drupal_8\Yml;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements d8:yml:theme-libraries command.
*/
class ThemeLibraries extends BaseGenerator {
protected $name = 'd8:yml:theme-libraries';
protected $description = 'Generates theme libraries yml file';
protected $alias = 'theme-libraries';
protected $label = 'Libraries (theme)';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['machine_name'] = new Question('Theme machine name');
$questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{machine_name}.libraries.yml')
->template('d8/yml/theme-libraries.twig');
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace DrupalCodeGenerator\Command;
/**
* Defines generator interface.
*/
interface GeneratorInterface {
/**
* Returns command label.
*
* @return string|null
* A label suitable for navigation command.
*/
public function getLabel();
/**
* Returns list of assets to dump.
*
* @return \DrupalCodeGenerator\Asset[]
* An array of assets.
*/
public function getAssets();
/**
* Sets working directory.
*
* @param string|null $directory
* The working directory.
*/
public function setDirectory($directory);
/**
* Returns current working directory.
*
* @return string|null
* The directory.
*/
public function getDirectory();
/**
* Sets destination.
*
* @param mixed $destination
* The destination.
*/
public function setDestination($destination);
/**
* Returns destination.
*
* @return mixed
* The recommended destination for dumped files.
* This value can be handy to determine the nature of the generated code
* (module, theme, etc). The DCG itself does not make use of it when saving
* files because of lack of Drupal context however all its generators have
* this property configured.
* The destination format is as follows:
* - modules (new module)
* - modules/% (existing module)
* - themes (new theme)
* - themes/% (existing theme)
* - profiles (new profile)
* - sites/default
* Note that the paths without leading slash are related to Drupal root
* directory.
*/
public function getDestination();
}

View file

@ -0,0 +1,292 @@
<?php
namespace DrupalCodeGenerator\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
/**
* Implements generate command.
*/
class Navigation extends Command {
/**
* Menu tree.
*
* @var array
*/
protected $menuTree;
/**
* Name of the generator to execute.
*
* @var string
*/
protected $generatorName;
/**
* Command labels.
*
* @var array
*/
protected $labels = [
'd7' => 'Drupal 7',
'd8' => 'Drupal 8',
];
/**
* Aliases for some sub-menus.
*
* @var array
*/
protected $defaultAliases = [
'service' => 'd8:service',
'plugin' => 'd8:plugin',
'theme' => 'd8:theme',
'module' => 'd8:module',
'form' => 'd8:form',
'test' => 'd8:test',
'yml' => 'd8:yml',
'links' => 'd8:yml:links',
];
/**
* Constructs menu command.
*
* @param \DrupalCodeGenerator\Command\GeneratorInterface[] $commands
* List of registered commands.
*/
public function __construct(array $commands) {
parent::__construct();
// Initialize the menu structure.
$this->menuTree = [];
$aliases = array_keys($this->defaultAliases);
// Build aliases for the navigation based on command namespaces.
foreach ($commands as $command) {
$command_name = $command->getName();
$sub_names = explode(':', $command_name);
$this->arraySetNestedValue($this->menuTree, $sub_names, TRUE);
// The last sub-name is actual command name so it cannot be used as an
// alias for navigation command.
$last_sub_name = array_pop($sub_names);
// Collect command labels.
if ($label = $command->getLabel()) {
$this->labels[$last_sub_name] = $label;
}
// We cannot use $application->getNamespaces() here because the
// application is not available at this point.
$alias = '';
foreach ($sub_names as $sub_name) {
$alias = $alias ? $alias . ':' . $sub_name : $sub_name;
$aliases[] = $alias;
}
}
$this->setAliases(array_unique($aliases));
$this->recursiveKsort($this->menuTree);
}
/**
* {@inheritdoc}
*/
public function getUsages() {
return ['<generator>'];
}
/**
* {@inheritdoc}
*/
protected function configure() {
$this
->setName('navigation')
->setDescription('Provides an interactive menu to select generator')
->setHelp('Run `dcg list` to check out all available generators.')
->setHidden(TRUE)
->addOption(
'directory',
'-d',
InputOption::VALUE_OPTIONAL,
'Working directory'
)
->addOption(
'answers',
'-a',
InputOption::VALUE_OPTIONAL,
'Default JSON formatted answers'
);
}
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$style = new OutputFormatterStyle('black', 'cyan', []);
$output->getFormatter()->setStyle('title', $style);
$command_name = $input->getFirstArgument();
// Before version 3.3.6 of Symfony console getFistArgument returned default
// command name.
$command_name = $command_name == 'navigation' ? NULL : $command_name;
if (isset($this->defaultAliases[$command_name])) {
$command_name = $this->defaultAliases[$command_name];
}
$menu_trail = $command_name ? explode(':', $command_name) : [];
$this->generatorName = $this->selectGenerator($input, $output, $menu_trail);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output) {
if (!$this->generatorName) {
return 0;
}
// Run the generator.
return $this->getApplication()
->find($this->generatorName)
->run($input, $output);
}
/**
* Returns a generator selected by the user from a multilevel console menu.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* Input instance.
* @param \Symfony\Component\Console\Output\OutputInterface $output
* Output instance.
* @param array $menu_trail
* Menu trail.
*
* @return string|null
* Generator name or null if user decided to exit the navigation.
*/
protected function selectGenerator(InputInterface $input, OutputInterface $output, array $menu_trail) {
// Narrow down menu tree.
$active_menu_tree = $this->menuTree;
foreach ($menu_trail as $active_menu_item) {
$active_menu_tree = $active_menu_tree[$active_menu_item];
}
// The $active_menu_tree can be either an array of menu items or TRUE if the
// user reached the final menu point.
if ($active_menu_tree === TRUE) {
return implode(':', $menu_trail);
}
$sub_menu_labels = $command_labels = [];
foreach ($active_menu_tree as $menu_item => $subtree) {
if (is_array($subtree)) {
$sub_menu_labels[$menu_item] = $this->createMenuItemLabel($menu_item, TRUE);
}
else {
$command_labels[$menu_item] = $this->createMenuItemLabel($menu_item, FALSE);
}
}
asort($sub_menu_labels);
asort($command_labels);
// Generally the choices array consists of the following parts:
// - Reference to the parent menu level.
// - Sorted list of nested menu levels.
// - Sorted list of commands.
$choices = ['..' => '..'] + $sub_menu_labels + $command_labels;
$question = new ChoiceQuestion('<title> Select generator: </title>', array_values($choices));
$question->setPrompt(count($choices) <= 10 ? ' ➤➤➤ ' : ' ➤➤➤➤ ');
$answer_label = $this->getHelper('question')->ask($input, $output, $question);
$answer = array_search($answer_label, $choices);
if ($answer == '..') {
// Exit the application if the user selected zero on the top menu level.
if (count($menu_trail) == 0) {
return NULL;
}
// Decrease menu level.
array_pop($menu_trail);
}
else {
// Increase menu level.
$menu_trail[] = $answer;
}
return $this->selectGenerator($input, $output, $menu_trail);
}
/**
* Creates a human readable label for a given menu item.
*
* @param string $menu_item
* Machine name of the menu item.
* @param bool $comment
* A boolean indicating that the label should be wrapped with comment tag.
*
* @return string
* The menu label.
*/
protected function createMenuItemLabel($menu_item, $comment) {
$label = isset($this->labels[$menu_item]) ?
$this->labels[$menu_item] : str_replace(['-', '_'], ' ', ucfirst($menu_item));
return $comment ? "<comment>$label</comment>" : $label;
}
/**
* Sort multi-dimensional array by keys.
*
* @param array $array
* An array being sorted.
*
* @return array
* Sorted array.
*/
protected function recursiveKsort(array &$array) {
foreach ($array as &$value) {
if (is_array($value)) {
$this->recursiveKsort($value);
}
}
return ksort($array);
}
/**
* Sets a value in a nested array with variable depth.
*
* @param array $array
* A reference to the array to modify.
* @param array $parents
* An array of parent keys, starting with the outermost key.
* @param mixed $value
* The value to set.
*
* @see https://api.drupal.org/api/drupal/includes!common.inc/function/drupal_array_set_nested_value/7
*/
protected function arraySetNestedValue(array &$array, array $parents, $value) {
$ref = &$array;
foreach ($parents as $parent) {
if (isset($ref) && !is_array($ref)) {
$ref = [];
}
$ref = &$ref[$parent];
}
if (!isset($ref)) {
$ref = $value;
}
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace DrupalCodeGenerator\Command\Other;
use DrupalCodeGenerator\Command\BaseGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements other:apache-virtual-host command.
*/
class ApacheVirtualHost extends BaseGenerator {
protected $name = 'other:apache-virtual-host';
protected $description = 'Generates an Apache site configuration file';
protected $alias = 'apache-virtual-host';
protected $destination = '/etc/apache2/sites-available';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = [
'hostname' => new Question('Host name', 'example.com'),
'docroot' => new Question('Document root', '/var/www/{hostname}/public'),
];
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{hostname}.conf')
->template('other/apache-virtual-host.twig');
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace DrupalCodeGenerator\Command\Other;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements other:dcg-command command.
*/
class DcgCommand extends BaseGenerator {
protected $name = 'other:dcg-command';
protected $description = 'Generates DCG command';
protected $alias = 'dcg-command';
protected $label = 'DCG command';
/**
* {@inheritdoc}
*/
protected function configure() {
$this->destination = Utils::getHomeDirectory() . '/.dcg/Command';
parent::configure();
}
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = [
'name' => new Question('Command name', 'custom:example'),
'description' => new Question('Command description', 'Some description'),
'alias' => new Question('Command alias', 'example'),
];
$vars = &$this->collectVars($input, $output, $questions);
$sub_names = explode(':', $vars['name']);
$last_sub_name = array_pop($sub_names);
$vars['class'] = Utils::camelize($last_sub_name);
$vars['namespace'] = 'DrupalCodeGenerator\Command';
$vars['template_name'] = $last_sub_name . '.twig';
$vars['path'] = '';
$file_path = '';
if ($sub_names) {
$vars['namespace'] .= '\\' . implode('\\', $sub_names);
$file_path = implode(DIRECTORY_SEPARATOR, $sub_names);
$vars['path'] = '/' . $file_path;
}
$this->addFile()
->path($file_path . '/{class}.php')
->template('other/dcg-command.twig');
$this->addFile()
->path($file_path . '/{template_name}')
->template('other/dcg-command-template.twig');
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace DrupalCodeGenerator\Command\Other;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements other:drupal-console-command command.
*/
class DrupalConsoleCommand extends BaseGenerator {
protected $name = 'other:drupal-console-command';
protected $description = 'Generates Drupal Console command';
protected $alias = 'drupal-console-command';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions = Utils::defaultQuestions() + [
'command_name' => new Question('Command name', '{machine_name}:example'),
'description' => new Question('Command description', 'Command description.'),
'container_aware' => new ConfirmationQuestion('Make the command aware of the drupal site installation?', TRUE),
];
$vars = &$this->collectVars($input, $output, $questions);
$vars['class'] = Utils::camelize(str_replace(':', '_', $vars['command_name'])) . 'Command';
$vars['command_trait'] = $vars['container_aware'] ? 'ContainerAwareCommandTrait' : 'CommandTrait';
$this->addFile()
->path('src/Command/{class}.php')
->template('other/drupal-console-command.twig');
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace DrupalCodeGenerator\Command\Other;
use DrupalCodeGenerator\Command\BaseGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements other:drush-command command.
*/
class DrushCommand extends BaseGenerator {
protected $name = 'other:drush-command';
protected $description = 'Generates Drush command';
protected $alias = 'drush-command';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$default_alias = function ($vars) {
return substr($vars['command_name'], 0, 3);
};
$default_command_file = function ($vars) {
$directoryBaseName = basename($this->directory);
// The suggestion depends on whether the command global or local.
$prefix = $directoryBaseName == 'drush' || $directoryBaseName == '.drush' ?
$vars['command_name'] : $directoryBaseName;
return str_replace('-', '_', $prefix) . '.drush.inc';
};
$questions = [
'command_name' => new Question('Command name', ''),
'alias' => new Question('Command alias', $default_alias),
'description' => new Question('Command description', 'Command description.'),
'argument' => new Question('Argument name', 'foo'),
'option' => new Question('Option name', 'bar'),
'command_file' => new Question('Command file', $default_command_file),
];
$vars = &$this->collectVars($input, $output, $questions);
list($vars['command_file_prefix']) = explode('.drush.inc', $vars['command_file']);
// Command callback name pattern gets shorter if command file name matches
// command name.
$vars['command_callback_suffix'] = $vars['command_file_prefix'] == str_replace('-', '_', $vars['command_name'])
? $vars['command_file_prefix']
: $vars['command_file_prefix'] . '_' . $vars['command_name'];
$this->addFile()
->path('{command_file}')
->template('other/drush-command.twig');
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace DrupalCodeGenerator\Command\Other;
use DrupalCodeGenerator\Command\BaseGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements other:html-page command.
*/
class HtmlPage extends BaseGenerator {
protected $name = 'other:html-page';
protected $description = 'Generates a simple html page';
protected $alias = 'html-page';
protected $label = 'HTML page';
protected $destination = FALSE;
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$questions['file_name'] = new Question('File name', 'index.html');
$this->collectVars($input, $output, $questions);
$this->addFile()
->path('{file_name}')
->template('other/html.twig');
$this->addFile()
->path('css/main.css')
->content("body{\n background-color: #EEE;\n}\n");
$this->addFile()
->path('js/main.js')
->content("console.log('It works!');\n");
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace DrupalCodeGenerator\Command\Other;
use DrupalCodeGenerator\Command\BaseGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
/**
* Implements other:nginx-virtual-host command.
*/
class NginxVirtualHost extends BaseGenerator {
protected $name = 'other:nginx-virtual-host';
protected $description = 'Generates an Nginx site configuration file';
protected $alias = 'nginx-virtual-host';
protected $destination = '/etc/nginx/sites-available';
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
$socket = PHP_MAJOR_VERSION == 5
? '/run/php5-fpm.sock'
: sprintf('/run/php/php%s.%s-fpm.sock', PHP_MAJOR_VERSION, PHP_MINOR_VERSION);
$questions = [
'server_name' => new Question('Server name', 'example.com'),
'docroot' => new Question('Document root', '/var/www/{server_name}/docroot'),
'file_public_path' => new Question('Public file system path', 'sites/default/files'),
'file_private_path' => new Question('Private file system path'),
'fastcgi_pass' => new Question('Address of a FastCGI server', 'unix:' . $socket),
];
$vars = &$this->collectVars($input, $output, $questions);
$vars['file_public_path'] = trim($vars['file_public_path'], '/');
$vars['file_private_path'] = trim($vars['file_private_path'], '/');
$this->addFile()
->path('{server_name}')
->template('other/nginx-virtual-host.twig');
}
}

Some files were not shown because too many files have changed in this diff Show more