Skip to content

Commit

Permalink
Merge pull request #3 from tripal/1-tripal_extension_module_template
Browse files Browse the repository at this point in the history
1 tripal extension module template
  • Loading branch information
laceysanderson authored Mar 4, 2024
2 parents 9853719 + cef123d commit 18d36cc
Show file tree
Hide file tree
Showing 11 changed files with 413 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ The following commands are currently implemented:
| tripal-chado:field-formatter | Generates a Chado Formatter to be used with an existing Chado Field. |
| tripal-chado:field-type | Generates a Chado Field Type for developing fields which store their data in chado. |
| tripal-chado:field-widget | Generates a Chado Field Widget to be used with an existing Chado Field. |
| tripal:extension-module | Generates a Tripal Extension module and its associated files. |

#### Usage:

Expand Down
132 changes: 132 additions & 0 deletions src/Drush/Generators/TripalExtensionModuleGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php declare(strict_types = 1);

namespace Drupal\tripal_devtools\Drush\Generators;

use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Extension\Exception\UnknownExtensionException;
use Drupal\Core\Extension\ModuleExtensionList;
use DrupalCodeGenerator\Application;
use DrupalCodeGenerator\Asset\AssetCollection;
use DrupalCodeGenerator\Attribute\Generator;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\GeneratorType;
use DrupalCodeGenerator\Validator\Required;
use Symfony\Component\DependencyInjection\ContainerInterface;

