module_builder-8.x-3.x-dev/src/Form/ComponentGenerateForm.php
src/Form/ComponentGenerateForm.php
<?php
namespace Drupal\module_builder\Form;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Render\Element;
use Drupal\module_builder\Element\GeneratedFiles;
use DrupalCodeBuilder\Exception\InvalidInputException;
/**
* Form showing generated component code.
*/
class ComponentGenerateForm extends ComponentFormBase {
use MessengerTrait;
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$component_data = $this->getComponentDataObject();
$module_name = $this->entity->id();
// Get the path to the module if it's previously been written.
$existing_module_path = $this->getExistingModule();
if ($existing_module_path) {
$this->messenger()->addWarning(t("This module already exists at @path. Some files may be possible to merge, but not all, and merges may overwrite parts of a file. It is recommended you use version control to prevent losing any existing code.", [
'@path' => $existing_module_path,
]));
$analyse_extension_task = $this->codeBuilder->getTask('AnalyseExtension');
$existing_extension = $analyse_extension_task->createExtension('module', $existing_module_path);
$file_listing = shell_exec("cd {$existing_module_path} && git ls-files");
$git_managed_files = $file_listing ? explode("\n", $file_listing) : [];
}
// Get the generation config.
$config = \Drupal::config('module_builder.settings');
$module_config = $config->get('generator_settings.module');
$config_data = \Drupal::service('module_builder.drupal_code_builder')->getTask('Configuration')->getConfigurationData('module');
$config_data->import($module_config ?? []);
// Get the files.
try {
/** @var \DrupalCodeBuilder\File\CodeFileInterface[] $files */
$files = $this->codeBuilderTaskHandlerGenerate->generateComponent($component_data, [], $config_data, $existing_extension ?? NULL);
}
catch (InvalidInputException $e) {
$this->messenger()->addError(t("Invalid input for code generator: @message", [
'@message' => $e->getMessage(),
]));
return $form;
}
if (\Drupal::moduleHandler()->moduleExists($module_name)) {
$this->messenger()->addWarning(t("This module is currently ENABLED on this site. Writing files MAY CAUSE YOUR SITE TO CRASH."));
}
$form['location'] = [
'#markup' => $this->t("Module files will be written into @dir.", [
'@dir' => \Drupal::service('module_builder.module_file_writer')->getRelativeModuleFolder($this->entity->id()),
]),
];
ksort($files);
$merge_statuses = [];
$git_statuses = [];
foreach ($files as $filename => $code_file) {
// We don't actually use the value from the form, as the POST process
// seems to be turning unix line endings into Windows line endings! Store
// it in the form state instead.
$form_state->set(['files', $filename], $code_file->getCode());
if (!$code_file->fileExists()) {
// Brand new file, no warning needed.
$merge_statuses[$filename] = GeneratedFiles::MERGE_NEW;
$git_statuses[$filename] = GeneratedFiles::VERSION_CONTROL_NEW;
}
elseif ($code_file->fileIsMerged()) {
// Exists, but merged.
$merge_statuses[$filename] = GeneratedFiles::MERGE_MERGED;
}
else {
// Exists, and not merged.
$merge_statuses[$filename] = GeneratedFiles::MERGE_OVERWRITTEN;
}
// Check git for any existing file, even if merged, as the merge may
// overwrite portions of the file.
if ($code_file->fileExists()) {
if (!in_array($filename, $git_managed_files)) {
$git_statuses[$filename] = GeneratedFiles::VERSION_CONTROL_UNMANAGED;
}
else {
// TODO: if the file is clean in git, we can maybe skip this, or say
// it's ok to overwrite?
// Perform the 'git status' command in the module folder, to allow for
// the case where the module has its own git repository.
$git_status = shell_exec("cd {$existing_module_path} && git status {$filename} --porcelain");
if (is_null($git_status)) {
// Nothing from git means that the file is clean.
$git_statuses[$filename] = GeneratedFiles::VERSION_CONTROL_MANAGED;
}
else {
$git_status_code = substr($git_status, 0, 2);
switch ($git_status_code) {
case '??':
$git_statuses[$filename] = GeneratedFiles::VERSION_CONTROL_UNMANAGED;
break;
case ' M':
$git_statuses[$filename] = GeneratedFiles::VERSION_CONTROL_CHANGED;
break;
case 'A ':
case 'M ':
case 'MM':
// Staged or partially staged file.
$git_statuses[$filename] = GeneratedFiles::VERSION_CONTROL_CHANGED;
break;
}
}
}
}
}
$form['files'] = [
'#type' => 'module_builder_generated_files',
'#files' => $files,
'#merge_statuses' => $merge_statuses,
'#git_statuses' => $git_statuses,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
// The 'write selected' button needs at least one file to be selected.
if ($form_state->getTriggeringElement()['#name'] == 'write_selected') {
$values = $form_state->getValue('generate');
$files_to_write = array_filter($values);
if (empty($files_to_write)) {
$form_state->setError($form['files']['generate'], $this->t("At least one file must be selected to write."));
}
}
}
/**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$actions = [];
$actions['write_selected'] = [
'#type' => 'submit',
'#name' => 'write_selected',
'#value' => $this->t('Write selected files'),
'#submit' => array('::writeSelected'),
'#dropbutton' => 'generate',
];
$actions['write_new'] = [
'#type' => 'submit',
'#name' => 'write_new',
'#value' => $this->t('Write new files'),
'#submit' => array('::writeNew'),
'#dropbutton' => 'generate',
];
$actions['write_all'] = [
'#type' => 'submit',
'#name' => 'write_all',
'#value' => $this->t('Write all files'),
'#submit' => array('::writeAll'),
'#dropbutton' => 'generate',
];
return $actions;
}
/**
* Returns the path to the module if it has previously been written.
*
* @return
* A Drupal-relative path to the module folder, or NULL if the module
* does not already exist.
*/
protected function getExistingModule() {
$module_name = $this->entity->id();
$registered_in_drupal = \Drupal::service('extension.list.module')->exists($module_name);
if ($registered_in_drupal) {
$module = \Drupal::service('extension.list.module')->get($module_name);
// The user may have deleted the module entirely, and in this situation
// Drupal's extension system would still have told us it exists.
$really_exists = file_exists($module->getPath());
if ($really_exists) {
return $module->getPath();
}
}
}
/**
* Submit callback to write all the module files.
*/
public function writeAll(array $form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$drupal_relative_module_dir = \Drupal::service('module_builder.module_file_writer')->getRelativeModuleFolder($this->entity);
\Drupal::service('file_system')->prepareDirectory($drupal_relative_module_dir, FileSystemInterface::CREATE_DIRECTORY);
$count_written = 0;
foreach (array_keys($values['file_code']) as $module_relative_filepath) {
$file_contents = $form_state->get(['files', $module_relative_filepath]);
$result = \Drupal::service('module_builder.module_file_writer')->writeSingleFile($drupal_relative_module_dir, $module_relative_filepath, $file_contents);
if ($result) {
$count_written++;
}
else {
$this->messenger()->addError(t("Problem writing file @file.", [
'@file' => $module_relative_filepath
]));
}
}
if ($count_written) {
$this->messenger()->addStatus(t("Written @count files to folder @folder.", [
'@count' => $count_written,
'@folder' => $drupal_relative_module_dir,
]));
}
}
/**
* Submit callback to write only the selected files.
*/
public function writeSelected(array $form, FormStateInterface $form_state) {
$values = $form_state->getValue('generate');
$files_to_write = array_filter($values);
$drupal_relative_module_dir = \Drupal::service('module_builder.module_file_writer')->getRelativeModuleFolder($this->entity->id());
$result = \Drupal::service('file_system')->prepareDirectory($drupal_relative_module_dir, FileSystemInterface::CREATE_DIRECTORY);
if (!$result) {
$this->messenger()->addError(t("Problem writing directory @dir.", [
'@dir' => $drupal_relative_module_dir,
]));
return;
}
$count_written = 0;
foreach (array_keys($files_to_write) as $module_relative_filepath) {
$file_contents = $form_state->get(['files', $module_relative_filepath]);
$result = \Drupal::service('module_builder.module_file_writer')->writeSingleFile($drupal_relative_module_dir, $module_relative_filepath, $file_contents);
if ($result) {
$count_written++;
}
else {
$this->messenger()->addError(t("Problem writing file @file.", [
'@file' => $module_relative_filepath
]));
}
}
if ($count_written) {
$this->messenger()->addStatus(t("Written @count files to folder @folder.", [
'@count' => $count_written,
'@folder' => $drupal_relative_module_dir,
]));
}
}
/**
* Submit callback to write only new module files.
*/
public function writeNew(array $form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$drupal_relative_module_dir = \Drupal::service('module_builder.module_file_writer')->getRelativeModuleFolder($this->entity->id());
\Drupal::service('file_system')->prepareDirectory($drupal_relative_module_dir, FileSystemInterface::CREATE_DIRECTORY);
$count_written = 0;
foreach (array_keys($values['file_code']) as $module_relative_filepath) {
if (file_exists($drupal_relative_module_dir . '/' . $module_relative_filepath)) {
continue;
}
$file_contents = $form_state->get(['files', $module_relative_filepath]);
$result = \Drupal::service('module_builder.module_file_writer')->writeSingleFile($drupal_relative_module_dir, $module_relative_filepath, $file_contents);
if ($result) {
$count_written++;
}
else {
$this->messenger()->addError(t("Problem writing file @file.", [
'@file' => $module_relative_filepath
]));
}
}
if ($count_written) {
$this->messenger()->addStatus(t("Written @count files to folder @folder.", [
'@count' => $count_written,
'@folder' => $drupal_relative_module_dir,
]));
}
}
/**
* Submit handler to write a single file.
*/
public function submitWriteSingle(array $form, FormStateInterface $form_state) {
$button = $form_state->getTriggeringElement();
$button_array_parents = $button['#array_parents'];
$file_key = $button_array_parents[1];
$file_contents = $form_state->get(['files', $file_key]);
$drupal_relative_module_dir = \Drupal::service('module_builder.module_file_writer')->getRelativeModuleFolder($this->entity->id());
$result = \Drupal::service('module_builder.module_file_writer')->writeSingleFile($drupal_relative_module_dir, $file_key, $file_contents);
if ($result) {
$this->messenger()->addStatus(t("Written file @file to folder @folder.", [
'@file' => $file_key,
'@folder' => $drupal_relative_module_dir,
]));
}
else {
$this->messenger()->addError(t("Problem writing file @file.", [
'@file' => $file_key,
]));
}
}
}
