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,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -0,0 +1,46 @@
# Drupal Code Generator
[![Build Status](https://travis-ci.org/Chi-teck/drupal-code-generator.svg?branch=master)](https://travis-ci.org/Chi-teck/drupal-code-generator)
A command line code generator for Drupal.
## Installation
1. Download the latest [stable release](https://github.com/Chi-teck/drupal-code-generator/releases/latest) of the code generator.
2. Make the file executable.
3. Move it to a directory that is part of your `PATH`.
```shell
release_url=https://api.github.com/repos/chi-teck/drupal-code-generator/releases/latest
wget $(wget -qO- $release_url | awk -F'"' '/browser_download_url/ { print $4 }')
chmod +x dcg.phar
sudo mv dcg.phar /usr/local/bin/dcg
dcg --version
```
Installation using Composer is also supported.
## Upgrade
Simply repeat installation commands.
## Usage
```shell
# Display main menu.
dcg
# Display Drupal 8 submenu.
dcg d8
# Call generator directly.
dcg d8:plugin:field:widget
# Generate code non interactively.
dcg twig-extension -a '{"name": "Example", "machine_name": "example", "class": "ExampleTwigExtension"}'
```
## Extending
All custom generators should be placed to _$HOME/.dcg/Command_ directory. The following command will help you to get started with creating own generators.
```bash
# Create custom DCG command.
dcg dcg-command -d $HOME/.dcg/Command
```
## License
GNU General Public License, version 2 or later.

35
vendor/chi-teck/drupal-code-generator/bin/dcg vendored Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env php
<?php
use DrupalCodeGenerator\ApplicationFactory;
use DrupalCodeGenerator\Command\Navigation;
use DrupalCodeGenerator\GeneratorDiscovery;
use DrupalCodeGenerator\Utils;
use Symfony\Component\Filesystem\Filesystem;
// The autoloader may have a different location if DCG is installed as a local
// Composer package.
$autoloader = file_exists(__DIR__ . '/../vendor/autoload.php')
? require __DIR__ . '/../vendor/autoload.php'
: require __DIR__ . '/../../../autoload.php';
// Create an application.
$application = ApplicationFactory::create();
// Discover generators.
$discovery = new GeneratorDiscovery(new Filesystem());
$commands_directories[] = ApplicationFactory::getRoot() . '/src/Command';
$home = Utils::getHomeDirectory();
if (file_exists($home . '/.dcg/Command')) {
$commands_directories[] = $home . '/.dcg/Command';
$autoloader->addPsr4('DrupalCodeGenerator\\', $home . '/.dcg');
}
$generators = $discovery->getGenerators($commands_directories);
$application->addCommands($generators);
// Add the navigation command.
$application->add(new Navigation($generators));
$application->setDefaultCommand('navigation');
// Run.
$application->run();

View file

@ -0,0 +1,26 @@
{
"name": "chi-teck/drupal-code-generator",
"description": "Drupal code generator",
"require": {
"php": ">=5.5.9",
"ext-json": "*",
"symfony/console": "~2.7|^3",
"symfony/filesystem": "~2.7|^3",
"twig/twig": "^1.23.1"
},
"bin": [
"bin/dcg"
],
"autoload": {
"files": ["src/bootstrap.php"],
"psr-4": {
"DrupalCodeGenerator\\": "src"
}
},
"license": "GPL-2.0-or-later",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
}
}

File diff suppressed because one or more lines are too long

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');
}
}

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