#[Generator(
name: 'tripal:extension-module',
description: 'Generates a Tripal extension module',
templatePath: __DIR__ . '/../../../templates/generator/tripal_extension_module',
type: GeneratorType::MODULE,
)]
final class TripalExtensionModuleGenerator extends BaseGenerator implements ContainerInjectionInterface {

/**
* {@inheritdoc}
*/
public function __construct(
private readonly ModuleExtensionList $moduleList,
) {
parent::__construct();
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): self {
return new self($container->get('extension.list.module'));
}

/**
* {@inheritdoc}
*/
protected function generate(array &$vars, AssetCollection $assets): void {
$ir = $this->createInterviewer($vars);

$vars['machine_name'] = $ir->askMachineName();
$vars['name'] = $ir->askName();

$vars['description'] = $ir->ask('Module description', 'Provides additional functionality for the site.', new Required());
$vars['package'] = $ir->ask('Package', 'Tripal Extension');

$dependencies = $ir->ask('Dependencies (comma separated)', 'tripal, tripal_chado');
// @todo Clean-up and test.
$vars['dependencies'] = $this->buildDependencies($dependencies);

$vars['class_prefix'] = '{machine_name|camelize}';

if ($ir->confirm('Would you like to create the module file?', TRUE)) {
$assets->addFile('{machine_name}/{machine_name}.module', 'module.module.twig');
}

if ($ir->confirm('Would you like to create install file (install tasks + creating tables)?', FALSE)) {
$assets->addFile('{machine_name}/{machine_name}.install', 'module.install.twig');
}

if ($ir->confirm('Would you like to create permissions.yml file?', FALSE)) {
$assets->addFile('{machine_name}/{machine_name}.permissions.yml', 'module.permissions.yml.twig');
$vars['permissions'] = TRUE;
}

if ($vars['form'] = $ir->confirm('Would you like to create settings form?', TRUE)) {
$assets->addFile('{machine_name}/src/Form/{machine_name|camelize}SettingsForm.php')
->template('SettingsForm.php.twig');
$assets->addFile('{machine_name}/config/schema/{machine_name}.schema.yml')
->template('module.schema.yml.twig');
$assets->addFile('{machine_name}/{machine_name}.links.menu.yml')
->template('module.links.menu.twig');
}

$assets->addFile('{machine_name}/{machine_name}.info.yml', 'module.info.yml.twig');

if ($vars['form']) {
$assets->addFile('{machine_name}/{machine_name}.routing.yml')
->template('module.routing.yml.twig');
}
}

/**
* Builds array of dependencies from comma-separated string.
*/
private function buildDependencies(?string $dependencies_encoded): array {

$dependencies = $dependencies_encoded ? \explode(',', $dependencies_encoded) : [];

foreach ($dependencies as &$dependency) {
$dependency = \str_replace(' ', '_', \trim(\strtolower($dependency)));
// Check if the module name is already prefixed.
if (\str_contains($dependency, ':')) {
continue;
}
// Dependencies should be namespaced in the format {project}:{name}.
$project = $dependency;
try {
// The extension list is internal for extending not for instantiating.
// @see \Drupal\Core\Extension\ExtensionList
/** @psalm-suppress InternalMethod */
$package = $this->moduleList->getExtensionInfo($dependency)['package'] ?? NULL;
if ($package === 'Core') {
$project = 'drupal';
}
}
catch (UnknownExtensionException) {

}
$dependency = $project . ':' . $dependency;
}

$dependency_sorter = static function (string $a, string $b): int {
// Core dependencies go first.
$a_is_drupal = \str_starts_with($a, 'drupal:');
$b_is_drupal = \str_starts_with($b, 'drupal:');
if ($a_is_drupal xor $b_is_drupal) {
return $a_is_drupal ? -1 : 1;
}
return $a <=> $b;
};
\uasort($dependencies, $dependency_sorter);

return $dependencies;
}

}
34 changes: 34 additions & 0 deletions templates/generator/tripal-extension-module.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php declare(strict_types = 1);
/**
* @file
* Primary module hooks for {{ name }} module.
*/
{% if create_theme %}
/**
* Implements hook_theme().
*/
function {{ machine_name }}_theme(): array {
return [
'{{ theme_key }}' => [
'variables' => ['foo' => NULL],
],
];
}
{% endif %}
{% if create_preprocess %}
/**
* Prepares variables for {{ template_name }} template.
*
* Default template: {{ template_name }}.
*
* @param array $variables
* An associative array containing:
* - foo: Foo variable description.
*/
function template_preprocess_{{ theme_key }}(array &$variables): void {
$variables['foo'] = 'bar';
}
{% endif %}
64 changes: 64 additions & 0 deletions templates/generator/tripal_extension_module/SettingsForm.php.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
namespace Drupal\{{ machine_name }}\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Configure {{ name }} settings for this site.
*/
class {{ machine_name|camelize }}SettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return '{{ machine_name }}_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['{{ machine_name }}.settings'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['example'] = [
'#type' => 'textfield',
'#title' => $this->t('Example'),
'#description' => $this->t('Please type the word "example".'),
'#default_value' => $this->config('{{ machine_name }}.settings')->get('example'),
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getValue('example') != 'example') {
$form_state->setErrorByName('example', $this->t('The value is not correct. Instead enter "example" to get validation to pass.'));
}
parent::validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$value = $form_state->getValue('example');
$this->config('{{ machine_name }}.settings')
->set('example', $value)
->save();
$this->messenger()->addStatus("Setting the value of {{ machine_name }}.settings.example to $value.");
parent::submitForm($form, $form_state);
}
}
14 changes: 14 additions & 0 deletions templates/generator/tripal_extension_module/module.info.yml.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: {{ name }}
type: module
description: {{ description }}
package: {{ package }}
core_version_requirement: ^10
{% if dependencies %}
dependencies:
{% for dependency in dependencies %}
- {{ dependency }}
{% endfor %}
{% endif %}
{% if form %}
configure: {{ machine_name }}.settings_form
{% endif %}
130 changes: 130 additions & 0 deletions templates/generator/tripal_extension_module/module.install.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php
/**
* @file
* Install, update and uninstall functions for the {{ name }} module.
*/
/**
* Implements hook_install().
*
* Use this function for all tasks which need to be executed when this module
* is installed. We suggest submitting Tripal jobs to complete any longer tasks
* such as importing an ontology.
*/
function {{ machine_name }}_install() {
// Uncomment the following code to add a Drupal status message
// showing when this function is called.
// \Drupal::messenger()->addStatus(__FUNCTION__);
}
/**
* Implements hook_uninstall().
*
* Use this method for all tasks which need to be executed when this module is
* uninstalled. We DO NOT suggest deleting any terms or data added to Chado
* by this module on uninstall.
*/
function {{ machine_name }}_uninstall() {
// Uncomment the following code to add a Drupal status message
// showing when this function is called.
// \Drupal::messenger()->addStatus(__FUNCTION__);
}
/**
* Implements hook_schema().
*
* Use the Drupal Schema API to define any database tables you want created
* in the DRUPAL Schema. Do Not use this to create Tripal/Chado Custom tables
* or Materialized Views.
*
* @see https://www.drupal.org/docs/7/api/schema-api/schema-reference
*/
function {{ machine_name }}_schema() {
$schema = [];
// Uncomment the following code example to create the {{ machine_name }}_example
// table in the Drupal Schema. The {{ machine_name }}_example table has
// id, uid, status, type created, and data columns, where the primary key is the id.
/* @code-sample
$schema['{{ machine_name }}_example'] = [
'description' => 'Table description.',
'fields' => [
'id' => [
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique record ID.',
],
'uid' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {users}.uid of the user who created the record.',
],
'status' => [
'description' => 'Boolean indicating whether this record is active.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
],
'type' => [
'type' => 'varchar_ascii',
'length' => 64,
'not null' => TRUE,
'default' => '',
'description' => 'Type of the record.',
],
'created' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Timestamp when the record was created.',
],
'data' => [
'type' => 'blob',
'not null' => TRUE,
'size' => 'big',
'description' => 'The arbitrary data for the item.',
],
],
'primary key' => ['id'],
'indexes' => [
'type' => ['type'],
'uid' => ['uid'],
'status' => ['status'],
],
];
*/
return $schema;
}
/**
* Implements hook_requirements().
*
* Use this function to define specific requirements for your module.
* If the severity key is set to warning or error then your module will
* not be able to be installed.
*/
function {{ machine_name }}_requirements($phase) {
$requirements = [];
// This example requirement is only run when checking requirements
// through the UI. It simply generates a random number between 1-100
// and produces a warning if it's above 50. This should show up on the
// Drupal Report Status page.
if ($phase == 'runtime') {
$value = mt_rand(0, 100);
$requirements['{{ machine_name }}_status'] = [
'title' => t('{{ name }} status'),
'value' => t('{{ name }} value: @value', ['@value' => $value]),
'severity' => $value > 50 ? REQUIREMENT_INFO : REQUIREMENT_WARNING,
];
}
return $requirements;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{ machine_name }}.settings_form:
title: '{{ name }}'
description: 'Configure {{ name }}.'
parent: tripal.extension
route_name: {{ machine_name }}.settings_form
weight: 10
Loading

0 comments on commit 18d36cc

Please sign in to comment.