diff --git a/README.md b/README.md index 9db709f..4df3b13 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/src/Drush/Generators/TripalExtensionModuleGenerator.php b/src/Drush/Generators/TripalExtensionModuleGenerator.php new file mode 100644 index 0000000..b94dd15 --- /dev/null +++ b/src/Drush/Generators/TripalExtensionModuleGenerator.php @@ -0,0 +1,132 @@ +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; + } + +} diff --git a/templates/generator/tripal-extension-module.twig b/templates/generator/tripal-extension-module.twig new file mode 100644 index 0000000..3f9155f --- /dev/null +++ b/templates/generator/tripal-extension-module.twig @@ -0,0 +1,34 @@ + [ + '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 %} diff --git a/templates/generator/tripal_extension_module/SettingsForm.php.twig b/templates/generator/tripal_extension_module/SettingsForm.php.twig new file mode 100644 index 0000000..8cf59a7 --- /dev/null +++ b/templates/generator/tripal_extension_module/SettingsForm.php.twig @@ -0,0 +1,64 @@ + '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); + } + +} diff --git a/templates/generator/tripal_extension_module/module.info.yml.twig b/templates/generator/tripal_extension_module/module.info.yml.twig new file mode 100644 index 0000000..48c2c61 --- /dev/null +++ b/templates/generator/tripal_extension_module/module.info.yml.twig @@ -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 %} \ No newline at end of file diff --git a/templates/generator/tripal_extension_module/module.install.twig b/templates/generator/tripal_extension_module/module.install.twig new file mode 100644 index 0000000..3395aa6 --- /dev/null +++ b/templates/generator/tripal_extension_module/module.install.twig @@ -0,0 +1,130 @@ +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; +} diff --git a/templates/generator/tripal_extension_module/module.links.menu.twig b/templates/generator/tripal_extension_module/module.links.menu.twig new file mode 100644 index 0000000..84b080b --- /dev/null +++ b/templates/generator/tripal_extension_module/module.links.menu.twig @@ -0,0 +1,6 @@ +{{ machine_name }}.settings_form: + title: '{{ name }}' + description: 'Configure {{ name }}.' + parent: tripal.extension + route_name: {{ machine_name }}.settings_form + weight: 10 \ No newline at end of file diff --git a/templates/generator/tripal_extension_module/module.module.twig b/templates/generator/tripal_extension_module/module.module.twig new file mode 100644 index 0000000..10f8318 --- /dev/null +++ b/templates/generator/tripal_extension_module/module.module.twig @@ -0,0 +1,6 @@ +