From 22bcc619fafce3b907ea599eaa184ab773fb1572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 10:27:01 -0500 Subject: [PATCH 01/36] Start moving installer functionality into a recipe-kit library --- dev.composer.json | 7 +- installer_kit/composer.json | 11 ++ .../src/RecipeAppliedSubscriber.php | 6 +- installer_kit/src/RecipeKit.php | 104 +++++++++++++++++ .../drupal_cms_installer.profile | 106 +----------------- .../drupal_cms_installer.services.yml | 2 +- .../src/Functional/CommandLineInstallTest.php | 2 +- .../src/Functional/InteractiveInstallTest.php | 2 +- 8 files changed, 132 insertions(+), 108 deletions(-) create mode 100644 installer_kit/composer.json rename {project_template/web/profiles/drupal_cms_installer => installer_kit}/src/RecipeAppliedSubscriber.php (90%) create mode 100644 installer_kit/src/RecipeKit.php diff --git a/dev.composer.json b/dev.composer.json index ee2687b85..2887ba367 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -56,6 +56,10 @@ "type": "path", "url": "recipes/drupal_cms_image" }, + "installer_kit": { + "type": "path", + "url": "installer_kit" + }, "news": { "type": "path", "url": "recipes/drupal_cms_news" @@ -103,7 +107,8 @@ }, "require-dev": { "drupal/core-dev": "^11.1.1", - "drupal/default_content": "^2" + "drupal/default_content": "^2", + "drupal/recipe-installer-kit": "*" }, "autoload-dev": { "files": [ diff --git a/installer_kit/composer.json b/installer_kit/composer.json new file mode 100644 index 000000000..5bddccb20 --- /dev/null +++ b/installer_kit/composer.json @@ -0,0 +1,11 @@ +{ + "name": "drupal/recipe-installer-kit", + "require": { + "drupal/core": ">=10.4" + }, + "autoload": { + "psr-4": { + "Drupal\\Installer\\RecipeKit\\": "src" + } + } +} diff --git a/project_template/web/profiles/drupal_cms_installer/src/RecipeAppliedSubscriber.php b/installer_kit/src/RecipeAppliedSubscriber.php similarity index 90% rename from project_template/web/profiles/drupal_cms_installer/src/RecipeAppliedSubscriber.php rename to installer_kit/src/RecipeAppliedSubscriber.php index d9babe337..6505bd74a 100644 --- a/project_template/web/profiles/drupal_cms_installer/src/RecipeAppliedSubscriber.php +++ b/installer_kit/src/RecipeAppliedSubscriber.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\drupal_cms_installer; +namespace Drupal\Installer\RecipeKit; use Drupal\Core\Recipe\RecipeAppliedEvent; use Drupal\Core\State\StateInterface; @@ -19,8 +19,8 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; * without applying recipes that have already been applied successfully. Once * the install is done, the list of recipes is deleted. * - * @see drupal_cms_installer_apply_recipes() - * @see drupal_cms_installer_uninstall_myself() + * @see \Drupal\Installer\RecipeKit\RecipeKit::applyRecipes() + * @see \Drupal\Installer\RecipeKit\RecipeKit::uninstallSelf() */ final class RecipeAppliedSubscriber implements EventSubscriberInterface { diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php new file mode 100644 index 000000000..4d736ae10 --- /dev/null +++ b/installer_kit/src/RecipeKit.php @@ -0,0 +1,104 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Installer\RecipeKit; + +use Composer\InstalledVersions; +use Drupal\Core\Extension\ModuleInstallerInterface; +use Drupal\Core\Messenger\MessengerInterface; +use Drupal\Core\Recipe\Recipe; +use Drupal\Core\Recipe\RecipeRunner; + +final class RecipeKit { + + /** + * Decorates install_install_profile(), ensuring User is installed first. + */ + public static function installSelf(): void { + // We'll need User to configure the site and administrator account. + \Drupal::service(ModuleInstallerInterface::class)->install(['user']); + + // Officially install the profile so that its behaviors and visual overrides + // will be in effect for the remainder of the install process. This also + // ensures that the administrator role is created and assigned to user 1 in + // the next step. + global $install_state; + install_install_profile($install_state); + } + + /** + * Runs a batch job that applies the chosen set of recipes. + * + * @param array $install_state + * An array of information about the current installation state. + * + * @return array + * The batch job definition. + */ + public static function applyRecipes(array &$install_state): array { + // If the installer ran before but failed mid-stream, don't reapply any + // recipes that were successfully applied. + $recipes_to_apply = array_diff( + $install_state['parameters']['recipes'], + \Drupal::state()->get(RecipeAppliedSubscriber::STATE_KEY, []), + ); + + // If we've already applied all the chosen recipes, there's nothing to do. + // Since we only start applying recipes once `install_profile_modules()` has + // finished, we can be safely certain that we already did that step. + if (empty($recipes_to_apply)) { + return []; + } + + $batch = install_profile_modules($install_state); + $batch['title'] = t('Setting up your site'); + + $recipe_operations = []; + + foreach ($recipes_to_apply as $name) { + $recipe = InstalledVersions::getInstallPath('drupal/' . $name); + $recipe = Recipe::createFromDirectory($recipe); + $recipe_operations = array_merge($recipe_operations, RecipeRunner::toBatchOperations($recipe)); + } + + // Only do each recipe's batch operations once. + foreach ($recipe_operations as $operation) { + if (in_array($operation, $batch['operations'], TRUE)) { + continue; + } + else { + $batch['operations'][] = $operation; + } + } + + return $batch; + } + + /** + * Uninstalls this install profile, as a final step. + * + * @see drupal_install_system() + */ + public static function uninstallSelf(): void { + global $install_state; + \Drupal::service(ModuleInstallerInterface::class)->uninstall([ + $install_state['parameters']['profile'], + ]); + + // The install is done, so we don't need the list of applied recipes anymore. + \Drupal::state()->delete(RecipeAppliedSubscriber::STATE_KEY); + + // Clear all previous status messages to avoid clutter. + \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_STATUS); + + // Invalidate the container in case any stray requests were made during the + // install process, which would have bootstrapped Drupal and cached the + // install-time container, which is now stale (during the installer, the + // container cannot be dumped, which would normally happen during the + // container rebuild triggered by uninstalling this profile). We do not want + // to redirect into Drupal with a stale container. + \Drupal::service('kernel')->invalidateContainer(); + } + +} diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 1ad097584..89e9dc083 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -2,19 +2,14 @@ declare(strict_types=1); -use Composer\InstalledVersions; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Render\Element\Password; -use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Messenger\MessengerInterface; -use Drupal\Core\Recipe\Recipe; -use Drupal\Core\Recipe\RecipeRunner; use Drupal\drupal_cms_installer\Form\RecipesForm; use Drupal\drupal_cms_installer\Form\SiteNameForm; use Drupal\drupal_cms_installer\MessageInterceptor; -use Drupal\drupal_cms_installer\RecipeAppliedSubscriber; +use Drupal\Installer\RecipeKit\RecipeKit; const SQLITE_DRIVER = 'Drupal\sqlite\Driver\Database\sqlite'; @@ -37,8 +32,9 @@ function drupal_cms_installer_install_tasks(): array { } return [ - 'drupal_cms_installer_uninstall_myself' => [ + 'uninstall_self' => [ // As a final task, this profile should uninstall itself. + 'function' => RecipeKit::class . '::uninstallSelf', ], ]; } @@ -80,7 +76,7 @@ function drupal_cms_installer_install_tasks_alter(array &$tasks, array $install_ ); $insert_before('install_profile_modules', [ 'install_install_profile' => [ - 'function' => 'drupal_cms_installer_install_myself', + 'function' => RecipeKit::class . '::installSelf', ], 'install_configure_form' => $configure_form_task, ]); @@ -91,26 +87,7 @@ function drupal_cms_installer_install_tasks_alter(array &$tasks, array $install_ // Wrap the install_profile_modules() function, which returns a batch job, and // add all the necessary operations to apply the chosen template recipe. - $tasks['install_profile_modules']['function'] = 'drupal_cms_installer_apply_recipes'; -} - -/** - * Installs the User module, and this profile. - * - * @param array $install_state - * The current installation state. - */ -function drupal_cms_installer_install_myself(array &$install_state): void { - // We'll need User installed for the next step, which is configuring the site - // and administrator account. - \Drupal::service(ModuleInstallerInterface::class)->install([ - 'user', - ]); - // Officially install this profile so that its behaviors and visual overrides - // will be in effect for the remainder of the install process. This also - // ensures that the administrator role is created and assigned to user 1 in - // the next step. - install_install_profile($install_state); + $tasks['install_profile_modules']['function'] = RecipeKit::class . '::applyRecipes'; } /** @@ -228,54 +205,6 @@ function _drupal_cms_installer_password_value(&$element, $input, FormStateInterf return Password::valueCallback($element, $input, $form_state); } -/** - * Runs a batch job that applies the template and add-on recipes. - * - * @param array $install_state - * An array of information about the current installation state. - * - * @return array - * The batch job definition. - */ -function drupal_cms_installer_apply_recipes(array &$install_state): array { - // If the installer ran before but failed mid-stream, don't reapply any - // recipes that were successfully applied. - $recipes_to_apply = array_diff( - $install_state['parameters']['recipes'], - \Drupal::state()->get(RecipeAppliedSubscriber::STATE_KEY, []), - ); - - // If we've already applied all the chosen recipes, there's nothing to do. - // Since we only start applying recipes once `install_profile_modules()` has - // finished, we can be safely certain that we already did that step. - if (empty($recipes_to_apply)) { - return []; - } - - $batch = install_profile_modules($install_state); - $batch['title'] = t('Setting up your site'); - - $recipe_operations = []; - - foreach ($recipes_to_apply as $name) { - $recipe = InstalledVersions::getInstallPath('drupal/' . $name); - $recipe = Recipe::createFromDirectory($recipe); - $recipe_operations = array_merge($recipe_operations, RecipeRunner::toBatchOperations($recipe)); - } - - // Only do each recipe's batch operations once. - foreach ($recipe_operations as $operation) { - if (in_array($operation, $batch['operations'], TRUE)) { - continue; - } - else { - $batch['operations'][] = $operation; - } - } - - return $batch; -} - /** * Implements hook_library_info_alter(). */ @@ -298,31 +227,6 @@ function drupal_cms_installer_library_info_alter(array &$libraries, string $exte } } -/** - * Uninstalls this install profile, as a final step. - * - * @see drupal_install_system() - */ -function drupal_cms_installer_uninstall_myself(): void { - \Drupal::service(ModuleInstallerInterface::class)->uninstall([ - 'drupal_cms_installer', - ]); - - // The install is done, so we don't need the list of applied recipes anymore. - \Drupal::state()->delete(RecipeAppliedSubscriber::STATE_KEY); - - // Clear all previous status messages to avoid clutter. - \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_STATUS); - - // Invalidate the container in case any stray requests were made during the - // install process, which would have bootstrapped Drupal and cached the - // install-time container, which is now stale (during the installer, the - // container cannot be dumped, which would normally happen during the - // container rebuild triggered by uninstalling this profile). We do not want - // to redirect into Drupal with a stale container. - \Drupal::service('kernel')->invalidateContainer(); -} - /** * Implements hook_theme_registry_alter(). */ diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml index 40478993c..46dee6ef5 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml @@ -1,5 +1,5 @@ services: - Drupal\drupal_cms_installer\RecipeAppliedSubscriber: + Drupal\Installer\RecipeKit\RecipeAppliedSubscriber: autowire: true tags: - { name: event_subscriber } diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php index e70bf3a88..2c2f0a83a 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Drupal\Tests\drupal_cms_installer\Functional; use Drupal\Core\Test\TestSetupTrait; -use Drupal\drupal_cms_installer\RecipeAppliedSubscriber; +use Drupal\Installer\RecipeKit\RecipeAppliedSubscriber; use Drush\TestTraits\DrushTestTrait; use PHPUnit\Framework\TestCase; use Symfony\Component\Filesystem\Filesystem; diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php index 10e629b46..8c0e24f0f 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php @@ -10,7 +10,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\State\StateInterface; -use Drupal\drupal_cms_installer\RecipeAppliedSubscriber; +use Drupal\Installer\RecipeKit\RecipeAppliedSubscriber; use Drupal\FunctionalTests\Installer\InstallerTestBase; use Drupal\user\Entity\User; use Symfony\Component\DependencyInjection\ContainerInterface; -- GitLab From ac18627a5394dcba279fa2447fd421c64cbb5034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 10:54:03 -0500 Subject: [PATCH 02/36] Move form alters into RecipeKit --- installer_kit/src/RecipeKit.php | 137 ++++++++++++++++++ .../drupal_cms_installer.profile | 117 +-------------- 2 files changed, 140 insertions(+), 114 deletions(-) diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php index 4d736ae10..ddb0d0f43 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/RecipeKit.php @@ -6,9 +6,11 @@ namespace Drupal\Installer\RecipeKit; use Composer\InstalledVersions; use Drupal\Core\Extension\ModuleInstallerInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipeRunner; +use Drupal\Core\Render\Element\Password; final class RecipeKit { @@ -27,6 +29,141 @@ final class RecipeKit { install_install_profile($install_state); } + /** + * Implements hook_form_alter(). + */ + public static function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void { + switch ($form_id) { + case 'install_configure_form': + static::installConfigureFormAlter($form); + break; + + case 'install_settings_form': + static::installSettingsFormAlter($form); + break; + + default: + break; + } + } + + /** + * Implements hook_form_alter() for install_configure_form. + * + * @see \Drupal\Core\Installer\Form\SiteConfigureForm + */ + private static function installConfigureFormAlter(array &$form): void { + global $install_state; + + $form['#title'] = t('Create your account'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('Creating an account allows you to log in to your site.'), + '#suffix' => '</p>', + '#weight' => -40, + ]; + + $form['site_information']['#type'] = 'container'; + // We collected the site name in a previous step. + $form['site_information']['site_name'] = [ + '#type' => 'hidden', + '#default_value' => $GLOBALS['install_state']['parameters']['site_name'], + ]; + + // Use a custom submit handler to set the site email. + unset($form['site_information']['site_mail']); + $form['#submit'][] = static::class . '::setSiteMail'; + + $form['admin_account']['#type'] = 'container'; + // `admin` is a sensible name for user 1. + $form['admin_account']['account']['name'] = [ + '#type' => 'hidden', + '#default_value' => 'admin', + ]; + $form['admin_account']['account']['mail'] = [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + '#type' => 'email', + '#title' => t('Email'), + '#required' => TRUE, + '#default_value' => $install_state['forms']['install_configure_form']['account']['mail'] ?? '', + '#weight' => 10, + ]; + $form['admin_account']['account']['pass'] = [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + '#type' => 'password', + '#title' => t('Password'), + '#required' => TRUE, + '#default_value' => $install_state['forms']['install_configure_form']['account']['pass']['pass1'] ?? '', + '#weight' => 20, + '#value_callback' => static::class . '::passwordValue', + ]; + + // Hide the timezone selection. Core automatically uses client-side JavaScript + // to detect it, but we don't need to expose that to the user. But the + // JavaScript expects the form elements to look a certain way, so hiding the + // fields visually is the correct approach here. + // @see core/misc/timezone.js + $form['regional_settings']['#attributes']['class'][] = 'visually-hidden'; + // Don't allow the timezone selection to be tab-focused. + $form['regional_settings']['date_default_timezone']['#attributes']['tabindex'] = -1; + + // We always install Automatic Updates, so we don't need to expose the update + // notification settings. + $form['update_notifications']['#access'] = FALSE; + + $form['actions']['submit']['#value'] = t('Finish'); + } + + public static function passwordValue(&$element, $input, FormStateInterface $form_state): mixed { + // Work around this fact that Drush and `drupal install`, which submit this + // form programmatically, assume the password is a password_confirm element. + if (is_array($input) && $form_state->isProgrammed()) { + $input = $input['pass1']; + } + return Password::valueCallback($element, $input, $form_state); + } + + /** + * Custom submit handler to update the site email. + */ + public static function setSiteMail(array &$form, FormStateInterface $form_state): void { + \Drupal::configFactory() + ->getEditable('system.site') + ->set('mail', $form_state->getValue(['account', 'mail'])) + ->save(); + } + + /** + * Implements hook_form_alter() for install_settings_form. + * + * @see \Drupal\Core\Installer\Form\SiteSettingsForm + */ + private static function installSettingsFormAlter(array &$form): void { + $sqlite = 'Drupal\sqlite\Driver\Database\sqlite'; + + // Default to SQLite, if available, because it doesn't require any additional + // configuration. + if (extension_loaded('pdo_sqlite') && array_key_exists($sqlite, $form['driver']['#options'])) { + $form['driver']['#default_value'] = $sqlite; + $form['driver']['#type'] = 'select'; + + // The database file path has a sensible default value, so move it into the + // advanced options. + $form['settings'][$sqlite]['advanced_options']['database'] = $form['settings'][$sqlite]['database']; + unset($form['settings'][$sqlite]['database']); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t("You don't need to change anything here unless you want to use a different database type."), + '#suffix' => '</p>', + '#weight' => -50, + ]; + } + } + /** * Runs a batch job that applies the chosen set of recipes. * diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 89e9dc083..dc2a09f89 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -3,7 +3,6 @@ declare(strict_types=1); use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Render\Element\Password; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\drupal_cms_installer\Form\RecipesForm; @@ -11,8 +10,6 @@ use Drupal\drupal_cms_installer\Form\SiteNameForm; use Drupal\drupal_cms_installer\MessageInterceptor; use Drupal\Installer\RecipeKit\RecipeKit; -const SQLITE_DRIVER = 'Drupal\sqlite\Driver\Database\sqlite'; - /** * Implements hook_install_tasks(). */ @@ -91,118 +88,10 @@ function drupal_cms_installer_install_tasks_alter(array &$tasks, array $install_ } /** - * Implements hook_form_alter() for install_settings_form. - * - * @see \Drupal\Core\Installer\Form\SiteSettingsForm + * Implements hook_form_alter(). */ -function drupal_cms_installer_form_install_settings_form_alter(array &$form): void { - // Default to SQLite, if available, because it doesn't require any additional - // configuration. - if (extension_loaded('pdo_sqlite') && array_key_exists(SQLITE_DRIVER, $form['driver']['#options'])) { - $form['driver']['#default_value'] = SQLITE_DRIVER; - $form['driver']['#type'] = 'select'; - - // The database file path has a sensible default value, so move it into the - // advanced options. - $form['settings'][SQLITE_DRIVER]['advanced_options']['database'] = $form['settings'][SQLITE_DRIVER]['database']; - unset($form['settings'][SQLITE_DRIVER]['database']); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t("You don't need to change anything here unless you want to use a different database type."), - '#suffix' => '</p>', - '#weight' => -50, - ]; - } -} - -/** - * Implements hook_form_alter() for install_configure_form. - * - * @see \Drupal\Core\Installer\Form\SiteConfigureForm - */ -function drupal_cms_installer_form_install_configure_form_alter(array &$form, FormStateInterface $form_state): void { - global $install_state; - - $form['#title'] = t('Create your account'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t('Creating an account allows you to log in to your site.'), - '#suffix' => '</p>', - '#weight' => -40, - ]; - - $form['site_information']['#type'] = 'container'; - // We collected the site name in a previous step. - $form['site_information']['site_name'] = [ - '#type' => 'hidden', - '#default_value' => $GLOBALS['install_state']['parameters']['site_name'], - ]; - - // Use a custom submit handler to set the site email. - unset($form['site_information']['site_mail']); - $form['#submit'][] = 'drupal_cms_installer_update_site_mail'; - - $form['admin_account']['#type'] = 'container'; - // `admin` is a sensible name for user 1. - $form['admin_account']['account']['name'] = [ - '#type' => 'hidden', - '#default_value' => 'admin', - ]; - $form['admin_account']['account']['mail'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - '#type' => 'email', - '#title' => t('Email'), - '#required' => TRUE, - '#default_value' => $install_state['forms']['install_configure_form']['account']['mail'] ?? '', - '#weight' => 10, - ]; - $form['admin_account']['account']['pass'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - '#type' => 'password', - '#title' => t('Password'), - '#required' => TRUE, - '#default_value' => $install_state['forms']['install_configure_form']['account']['pass']['pass1'] ?? '', - '#weight' => 20, - '#value_callback' => '_drupal_cms_installer_password_value', - ]; - - // Hide the timezone selection. Core automatically uses client-side JavaScript - // to detect it, but we don't need to expose that to the user. But the - // JavaScript expects the form elements to look a certain way, so hiding the - // fields visually is the correct approach here. - // @see core/misc/timezone.js - $form['regional_settings']['#attributes']['class'][] = 'visually-hidden'; - // Don't allow the timezone selection to be tab-focused. - $form['regional_settings']['date_default_timezone']['#attributes']['tabindex'] = -1; - - // We always install Automatic Updates, so we don't need to expose the update - // notification settings. - $form['update_notifications']['#access'] = FALSE; - - $form['actions']['submit']['#value'] = t('Finish'); -} - -/** - * Custom submit handler to update the site email. - */ -function drupal_cms_installer_update_site_mail(array &$form, FormStateInterface $form_state): void { - \Drupal::configFactory() - ->getEditable('system.site') - ->set('mail', $form_state->getValue(['account', 'mail'])) - ->save(); -} - -function _drupal_cms_installer_password_value(&$element, $input, FormStateInterface $form_state): mixed { - // Work around this fact that Drush and `drupal install`, which submit this - // form programmatically, assume the password is a password_confirm element. - if (is_array($input) && $form_state->isProgrammed()) { - $input = $input['pass1']; - } - return Password::valueCallback($element, $input, $form_state); +function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { + RecipeKit::formAlter($form, $form_state, $form_id); } /** -- GitLab From dc09796c8443ce30ff68e8bb67fd0843647711ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 11:07:37 -0500 Subject: [PATCH 03/36] Move the remainder of the runtime code and forms into the recipe kit --- .../src/Form/RecipesForm.php | 2 +- .../src/Form/SiteNameForm.php | 2 +- .../src/MessageInterceptor.php | 2 +- installer_kit/src/RecipeAppliedSubscriber.php | 2 +- installer_kit/src/RecipeKit.php | 81 ++++++++++++++++++- .../drupal_cms_installer.profile | 71 +--------------- .../src/Functional/InteractiveInstallTest.php | 2 +- 7 files changed, 85 insertions(+), 77 deletions(-) rename {project_template/web/profiles/drupal_cms_installer => installer_kit}/src/Form/RecipesForm.php (98%) rename {project_template/web/profiles/drupal_cms_installer => installer_kit}/src/Form/SiteNameForm.php (97%) rename {project_template/web/profiles/drupal_cms_installer => installer_kit}/src/MessageInterceptor.php (98%) diff --git a/project_template/web/profiles/drupal_cms_installer/src/Form/RecipesForm.php b/installer_kit/src/Form/RecipesForm.php similarity index 98% rename from project_template/web/profiles/drupal_cms_installer/src/Form/RecipesForm.php rename to installer_kit/src/Form/RecipesForm.php index 38c0de6dc..158399cf6 100644 --- a/project_template/web/profiles/drupal_cms_installer/src/Form/RecipesForm.php +++ b/installer_kit/src/Form/RecipesForm.php @@ -1,6 +1,6 @@ <?php -namespace Drupal\drupal_cms_installer\Form; +namespace Drupal\Installer\RecipeKit\Form; use Drupal\Core\Form\FormBase; use Composer\InstalledVersions; diff --git a/project_template/web/profiles/drupal_cms_installer/src/Form/SiteNameForm.php b/installer_kit/src/Form/SiteNameForm.php similarity index 97% rename from project_template/web/profiles/drupal_cms_installer/src/Form/SiteNameForm.php rename to installer_kit/src/Form/SiteNameForm.php index 445ca9764..278efddb5 100644 --- a/project_template/web/profiles/drupal_cms_installer/src/Form/SiteNameForm.php +++ b/installer_kit/src/Form/SiteNameForm.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\drupal_cms_installer\Form; +namespace Drupal\Installer\RecipeKit\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; diff --git a/project_template/web/profiles/drupal_cms_installer/src/MessageInterceptor.php b/installer_kit/src/MessageInterceptor.php similarity index 98% rename from project_template/web/profiles/drupal_cms_installer/src/MessageInterceptor.php rename to installer_kit/src/MessageInterceptor.php index 1f5958065..7a5167484 100644 --- a/project_template/web/profiles/drupal_cms_installer/src/MessageInterceptor.php +++ b/installer_kit/src/MessageInterceptor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\drupal_cms_installer; +namespace Drupal\Installer\RecipeKit; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; diff --git a/installer_kit/src/RecipeAppliedSubscriber.php b/installer_kit/src/RecipeAppliedSubscriber.php index 6505bd74a..fc780b21d 100644 --- a/installer_kit/src/RecipeAppliedSubscriber.php +++ b/installer_kit/src/RecipeAppliedSubscriber.php @@ -20,7 +20,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; * the install is done, the list of recipes is deleted. * * @see \Drupal\Installer\RecipeKit\RecipeKit::applyRecipes() - * @see \Drupal\Installer\RecipeKit\RecipeKit::uninstallSelf() + * @see \Drupal\Installer\RecipeKit\RecipeKit::uninstallProfile() */ final class RecipeAppliedSubscriber implements EventSubscriberInterface { diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php index ddb0d0f43..c86efe960 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/RecipeKit.php @@ -5,19 +5,94 @@ declare(strict_types=1); namespace Drupal\Installer\RecipeKit; use Composer\InstalledVersions; +use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipeRunner; use Drupal\Core\Render\Element\Password; +use Drupal\Installer\RecipeKit\Form\RecipesForm; +use Drupal\Installer\RecipeKit\Form\SiteNameForm; final class RecipeKit { + /** + * Implements hook_install_tasks(). + */ + public static function installTasks(): array { + // If the container can be altered, wrap the messenger service to suppress + // certain messages. + $container = \Drupal::getContainer(); + if ($container instanceof ContainerBuilder) { + $container->set('messenger', new MessageInterceptor( + \Drupal::messenger(), + )); + } + + return [ + 'uninstall_profile' => [ + // As a final task, uninstall the install profile. + 'function' => static::class . '::uninstallProfile', + ], + ]; + } + + /** + * Implements hook_install_tasks_alter(). + */ + public static function installTasksAlter(array &$tasks, array $install_state): void { + $insert_before = function (string $key, array $additions) use (&$tasks): void { + $key = array_search($key, array_keys($tasks), TRUE); + if ($key === FALSE) { + return; + } + // This isn't very clean, but it's the only way to positionally splice into + // an associative (and therefore by definition unordered) array. + $tasks_before = array_slice($tasks, 0, $key, TRUE); + $tasks_after = array_slice($tasks, $key, NULL, TRUE); + $tasks = $tasks_before + $additions + $tasks_after; + }; + $insert_before('install_settings_form', [ + 'drupal_cms_installer_choose_recipes' => [ + 'display_name' => t('Choose add-ons'), + 'type' => 'form', + 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, + 'function' => RecipesForm::class, + ], + 'drupal_cms_installer_site_name_form' => [ + 'display_name' => t('Name your site'), + 'type' => 'form', + 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, + 'function' => SiteNameForm::class, + ], + ]); + + $configure_form_task = $tasks['install_configure_form']; + unset( + $tasks['install_install_profile'], + $tasks['install_configure_form'], + ); + $insert_before('install_profile_modules', [ + 'install_install_profile' => [ + 'function' => RecipeKit::class . '::installProfile', + ], + 'install_configure_form' => $configure_form_task, + ]); + + // Set English as the default language; it can be changed mid-stream. We can't + // use the passed-in $install_state because it's not passed by reference. + $GLOBALS['install_state']['parameters'] += ['langcode' => 'en']; + + // Wrap the install_profile_modules() function, which returns a batch job, and + // add all the necessary operations to apply the chosen template recipe. + $tasks['install_profile_modules']['function'] = RecipeKit::class . '::applyRecipes'; + } + /** * Decorates install_install_profile(), ensuring User is installed first. */ - public static function installSelf(): void { + public static function installProfile(): void { // We'll need User to configure the site and administrator account. \Drupal::service(ModuleInstallerInterface::class)->install(['user']); @@ -213,11 +288,11 @@ final class RecipeKit { } /** - * Uninstalls this install profile, as a final step. + * Uninstalls the install profile, as a final step. * * @see drupal_install_system() */ - public static function uninstallSelf(): void { + public static function uninstallProfile(): void { global $install_state; \Drupal::service(ModuleInstallerInterface::class)->uninstall([ $install_state['parameters']['profile'], diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index dc2a09f89..7f581087a 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -2,89 +2,22 @@ declare(strict_types=1); -use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\drupal_cms_installer\Form\RecipesForm; -use Drupal\drupal_cms_installer\Form\SiteNameForm; -use Drupal\drupal_cms_installer\MessageInterceptor; use Drupal\Installer\RecipeKit\RecipeKit; /** * Implements hook_install_tasks(). */ function drupal_cms_installer_install_tasks(): array { - // Ensure our forms are loadable in all situations, even if the installer is - // not a Composer-managed package. - \Drupal::service('class_loader') - ->addPsr4('Drupal\\drupal_cms_installer\\', __DIR__ . '/src'); - - // If the container can be altered, wrap the messenger service to suppress - // certain messages. - $container = \Drupal::getContainer(); - if ($container instanceof ContainerBuilder) { - $container->set('messenger', new MessageInterceptor( - \Drupal::messenger(), - )); - } - - return [ - 'uninstall_self' => [ - // As a final task, this profile should uninstall itself. - 'function' => RecipeKit::class . '::uninstallSelf', - ], - ]; + return RecipeKit::installTasks(); } /** * Implements hook_install_tasks_alter(). */ function drupal_cms_installer_install_tasks_alter(array &$tasks, array $install_state): void { - $insert_before = function (string $key, array $additions) use (&$tasks): void { - $key = array_search($key, array_keys($tasks), TRUE); - if ($key === FALSE) { - return; - } - // This isn't very clean, but it's the only way to positionally splice into - // an associative (and therefore by definition unordered) array. - $tasks_before = array_slice($tasks, 0, $key, TRUE); - $tasks_after = array_slice($tasks, $key, NULL, TRUE); - $tasks = $tasks_before + $additions + $tasks_after; - }; - $insert_before('install_settings_form', [ - 'drupal_cms_installer_choose_recipes' => [ - 'display_name' => t('Choose add-ons'), - 'type' => 'form', - 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => RecipesForm::class, - ], - 'drupal_cms_installer_site_name_form' => [ - 'display_name' => t('Name your site'), - 'type' => 'form', - 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => SiteNameForm::class, - ], - ]); - - $configure_form_task = $tasks['install_configure_form']; - unset( - $tasks['install_install_profile'], - $tasks['install_configure_form'], - ); - $insert_before('install_profile_modules', [ - 'install_install_profile' => [ - 'function' => RecipeKit::class . '::installSelf', - ], - 'install_configure_form' => $configure_form_task, - ]); - - // Set English as the default language; it can be changed mid-stream. We can't - // use the passed-in $install_state because it's not passed by reference. - $GLOBALS['install_state']['parameters'] += ['langcode' => 'en']; - - // Wrap the install_profile_modules() function, which returns a batch job, and - // add all the necessary operations to apply the chosen template recipe. - $tasks['install_profile_modules']['function'] = RecipeKit::class . '::applyRecipes'; + RecipeKit::installTasksAlter($tasks, $install_state); } /** diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php index 8c0e24f0f..78c33fc02 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php @@ -110,7 +110,7 @@ class InteractiveInstallTest extends InstallerTestBase { $this->assertNull($this->container->get(StateInterface::class)->get(RecipeAppliedSubscriber::STATE_KEY)); // The site name and site-wide email address should have been set. - // @see \Drupal\drupal_cms_installer\Form\SiteNameForm + // @see \Drupal\Installer\RecipeKit\Form\SiteNameForm $site_config = $this->config('system.site'); $this->assertSame('Installer Test', $site_config->get('name')); $this->assertSame("hello@good.bye", $site_config->get('mail')); -- GitLab From c0020c2c7985081df3a3013fd5411e6506662e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 11:10:25 -0500 Subject: [PATCH 04/36] Remove most mentions of Drupal CMS from RecipeKit --- installer_kit/src/Form/RecipesForm.php | 2 +- installer_kit/src/Form/SiteNameForm.php | 2 +- installer_kit/src/RecipeAppliedSubscriber.php | 2 +- installer_kit/src/RecipeKit.php | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/installer_kit/src/Form/RecipesForm.php b/installer_kit/src/Form/RecipesForm.php index 158399cf6..fc49c76f1 100644 --- a/installer_kit/src/Form/RecipesForm.php +++ b/installer_kit/src/Form/RecipesForm.php @@ -20,7 +20,7 @@ final class RecipesForm extends FormBase { * {@inheritdoc} */ public function getFormId(): string { - return 'drupal_cms_installer_recipes_form'; + return 'installer_recipes_form'; } /** diff --git a/installer_kit/src/Form/SiteNameForm.php b/installer_kit/src/Form/SiteNameForm.php index 278efddb5..3acf919a4 100644 --- a/installer_kit/src/Form/SiteNameForm.php +++ b/installer_kit/src/Form/SiteNameForm.php @@ -16,7 +16,7 @@ final class SiteNameForm extends FormBase { * {@inheritdoc} */ public function getFormId(): string { - return 'drupal_cms_installer_site_name_form'; + return 'installer_site_name_form'; } /** diff --git a/installer_kit/src/RecipeAppliedSubscriber.php b/installer_kit/src/RecipeAppliedSubscriber.php index fc780b21d..e2741d1a7 100644 --- a/installer_kit/src/RecipeAppliedSubscriber.php +++ b/installer_kit/src/RecipeAppliedSubscriber.php @@ -29,7 +29,7 @@ final class RecipeAppliedSubscriber implements EventSubscriberInterface { * * @var string */ - public const STATE_KEY = 'drupal_cms_installer.applied_recipes'; + public const STATE_KEY = 'installer.applied_recipes'; public function __construct( private readonly StateInterface $state, diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php index c86efe960..44f144500 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/RecipeKit.php @@ -54,13 +54,13 @@ final class RecipeKit { $tasks = $tasks_before + $additions + $tasks_after; }; $insert_before('install_settings_form', [ - 'drupal_cms_installer_choose_recipes' => [ + 'installer_recipes_form' => [ 'display_name' => t('Choose add-ons'), 'type' => 'form', 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, 'function' => RecipesForm::class, ], - 'drupal_cms_installer_site_name_form' => [ + 'installer_site_name_form' => [ 'display_name' => t('Name your site'), 'type' => 'form', 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, -- GitLab From 75c024bc800421b3adfcd1808f3f3eed0b11f557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 11:22:02 -0500 Subject: [PATCH 05/36] Read required and optional recipe lists from the install profile --- installer_kit/src/Form/RecipesForm.php | 18 ++++++------------ installer_kit/src/RecipeKit.php | 2 +- .../drupal_cms_installer.info.yml | 10 ++++++++++ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/installer_kit/src/Form/RecipesForm.php b/installer_kit/src/Form/RecipesForm.php index fc49c76f1..584a99b73 100644 --- a/installer_kit/src/Form/RecipesForm.php +++ b/installer_kit/src/Form/RecipesForm.php @@ -26,7 +26,7 @@ final class RecipesForm extends FormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state): array { + public function buildForm(array $form, FormStateInterface $form_state, ?array $install_state = NULL): array { $form['#title'] = $this->t('Get started'); $form['help'] = [ @@ -42,21 +42,15 @@ final class RecipesForm extends FormBase { '#value_callback' => static::class . '::valueCallback', ]; - $base_recipe_path = InstalledVersions::getInstallPath('drupal/drupal_cms_starter'); - $cookbook_path = dirname($base_recipe_path); - - // Read the list of optional recipes from the base recipe's `composer.json`. - $composer = file_get_contents($base_recipe_path . '/composer.json'); - $composer = json_decode($composer, TRUE, flags: JSON_THROW_ON_ERROR); - $optional_recipes = array_keys($composer['suggest'] ?? []); + assert(is_array($install_state)); + $optional_recipes = $install_state['profile_info']['recipes']['optional'] ?? []; foreach ($optional_recipes as $name) { - $recipe = $cookbook_path . '/' . basename($name) . '/recipe.yml'; + $recipe = InstalledVersions::getInstallPath($name) . '/recipe.yml'; if (file_exists($recipe)) { $recipe = file_get_contents($recipe); $recipe = Yaml::decode($recipe); - $key = basename($name); - $form['add_ons']['#options'][$key] = $recipe['name']; + $form['add_ons']['#options'][$name] = $recipe['name']; } } @@ -92,7 +86,7 @@ final class RecipesForm extends FormBase { */ public function submitForm(array &$form, FormStateInterface $form_state): void { global $install_state; - $install_state['parameters']['recipes'] = ['drupal_cms_starter']; + $install_state['parameters']['recipes'] = $install_state['profile_info']['recipes']['required'] ?? []; $pressed_button = $form_state->getTriggeringElement(); // Only choose add-ons if the Next button was pressed, or if the form was diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php index 44f144500..4f386f0b4 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/RecipeKit.php @@ -269,7 +269,7 @@ final class RecipeKit { $recipe_operations = []; foreach ($recipes_to_apply as $name) { - $recipe = InstalledVersions::getInstallPath('drupal/' . $name); + $recipe = InstalledVersions::getInstallPath($name); $recipe = Recipe::createFromDirectory($recipe); $recipe_operations = array_merge($recipe_operations, RecipeRunner::toBatchOperations($recipe)); } diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index bd7a5238a..cd135e2f5 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -12,3 +12,13 @@ distribution: # injecting Stark into it. # @see install_profile_info() themes: [] +recipes: + required: + - drupal/drupal_cms_starter + optional: + - drupal/drupal_cms_blog + - drupal/drupal_cms_case_study + - drupal/drupal_cms_events + - drupal/drupal_cms_news + - drupal/drupal_cms_person + - drupal/drupal_cms_project -- GitLab From 13799e8fa809f6478932fbdd2bcc45cab918ac61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 11:26:46 -0500 Subject: [PATCH 06/36] Decouple look and feel from the profile --- .../drupal_cms_installer/drupal_cms_installer.profile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 7f581087a..935201714 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -32,10 +32,11 @@ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_ */ function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { global $install_state; + $profile = $install_state['parameters']['profile']; // If a library file's path starts with `/`, the library collection system // treats it as relative to the base path. // @see \Drupal\Core\Asset\LibraryDiscoveryParser::buildByExtension() - $base_path = '/' . $install_state['profiles']['drupal_cms_installer']->getPath(); + $base_path = '/' . $install_state['profiles'][$profile]->getPath(); if ($extension === 'claro') { $libraries['maintenance-page']['css']['theme']["$base_path/css/gin-variables.css"] = []; @@ -54,7 +55,8 @@ function drupal_cms_installer_library_info_alter(array &$libraries, string $exte */ function drupal_cms_installer_theme_registry_alter(array &$hooks): void { global $install_state; - $installer_path = $install_state['profiles']['drupal_cms_installer']->getPath(); + $profile = $install_state['parameters']['profile']; + $installer_path = $install_state['profiles'][$profile]->getPath(); $hooks['install_page']['path'] = $installer_path . '/templates'; } @@ -67,7 +69,8 @@ function drupal_cms_installer_preprocess_install_page(array &$variables): void { unset($variables['page']['sidebar_first'], $variables['site_version']); global $install_state; - $images_path = $install_state['profiles']['drupal_cms_installer']->getPath() . '/images'; + $profile = $install_state['parameters']['profile']; + $images_path = $install_state['profiles'][$profile]->getPath() . '/images'; $images_path = \Drupal::service(FileUrlGeneratorInterface::class) ->generateString($images_path); $variables['images_path'] = $images_path; -- GitLab From abdaf710a7d9f066cbe0ac75da326fe6e112394e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 11:36:49 -0500 Subject: [PATCH 07/36] Move theme stuff into RecipeKit --- installer_kit/src/RecipeKit.php | 28 +++++++++++++++++++ .../drupal_cms_installer.profile | 21 +++----------- .../templates/install-page.html.twig | 7 +---- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php index 4f386f0b4..01ef09122 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/RecipeKit.php @@ -7,6 +7,7 @@ namespace Drupal\Installer\RecipeKit; use Composer\InstalledVersions; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Extension\ModuleInstallerInterface; +use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Recipe\Recipe; @@ -287,6 +288,33 @@ final class RecipeKit { return $batch; } + /** + * Implements hook_theme_registry_alter(). + */ + public static function themeRegistryAlter(array &$hooks): void { + global $install_state; + $profile = $install_state['parameters']['profile']; + + $template = sprintf( + '%s/templates/install-page.html.twig', + $install_state['profiles'][$profile]->getPath(), + ); + if (file_exists($template)) { + $hooks['install_page']['path'] = dirname($template); + } + } + + /** + * Preprocess function for all theme hooks. + */ + public static function preprocess(array &$variables): void { + global $install_state; + $profile = $install_state['parameters']['profile']; + $profile_path = $install_state['profiles'][$profile]->getPath(); + $variables['profile_path'] = \Drupal::service(FileUrlGeneratorInterface::class) + ->generateString($profile_path); + } + /** * Uninstalls the install profile, as a final step. * diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 935201714..0d0a9229d 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -2,7 +2,6 @@ declare(strict_types=1); -use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Installer\RecipeKit\RecipeKit; @@ -54,24 +53,12 @@ function drupal_cms_installer_library_info_alter(array &$libraries, string $exte * Implements hook_theme_registry_alter(). */ function drupal_cms_installer_theme_registry_alter(array &$hooks): void { - global $install_state; - $profile = $install_state['parameters']['profile']; - $installer_path = $install_state['profiles'][$profile]->getPath(); - - $hooks['install_page']['path'] = $installer_path . '/templates'; + RecipeKit::themeRegistryAlter($hooks); } /** - * Preprocess function for all pages in the installer. + * Preprocess function for all theme hooks. */ -function drupal_cms_installer_preprocess_install_page(array &$variables): void { - // Don't show the task list or the version of Drupal. - unset($variables['page']['sidebar_first'], $variables['site_version']); - - global $install_state; - $profile = $install_state['parameters']['profile']; - $images_path = $install_state['profiles'][$profile]->getPath() . '/images'; - $images_path = \Drupal::service(FileUrlGeneratorInterface::class) - ->generateString($images_path); - $variables['images_path'] = $images_path; +function drupal_cms_installer_preprocess(array &$variables): void { + RecipeKit::preprocess($variables); } diff --git a/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig b/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig index f5d3676bf..eea39d1a7 100644 --- a/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig +++ b/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig @@ -12,15 +12,10 @@ <div class="cms-installer"> <header class="cms-installer__header"> <h1 class="cms-installer__heading"> - <img src="{{ images_path }}/drupal-cms-logo.svg" alt="{{ 'Drupal CMS'|t }}" /> + <img src="{{ profile_path }}/images/drupal-cms-logo.svg" alt="{{ 'Drupal CMS'|t }}" /> </h1> </header> <div class="cms-installer__wrapper"> - {% if page.sidebar_first %} - <aside class="cms-installer__sidebar-first"> - {{ page.sidebar_first }} - </aside> - {% endif %} <main class="cms-installer__main"> {% if title %} -- GitLab From 07d0af70abdb880c882cdac010078be2889219c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 12:15:14 -0500 Subject: [PATCH 08/36] Move library stuff into RecipeKit --- installer_kit/src/RecipeKit.php | 50 ++++++++++++++++++- .../drupal_cms_installer.info.yml | 13 +++++ .../drupal_cms_installer.profile | 18 +------ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/RecipeKit.php index 01ef09122..13998bf72 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/RecipeKit.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Drupal\Installer\RecipeKit; use Composer\InstalledVersions; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\File\FileUrlGeneratorInterface; @@ -304,14 +305,61 @@ final class RecipeKit { } } + public static function libraryInfoAlter(array &$libraries, string $extension): void { + global $install_state; + $library_overrides = $install_state['profile_info']['libraries-override'] ?? []; + + foreach ($library_overrides as $name => $override) { + [$overridden_extension, $library_name] = explode('/', $name, 2); + if ($extension === $overridden_extension) { + $libraries[$library_name] = NestedArray::mergeDeep( + $libraries[$library_name], + static::prefixLibraryAssets($override), + ); + } + } + } + + private static function prefixLibraryAssets(array $library): array { + $profile_path = static::getProfilePath(); + + $prefix_keys = function (array $items) use ($profile_path): array { + foreach ($items as $key => $value) { + // If a library file's path starts with `/`, the library collection + // system treats it as relative to the base path. + // @see \Drupal\Core\Asset\LibraryDiscoveryParser::buildByExtension() + $items["$profile_path/$key"] = $value; + unset($items[$key]); + } + return $items; + }; + + foreach ($library as $type => $section) { + if ($type === 'js') { + $library[$type] = $prefix_keys($section); + } + elseif ($type === 'css') { + foreach ($section as $subtype => $subsection) { + $library[$type][$subtype] = $prefix_keys($subsection); + } + } + } + return $library; + } + /** * Preprocess function for all theme hooks. */ public static function preprocess(array &$variables): void { + $variables['profile_path'] = static::getProfilePath(); + } + + private static function getProfilePath(): string { global $install_state; $profile = $install_state['parameters']['profile']; $profile_path = $install_state['profiles'][$profile]->getPath(); - $variables['profile_path'] = \Drupal::service(FileUrlGeneratorInterface::class) + + return \Drupal::service(FileUrlGeneratorInterface::class) ->generateString($profile_path); } diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index cd135e2f5..cfe3c2914 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -22,3 +22,16 @@ recipes: - drupal/drupal_cms_news - drupal/drupal_cms_person - drupal/drupal_cms_project +libraries-override: + claro/maintenance-page: + css: + theme: + css/gin-variables.css: {} + css/fonts.css: {} + css/installer-styles.css: {} + css/add-ons.css: {} + dependencies: + - core/once + core/drupal.progress: + js: + js/progress.js: {} diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 0d0a9229d..554a14abb 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -30,23 +30,7 @@ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_ * Implements hook_library_info_alter(). */ function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { - global $install_state; - $profile = $install_state['parameters']['profile']; - // If a library file's path starts with `/`, the library collection system - // treats it as relative to the base path. - // @see \Drupal\Core\Asset\LibraryDiscoveryParser::buildByExtension() - $base_path = '/' . $install_state['profiles'][$profile]->getPath(); - - if ($extension === 'claro') { - $libraries['maintenance-page']['css']['theme']["$base_path/css/gin-variables.css"] = []; - $libraries['maintenance-page']['css']['theme']["$base_path/css/fonts.css"] = []; - $libraries['maintenance-page']['css']['theme']["$base_path/css/installer-styles.css"] = []; - $libraries['maintenance-page']['css']['theme']["$base_path/css/add-ons.css"] = []; - $libraries['maintenance-page']['dependencies'][] = 'core/once'; - } - if ($extension === 'core') { - $libraries['drupal.progress']['js']["$base_path/js/progress.js"] = []; - } + RecipeKit::libraryInfoAlter($libraries, $extension); } /** -- GitLab From e398d6a185d13c90a1cc6c443f5fc84f134ed2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 12:29:54 -0500 Subject: [PATCH 09/36] Create a Drush generator --- .../Generators/RecipeInstallerGenerator.php | 34 +++++++++++++ installer_kit/src/Drush/Generators/info.twig | 35 ++++++++++++++ .../src/Drush/Generators/profile.twig | 48 +++++++++++++++++++ .../src/Drush/Generators/services.twig | 5 ++ .../Generators/user.role.administrator.yml | 8 ++++ 5 files changed, 130 insertions(+) create mode 100644 installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php create mode 100644 installer_kit/src/Drush/Generators/info.twig create mode 100644 installer_kit/src/Drush/Generators/profile.twig create mode 100644 installer_kit/src/Drush/Generators/services.twig create mode 100644 installer_kit/src/Drush/Generators/user.role.administrator.yml diff --git a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php new file mode 100644 index 000000000..9dc9ad76d --- /dev/null +++ b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Installer\RecipeKit\Drush\Generators; + +use DrupalCodeGenerator\Asset\AssetCollection as Assets; +use DrupalCodeGenerator\Attribute\Generator; +use DrupalCodeGenerator\Command\BaseGenerator; + +#[Generator( + name: 'recipe-kit:installer', + description: 'Generates a stub install profile built on the recipe installer kit.', + templatePath: __DIR__, +)] +final class RecipeInstallerGenerator extends BaseGenerator { + + /** + * {@inheritdoc} + */ + protected function generate(array &$vars, Assets $assets): void { + $ir = $this->createInterviewer($vars); + + $vars['machine_name'] = $ir->askMachineName(); + $vars['name'] = $ir->askName(); + $vars['finish_url'] = $ir->ask('Enter a Drupal path where users should be redirected after installing.'); + + $assets->addFile('{machine_name}.info.yml', 'info.twig'); + $assets->addFile('{machine_name}.profile', 'profile.twig'); + $assets->addFile('{machine_name}.services.yml', 'services.twig'); + $assets->addFile('config/install/user.role.administrator.yml', 'user.role.administrator.yml'); + } + +} diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig new file mode 100644 index 000000000..4962512f7 --- /dev/null +++ b/installer_kit/src/Drush/Generators/info.twig @@ -0,0 +1,35 @@ +name: {{ name }} +type: profile + +# Change this to whatever your minimum required version of core is, depending on the +# needs of the recipes you want to apply. +core_version_requirement: '^11.1' + +description: 'Provides install-time tweaks. Not to be used in production.' + +# Use this `distribution` key in order to skip the installer's profile selection step. +distribution: + name: {{ name }} +{% if finish_url %} + install: + # Set this to a URL where the user should be redirected after install. + finish_url: '{{ finish_url }}' +{% endif %} + +# Explicitly provide an empty list of themes -- this prevents the installer from +# injecting Stark into it. It's best not to change this. +# @see install_profile_info() +themes: [] + +recipes: + # This should be a list of recipes' Composer package names that should always be + # applied during the install process. + required: [] + + # This should be a list of recipes' Composer package names that should be shown to the + # user as choices at the start of the install process. + optional: [] + +# To control the look and feel of the install process, you can override various libraries +# here, using the same syntax as you would in a theme. +libraries-override: {} diff --git a/installer_kit/src/Drush/Generators/profile.twig b/installer_kit/src/Drush/Generators/profile.twig new file mode 100644 index 000000000..5ceb6f4ee --- /dev/null +++ b/installer_kit/src/Drush/Generators/profile.twig @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +use Drupal\Core\Form\FormStateInterface; +use Drupal\Installer\RecipeKit\RecipeKit; + +/** + * Implements hook_install_tasks(). + */ +function {{ machine_name }}_install_tasks(): array { + return RecipeKit::installTasks(); +} + +/** + * Implements hook_install_tasks_alter(). + */ +function {{ machine_name }}_install_tasks_alter(array &$tasks, array $install_state): void { + RecipeKit::installTasksAlter($tasks, $install_state); +} + +/** + * Implements hook_form_alter(). + */ +function {{ machine_name }}_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { + RecipeKit::formAlter($form, $form_state, $form_id); +} + +/** + * Implements hook_library_info_alter(). + */ +function {{ machine_name }}_library_info_alter(array &$libraries, string $extension): void { + RecipeKit::libraryInfoAlter($libraries, $extension); +} + +/** + * Implements hook_theme_registry_alter(). + */ +function {{ machine_name }}_theme_registry_alter(array &$hooks): void { + RecipeKit::themeRegistryAlter($hooks); +} + +/** + * Preprocess function for all theme hooks. + */ +function {{ machine_name }}_preprocess(array &$variables): void { + RecipeKit::preprocess($variables); +} diff --git a/installer_kit/src/Drush/Generators/services.twig b/installer_kit/src/Drush/Generators/services.twig new file mode 100644 index 000000000..46dee6ef5 --- /dev/null +++ b/installer_kit/src/Drush/Generators/services.twig @@ -0,0 +1,5 @@ +services: + Drupal\Installer\RecipeKit\RecipeAppliedSubscriber: + autowire: true + tags: + - { name: event_subscriber } diff --git a/installer_kit/src/Drush/Generators/user.role.administrator.yml b/installer_kit/src/Drush/Generators/user.role.administrator.yml new file mode 100644 index 000000000..ca48a58b4 --- /dev/null +++ b/installer_kit/src/Drush/Generators/user.role.administrator.yml @@ -0,0 +1,8 @@ +langcode: en +status: true +dependencies: { } +id: administrator +label: Administrator +weight: 3 +is_admin: true +permissions: { } -- GitLab From 704a4cc8e7f274a3cb083918e281a1bff77c6c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 13:03:03 -0500 Subject: [PATCH 10/36] Smooth out the generator and add a readme --- installer_kit/README.md | 32 +++++++++++++++++++ installer_kit/composer.json | 3 +- .../Generators/RecipeInstallerGenerator.php | 1 + .../src/Drush/Generators/composer.twig | 6 ++++ project_template/composer.json | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 installer_kit/README.md create mode 100644 installer_kit/src/Drush/Generators/composer.twig diff --git a/installer_kit/README.md b/installer_kit/README.md new file mode 100644 index 000000000..93f6a4746 --- /dev/null +++ b/installer_kit/README.md @@ -0,0 +1,32 @@ +# Drupal Recipe Installer Kit +If you thought the Drupal CMS installer was really slick, you might have wondered how to adapt it for your own nefarious purposes. Until now, it was impossible; your only option was to completely fork the installer. + +This package is a toolkit to make create install profiles that work similarly to the Drupal CMS installer -- the same user flow and functionality. But you can customize the stuff that really matters: +* Which recipes are shown to users at the beginning +* Which recipes are _always_ applied +* Where users should be redirected afterwards +* How the installer looks and feels + +This is for developers who want a better, recipe-based installer experience, but don't want to write a pile of complicated PHP code to bend the Drupal installer to their will. + +## How to use +First, require this package into your project: +```shell +composer require drupal/recipe-installer-kit +``` +Then, use Drush to generate a stub install profile: +```shell +drush generate recipe-kit:installer --destination=profiles/SOME_MACHINE_NAME +``` +Then go to the profile's directory and start editing the `SOME_MACHINE_NAME.info.yml` file. + +To edit the look and feel, you can create a `templates/install-page.html.twig` file by copying it from `core/modules/system/templates/install-page.html.twig`. This is more or less the only template used by the installer. You can use the info file's `libraries-override` section to alter the CSS and JavaScript from Drupal core. + +Profiles generated from this package should include this package as a Composer dependency. This means that your generated profile should have a `composer.json` that contains the following: +```json +"require": { + "drupal/recipe-installer-kit": "dev-main" +} +``` + +An example use case is [Drupal CMS's project template](https://git.drupalcode.org/project/cms), which uses this package to generate its install profile. diff --git a/installer_kit/composer.json b/installer_kit/composer.json index 5bddccb20..d6eb524d2 100644 --- a/installer_kit/composer.json +++ b/installer_kit/composer.json @@ -1,7 +1,8 @@ { "name": "drupal/recipe-installer-kit", "require": { - "drupal/core": ">=10.4" + "drupal/core": ">=10.4", + "drush/drush": "^13" }, "autoload": { "psr-4": { diff --git a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php index 9dc9ad76d..13a31375f 100644 --- a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php +++ b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php @@ -28,6 +28,7 @@ final class RecipeInstallerGenerator extends BaseGenerator { $assets->addFile('{machine_name}.info.yml', 'info.twig'); $assets->addFile('{machine_name}.profile', 'profile.twig'); $assets->addFile('{machine_name}.services.yml', 'services.twig'); + $assets->addFile('composer.json', 'composer.twig'); $assets->addFile('config/install/user.role.administrator.yml', 'user.role.administrator.yml'); } diff --git a/installer_kit/src/Drush/Generators/composer.twig b/installer_kit/src/Drush/Generators/composer.twig new file mode 100644 index 000000000..ad23892db --- /dev/null +++ b/installer_kit/src/Drush/Generators/composer.twig @@ -0,0 +1,6 @@ +{ + "require": { + "drupal/recipe-installer-kit": "dev-main" + }, + "type": "drupal-profile" +} diff --git a/project_template/composer.json b/project_template/composer.json index fd08374a6..0b4d70c0b 100644 --- a/project_template/composer.json +++ b/project_template/composer.json @@ -34,6 +34,7 @@ "drupal/drupal_cms_seo_tools": "^1.0.1", "drupal/drupal_cms_starter": "^1.0.1", "drupal/project_browser": "@alpha", + "drupal/recipe-installer-kit": "dev-main@dev", "drupal/webform": "@beta", "drush/drush": "^13" }, -- GitLab From 126e9fe4abc9d2d08e379f36a222f7d4d2bf5a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 13:28:35 -0500 Subject: [PATCH 11/36] Rename --- installer_kit/composer.json | 2 +- installer_kit/src/Drush/Generators/profile.twig | 14 +++++++------- installer_kit/src/Drush/Generators/services.twig | 2 +- installer_kit/src/Form/RecipesForm.php | 2 +- installer_kit/src/Form/SiteNameForm.php | 2 +- installer_kit/src/{RecipeKit.php => Installer.php} | 8 ++++---- installer_kit/src/MessageInterceptor.php | 2 +- installer_kit/src/RecipeAppliedSubscriber.php | 2 +- 8 files changed, 17 insertions(+), 17 deletions(-) rename installer_kit/src/{RecipeKit.php => Installer.php} (98%) diff --git a/installer_kit/composer.json b/installer_kit/composer.json index d6eb524d2..80ddf69dc 100644 --- a/installer_kit/composer.json +++ b/installer_kit/composer.json @@ -6,7 +6,7 @@ }, "autoload": { "psr-4": { - "Drupal\\Installer\\RecipeKit\\": "src" + "Drupal\\RecipeKit\\": "src" } } } diff --git a/installer_kit/src/Drush/Generators/profile.twig b/installer_kit/src/Drush/Generators/profile.twig index 5ceb6f4ee..fe9f9630e 100644 --- a/installer_kit/src/Drush/Generators/profile.twig +++ b/installer_kit/src/Drush/Generators/profile.twig @@ -3,46 +3,46 @@ declare(strict_types=1); use Drupal\Core\Form\FormStateInterface; -use Drupal\Installer\RecipeKit\RecipeKit; +use Drupal\RecipeKit\Installer; /** * Implements hook_install_tasks(). */ function {{ machine_name }}_install_tasks(): array { - return RecipeKit::installTasks(); + return Installer::installTasks(); } /** * Implements hook_install_tasks_alter(). */ function {{ machine_name }}_install_tasks_alter(array &$tasks, array $install_state): void { - RecipeKit::installTasksAlter($tasks, $install_state); + Installer::installTasksAlter($tasks, $install_state); } /** * Implements hook_form_alter(). */ function {{ machine_name }}_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { - RecipeKit::formAlter($form, $form_state, $form_id); + Installer::formAlter($form, $form_state, $form_id); } /** * Implements hook_library_info_alter(). */ function {{ machine_name }}_library_info_alter(array &$libraries, string $extension): void { - RecipeKit::libraryInfoAlter($libraries, $extension); + Installer::libraryInfoAlter($libraries, $extension); } /** * Implements hook_theme_registry_alter(). */ function {{ machine_name }}_theme_registry_alter(array &$hooks): void { - RecipeKit::themeRegistryAlter($hooks); + Installer::themeRegistryAlter($hooks); } /** * Preprocess function for all theme hooks. */ function {{ machine_name }}_preprocess(array &$variables): void { - RecipeKit::preprocess($variables); + Installer::preprocess($variables); } diff --git a/installer_kit/src/Drush/Generators/services.twig b/installer_kit/src/Drush/Generators/services.twig index 46dee6ef5..8600226df 100644 --- a/installer_kit/src/Drush/Generators/services.twig +++ b/installer_kit/src/Drush/Generators/services.twig @@ -1,5 +1,5 @@ services: - Drupal\Installer\RecipeKit\RecipeAppliedSubscriber: + Drupal\RecipeKit\RecipeAppliedSubscriber: autowire: true tags: - { name: event_subscriber } diff --git a/installer_kit/src/Form/RecipesForm.php b/installer_kit/src/Form/RecipesForm.php index 584a99b73..5aeb8c564 100644 --- a/installer_kit/src/Form/RecipesForm.php +++ b/installer_kit/src/Form/RecipesForm.php @@ -1,6 +1,6 @@ <?php -namespace Drupal\Installer\RecipeKit\Form; +namespace Drupal\RecipeKit\Form; use Drupal\Core\Form\FormBase; use Composer\InstalledVersions; diff --git a/installer_kit/src/Form/SiteNameForm.php b/installer_kit/src/Form/SiteNameForm.php index 3acf919a4..34fb82fa7 100644 --- a/installer_kit/src/Form/SiteNameForm.php +++ b/installer_kit/src/Form/SiteNameForm.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Installer\RecipeKit\Form; +namespace Drupal\RecipeKit\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; diff --git a/installer_kit/src/RecipeKit.php b/installer_kit/src/Installer.php similarity index 98% rename from installer_kit/src/RecipeKit.php rename to installer_kit/src/Installer.php index 13998bf72..c6247c82d 100644 --- a/installer_kit/src/RecipeKit.php +++ b/installer_kit/src/Installer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Installer\RecipeKit; +namespace Drupal\RecipeKit; use Composer\InstalledVersions; use Drupal\Component\Utility\NestedArray; @@ -17,7 +17,7 @@ use Drupal\Core\Render\Element\Password; use Drupal\Installer\RecipeKit\Form\RecipesForm; use Drupal\Installer\RecipeKit\Form\SiteNameForm; -final class RecipeKit { +final class Installer { /** * Implements hook_install_tasks(). @@ -77,7 +77,7 @@ final class RecipeKit { ); $insert_before('install_profile_modules', [ 'install_install_profile' => [ - 'function' => RecipeKit::class . '::installProfile', + 'function' => Installer::class . '::installProfile', ], 'install_configure_form' => $configure_form_task, ]); @@ -88,7 +88,7 @@ final class RecipeKit { // Wrap the install_profile_modules() function, which returns a batch job, and // add all the necessary operations to apply the chosen template recipe. - $tasks['install_profile_modules']['function'] = RecipeKit::class . '::applyRecipes'; + $tasks['install_profile_modules']['function'] = Installer::class . '::applyRecipes'; } /** diff --git a/installer_kit/src/MessageInterceptor.php b/installer_kit/src/MessageInterceptor.php index 7a5167484..2c2c6cbff 100644 --- a/installer_kit/src/MessageInterceptor.php +++ b/installer_kit/src/MessageInterceptor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Installer\RecipeKit; +namespace Drupal\RecipeKit; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; diff --git a/installer_kit/src/RecipeAppliedSubscriber.php b/installer_kit/src/RecipeAppliedSubscriber.php index e2741d1a7..e66e25429 100644 --- a/installer_kit/src/RecipeAppliedSubscriber.php +++ b/installer_kit/src/RecipeAppliedSubscriber.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Installer\RecipeKit; +namespace Drupal\RecipeKit; use Drupal\Core\Recipe\RecipeAppliedEvent; use Drupal\Core\State\StateInterface; -- GitLab From 6b9479f81f02bf24dc4ec6e55de9316a56a7cacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 13:40:00 -0500 Subject: [PATCH 12/36] Moar renames --- .../src/Drush/Generators/profile.twig | 14 ++++----- ...cipesForm.php => InstallerRecipesForm.php} | 2 +- ...NameForm.php => InstallerSiteNameForm.php} | 2 +- .../src/{Installer.php => InstallerHooks.php} | 31 ++++++++++++------- ...Interceptor.php => InstallerMessenger.php} | 27 +++++++++++----- installer_kit/src/RecipeAppliedSubscriber.php | 4 +-- .../drupal_cms_installer.profile | 22 ++++++++----- 7 files changed, 66 insertions(+), 36 deletions(-) rename installer_kit/src/Form/{RecipesForm.php => InstallerRecipesForm.php} (98%) rename installer_kit/src/Form/{SiteNameForm.php => InstallerSiteNameForm.php} (96%) rename installer_kit/src/{Installer.php => InstallerHooks.php} (94%) rename installer_kit/src/{MessageInterceptor.php => InstallerMessenger.php} (69%) diff --git a/installer_kit/src/Drush/Generators/profile.twig b/installer_kit/src/Drush/Generators/profile.twig index fe9f9630e..39120a99e 100644 --- a/installer_kit/src/Drush/Generators/profile.twig +++ b/installer_kit/src/Drush/Generators/profile.twig @@ -3,46 +3,46 @@ declare(strict_types=1); use Drupal\Core\Form\FormStateInterface; -use Drupal\RecipeKit\Installer; +use Drupal\RecipeKit\InstallerHooks; /** * Implements hook_install_tasks(). */ function {{ machine_name }}_install_tasks(): array { - return Installer::installTasks(); + return InstallerHooks::installTasks(); } /** * Implements hook_install_tasks_alter(). */ function {{ machine_name }}_install_tasks_alter(array &$tasks, array $install_state): void { - Installer::installTasksAlter($tasks, $install_state); + InstallerHooks::installTasksAlter($tasks, $install_state); } /** * Implements hook_form_alter(). */ function {{ machine_name }}_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { - Installer::formAlter($form, $form_state, $form_id); + InstallerHooks::formAlter($form, $form_state, $form_id); } /** * Implements hook_library_info_alter(). */ function {{ machine_name }}_library_info_alter(array &$libraries, string $extension): void { - Installer::libraryInfoAlter($libraries, $extension); + InstallerHooks::libraryInfoAlter($libraries, $extension); } /** * Implements hook_theme_registry_alter(). */ function {{ machine_name }}_theme_registry_alter(array &$hooks): void { - Installer::themeRegistryAlter($hooks); + InstallerHooks::themeRegistryAlter($hooks); } /** * Preprocess function for all theme hooks. */ function {{ machine_name }}_preprocess(array &$variables): void { - Installer::preprocess($variables); + InstallerHooks::preprocess($variables); } diff --git a/installer_kit/src/Form/RecipesForm.php b/installer_kit/src/Form/InstallerRecipesForm.php similarity index 98% rename from installer_kit/src/Form/RecipesForm.php rename to installer_kit/src/Form/InstallerRecipesForm.php index 5aeb8c564..510692a10 100644 --- a/installer_kit/src/Form/RecipesForm.php +++ b/installer_kit/src/Form/InstallerRecipesForm.php @@ -14,7 +14,7 @@ use Drupal\Core\Render\Element\Checkboxes; * @todo Present this as a mini project browser once * https://www.drupal.org/i/3450629 is fixed. */ -final class RecipesForm extends FormBase { +final class InstallerRecipesForm extends FormBase { /** * {@inheritdoc} diff --git a/installer_kit/src/Form/SiteNameForm.php b/installer_kit/src/Form/InstallerSiteNameForm.php similarity index 96% rename from installer_kit/src/Form/SiteNameForm.php rename to installer_kit/src/Form/InstallerSiteNameForm.php index 34fb82fa7..e01eb91fb 100644 --- a/installer_kit/src/Form/SiteNameForm.php +++ b/installer_kit/src/Form/InstallerSiteNameForm.php @@ -10,7 +10,7 @@ use Drupal\Core\Form\FormStateInterface; /** * Defines a form to set the site name. */ -final class SiteNameForm extends FormBase { +final class InstallerSiteNameForm extends FormBase { /** * {@inheritdoc} diff --git a/installer_kit/src/Installer.php b/installer_kit/src/InstallerHooks.php similarity index 94% rename from installer_kit/src/Installer.php rename to installer_kit/src/InstallerHooks.php index c6247c82d..fe3de9114 100644 --- a/installer_kit/src/Installer.php +++ b/installer_kit/src/InstallerHooks.php @@ -14,10 +14,19 @@ use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipeRunner; use Drupal\Core\Render\Element\Password; -use Drupal\Installer\RecipeKit\Form\RecipesForm; -use Drupal\Installer\RecipeKit\Form\SiteNameForm; - -final class Installer { +use Drupal\RecipeKit\Form\InstallerRecipesForm; +use Drupal\RecipeKit\Form\InstallerSiteNameForm; + +/** + * Provides hook implementations for profiles built on this kit. + * + * @internal + * Everything in this class is internal, which means it could be changed in + * any way, or removed outright, at any time without warning. It is only meant + * to be used by profiles that were generated by this kit. You should not use + * it in your own code in any way. + */ +final class InstallerHooks { /** * Implements hook_install_tasks(). @@ -27,7 +36,7 @@ final class Installer { // certain messages. $container = \Drupal::getContainer(); if ($container instanceof ContainerBuilder) { - $container->set('messenger', new MessageInterceptor( + $container->set('messenger', new InstallerMessenger( \Drupal::messenger(), )); } @@ -60,13 +69,13 @@ final class Installer { 'display_name' => t('Choose add-ons'), 'type' => 'form', 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => RecipesForm::class, + 'function' => InstallerRecipesForm::class, ], 'installer_site_name_form' => [ 'display_name' => t('Name your site'), 'type' => 'form', 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => SiteNameForm::class, + 'function' => InstallerSiteNameForm::class, ], ]); @@ -77,7 +86,7 @@ final class Installer { ); $insert_before('install_profile_modules', [ 'install_install_profile' => [ - 'function' => Installer::class . '::installProfile', + 'function' => InstallerHooks::class . '::installProfile', ], 'install_configure_form' => $configure_form_task, ]); @@ -88,7 +97,7 @@ final class Installer { // Wrap the install_profile_modules() function, which returns a batch job, and // add all the necessary operations to apply the chosen template recipe. - $tasks['install_profile_modules']['function'] = Installer::class . '::applyRecipes'; + $tasks['install_profile_modules']['function'] = InstallerHooks::class . '::applyRecipes'; } /** @@ -221,8 +230,8 @@ final class Installer { private static function installSettingsFormAlter(array &$form): void { $sqlite = 'Drupal\sqlite\Driver\Database\sqlite'; - // Default to SQLite, if available, because it doesn't require any additional - // configuration. + // Default to SQLite, if available, because it doesn't require any + // additional configuration. if (extension_loaded('pdo_sqlite') && array_key_exists($sqlite, $form['driver']['#options'])) { $form['driver']['#default_value'] = $sqlite; $form['driver']['#type'] = 'select'; diff --git a/installer_kit/src/MessageInterceptor.php b/installer_kit/src/InstallerMessenger.php similarity index 69% rename from installer_kit/src/MessageInterceptor.php rename to installer_kit/src/InstallerMessenger.php index 2c2c6cbff..c99b0197b 100644 --- a/installer_kit/src/MessageInterceptor.php +++ b/installer_kit/src/InstallerMessenger.php @@ -9,19 +9,32 @@ use Drupal\Core\StringTranslation\TranslatableMarkup; /** * Decorates the messenger to suppress or alter certain install-time messages. + * + * @internal + * Everything in this class is internal, which means it could be changed in + * any way, or removed outright, at any time without warning. It is only meant + * to be used by profiles that were generated by this kit. You should not use + * it in your own code in any way. */ -final class MessageInterceptor implements MessengerInterface { +final class InstallerMessenger implements MessengerInterface { - private array $reject = [ + private static array $reject = [ 'Congratulations, you installed @drupal!', ]; public function __construct( private readonly MessengerInterface $decorated, - ) { - if (getenv('IS_DDEV_PROJECT')) { - $this->reject[] = 'All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href=":handbook_url">online handbook</a>.'; - } + ) {} + + /** + * Adds a message to the reject list. + * + * @param string $message + * The untranslated, unformatted (i.e., containing raw placeholders) message + * that should be suppressed. + */ + public static function reject(string $message): void { + static::$reject[] = $message; } /** @@ -32,7 +45,7 @@ final class MessageInterceptor implements MessengerInterface { ? $message->getUntranslatedString() : strval($message); - if (!in_array($raw, $this->reject, TRUE)) { + if (!in_array($raw, static::$reject, TRUE)) { $this->decorated->addMessage($message, $type, $repeat); } return $this; diff --git a/installer_kit/src/RecipeAppliedSubscriber.php b/installer_kit/src/RecipeAppliedSubscriber.php index e66e25429..1fa3cc692 100644 --- a/installer_kit/src/RecipeAppliedSubscriber.php +++ b/installer_kit/src/RecipeAppliedSubscriber.php @@ -19,8 +19,8 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; * without applying recipes that have already been applied successfully. Once * the install is done, the list of recipes is deleted. * - * @see \Drupal\Installer\RecipeKit\RecipeKit::applyRecipes() - * @see \Drupal\Installer\RecipeKit\RecipeKit::uninstallProfile() + * @see \Drupal\RecipeKit\InstallerHooks::applyRecipes() + * @see \Drupal\RecipeKit\InstallerHooks::uninstallProfile() */ final class RecipeAppliedSubscriber implements EventSubscriberInterface { diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 554a14abb..9f056156c 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -3,46 +3,54 @@ declare(strict_types=1); use Drupal\Core\Form\FormStateInterface; -use Drupal\Installer\RecipeKit\RecipeKit; +use Drupal\RecipeKit\InstallerHooks; +use Drupal\RecipeKit\InstallerMessenger; /** * Implements hook_install_tasks(). */ function drupal_cms_installer_install_tasks(): array { - return RecipeKit::installTasks(); + $tasks = InstallerHooks::installTasks(); + + if (getenv('IS_DDEV_PROJECT')) { + InstallerMessenger::reject( + 'All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href=":handbook_url">online handbook</a>.', + ); + } + return $tasks; } /** * Implements hook_install_tasks_alter(). */ function drupal_cms_installer_install_tasks_alter(array &$tasks, array $install_state): void { - RecipeKit::installTasksAlter($tasks, $install_state); + InstallerHooks::installTasksAlter($tasks, $install_state); } /** * Implements hook_form_alter(). */ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { - RecipeKit::formAlter($form, $form_state, $form_id); + InstallerHooks::formAlter($form, $form_state, $form_id); } /** * Implements hook_library_info_alter(). */ function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { - RecipeKit::libraryInfoAlter($libraries, $extension); + InstallerHooks::libraryInfoAlter($libraries, $extension); } /** * Implements hook_theme_registry_alter(). */ function drupal_cms_installer_theme_registry_alter(array &$hooks): void { - RecipeKit::themeRegistryAlter($hooks); + InstallerHooks::themeRegistryAlter($hooks); } /** * Preprocess function for all theme hooks. */ function drupal_cms_installer_preprocess(array &$variables): void { - RecipeKit::preprocess($variables); + InstallerHooks::preprocess($variables); } -- GitLab From 151d39aa6ec197037731a03074d2a570325613c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 13:44:06 -0500 Subject: [PATCH 13/36] Remove suggest list --- recipes/drupal_cms_starter/composer.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/recipes/drupal_cms_starter/composer.json b/recipes/drupal_cms_starter/composer.json index 1ea92aa54..69d710645 100644 --- a/recipes/drupal_cms_starter/composer.json +++ b/recipes/drupal_cms_starter/composer.json @@ -22,13 +22,5 @@ "drupal/project_browser": "^2-alpha8", "drupal/token": "^1" }, - "suggest": { - "drupal/drupal_cms_blog": "Adds a blog post content type and listing page.", - "drupal/drupal_cms_case_study": "Adds a case study content type and listing page.", - "drupal/drupal_cms_events": "Adds an event content type and listing page.", - "drupal/drupal_cms_news": "Adds a news content type and listing page.", - "drupal/drupal_cms_person": "Adds a person profile content type.", - "drupal/drupal_cms_project": "Adds a project content type and listing page." - }, "version": "1.x-dev" } -- GitLab From f59fa510ff005de5ee177975d95aeb03c8174d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 13:47:57 -0500 Subject: [PATCH 14/36] Moved a few things around --- installer_kit/src/InstallerHooks.php | 16 ++++------------ project_template/composer.json | 2 +- .../drupal_cms_installer.profile | 18 ++++++++++++++++++ .../drupal_cms_installer.services.yml | 2 +- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/installer_kit/src/InstallerHooks.php b/installer_kit/src/InstallerHooks.php index fe3de9114..2bd8c00ad 100644 --- a/installer_kit/src/InstallerHooks.php +++ b/installer_kit/src/InstallerHooks.php @@ -168,8 +168,6 @@ final class InstallerHooks { '#default_value' => 'admin', ]; $form['admin_account']['account']['mail'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', '#type' => 'email', '#title' => t('Email'), '#required' => TRUE, @@ -177,8 +175,6 @@ final class InstallerHooks { '#weight' => 10, ]; $form['admin_account']['account']['pass'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', '#type' => 'password', '#title' => t('Password'), '#required' => TRUE, @@ -187,19 +183,15 @@ final class InstallerHooks { '#value_callback' => static::class . '::passwordValue', ]; - // Hide the timezone selection. Core automatically uses client-side JavaScript - // to detect it, but we don't need to expose that to the user. But the - // JavaScript expects the form elements to look a certain way, so hiding the - // fields visually is the correct approach here. + // Hide the timezone selection. Core automatically uses client-side + // JavaScript to detect it, but we don't need to expose that to the user. + // But the JavaScript expects the form elements to look a certain way, so + // hiding the fields visually is the correct approach here. // @see core/misc/timezone.js $form['regional_settings']['#attributes']['class'][] = 'visually-hidden'; // Don't allow the timezone selection to be tab-focused. $form['regional_settings']['date_default_timezone']['#attributes']['tabindex'] = -1; - // We always install Automatic Updates, so we don't need to expose the update - // notification settings. - $form['update_notifications']['#access'] = FALSE; - $form['actions']['submit']['#value'] = t('Finish'); } diff --git a/project_template/composer.json b/project_template/composer.json index 0b4d70c0b..76bd03727 100644 --- a/project_template/composer.json +++ b/project_template/composer.json @@ -34,7 +34,7 @@ "drupal/drupal_cms_seo_tools": "^1.0.1", "drupal/drupal_cms_starter": "^1.0.1", "drupal/project_browser": "@alpha", - "drupal/recipe-installer-kit": "dev-main@dev", + "drupal/recipe-installer-kit": "*", "drupal/webform": "@beta", "drush/drush": "^13" }, diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 9f056156c..bdeaf4c9c 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -34,6 +34,24 @@ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_ InstallerHooks::formAlter($form, $form_state, $form_id); } +/** + * Implements hook_form_alter() for install_configure_form. + */ +function drupal_cms_installer_form_install_configure_form_alter(array &$form, FormStateInterface $form_state): void { + // We always install Automatic Updates, so we don't need to expose the update + // notification settings. + $form['update_notifications']['#access'] = FALSE; + + $form['admin_account']['account']['mail'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + $form['admin_account']['account']['pass'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; +} + /** * Implements hook_library_info_alter(). */ diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml index 46dee6ef5..8600226df 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml @@ -1,5 +1,5 @@ services: - Drupal\Installer\RecipeKit\RecipeAppliedSubscriber: + Drupal\RecipeKit\RecipeAppliedSubscriber: autowire: true tags: - { name: event_subscriber } -- GitLab From 8c2b760aecfb8b653b9efd2605c8e7a0587e34cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 14:02:53 -0500 Subject: [PATCH 15/36] Make optional recipes an associatve array for greater customizability --- installer_kit/src/Drush/Generators/info.twig | 7 ++++--- installer_kit/src/Form/InstallerRecipesForm.php | 16 ++-------------- .../drupal_cms_installer.info.yml | 12 ++++++------ 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index 4962512f7..0abb6bccb 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -26,9 +26,10 @@ recipes: # applied during the install process. required: [] - # This should be a list of recipes' Composer package names that should be shown to the - # user as choices at the start of the install process. - optional: [] + # This should be a list of recipes to show to the user as choices at the start of the + # install process. The keys should the recipes' Composer package names, and the values + # should be the user-facing names of the recipes. + optional: {} # To control the look and feel of the install process, you can override various libraries # here, using the same syntax as you would in a theme. diff --git a/installer_kit/src/Form/InstallerRecipesForm.php b/installer_kit/src/Form/InstallerRecipesForm.php index 510692a10..92750330f 100644 --- a/installer_kit/src/Form/InstallerRecipesForm.php +++ b/installer_kit/src/Form/InstallerRecipesForm.php @@ -3,8 +3,6 @@ namespace Drupal\RecipeKit\Form; use Drupal\Core\Form\FormBase; -use Composer\InstalledVersions; -use Drupal\Component\Serialization\Yaml; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\Checkboxes; @@ -35,25 +33,15 @@ final class InstallerRecipesForm extends FormBase { '#suffix' => '</p>', ]; + assert(is_array($install_state)); $form['add_ons'] = [ '#prefix' => '<div class="cms-installer__form-group">', '#suffix' => '</div>', '#type' => 'checkboxes', '#value_callback' => static::class . '::valueCallback', + '#options' => $install_state['profile_info']['recipes']['optional'] ?? [], ]; - assert(is_array($install_state)); - $optional_recipes = $install_state['profile_info']['recipes']['optional'] ?? []; - - foreach ($optional_recipes as $name) { - $recipe = InstalledVersions::getInstallPath($name) . '/recipe.yml'; - if (file_exists($recipe)) { - $recipe = file_get_contents($recipe); - $recipe = Yaml::decode($recipe); - $form['add_ons']['#options'][$name] = $recipe['name']; - } - } - $form['add_ons']['help'] = [ '#prefix' => '<p class="cms-installer__info">', '#markup' => $this->t('Don’t see what you’re looking for? You can set up customized content later.'), diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index cfe3c2914..d89321f8c 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -16,12 +16,12 @@ recipes: required: - drupal/drupal_cms_starter optional: - - drupal/drupal_cms_blog - - drupal/drupal_cms_case_study - - drupal/drupal_cms_events - - drupal/drupal_cms_news - - drupal/drupal_cms_person - - drupal/drupal_cms_project + drupal/drupal_cms_blog: Blog + drupal/drupal_cms_case_study: Case Studies + drupal/drupal_cms_events: Events + drupal/drupal_cms_news: News + drupal/drupal_cms_person: Person Profiles + drupal/drupal_cms_project: Projects libraries-override: claro/maintenance-page: css: -- GitLab From bd35100f3c8cf2c1fd402512717bf25b1419bfb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 14:10:47 -0500 Subject: [PATCH 16/36] Move all form customizations back into the main profile. --- .../src/Form/InstallerRecipesForm.php | 20 ------ .../src/Form/InstallerSiteNameForm.php | 13 ---- installer_kit/src/InstallerHooks.php | 16 ----- .../drupal_cms_installer.profile | 68 ++++++++++++++++++- 4 files changed, 67 insertions(+), 50 deletions(-) diff --git a/installer_kit/src/Form/InstallerRecipesForm.php b/installer_kit/src/Form/InstallerRecipesForm.php index 92750330f..d6ff2199b 100644 --- a/installer_kit/src/Form/InstallerRecipesForm.php +++ b/installer_kit/src/Form/InstallerRecipesForm.php @@ -25,39 +25,19 @@ final class InstallerRecipesForm extends FormBase { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, ?array $install_state = NULL): array { - $form['#title'] = $this->t('Get started'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => $this->t('You can select pre-configured types of content now, or add them later.'), - '#suffix' => '</p>', - ]; - assert(is_array($install_state)); $form['add_ons'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', '#type' => 'checkboxes', '#value_callback' => static::class . '::valueCallback', '#options' => $install_state['profile_info']['recipes']['optional'] ?? [], ]; - $form['add_ons']['help'] = [ - '#prefix' => '<p class="cms-installer__info">', - '#markup' => $this->t('Don’t see what you’re looking for? You can set up customized content later.'), - '#suffix' => '</p>', - '#weight' => 100, - ]; - $form['actions'] = [ 'submit' => [ '#type' => 'submit', '#value' => $this->t('Next'), '#button_type' => 'primary', '#op' => 'submit', - '#attributes' => [ - 'class' => ['button--next'] - ] ], 'skip' => [ '#type' => 'submit', diff --git a/installer_kit/src/Form/InstallerSiteNameForm.php b/installer_kit/src/Form/InstallerSiteNameForm.php index e01eb91fb..e8e5fcf42 100644 --- a/installer_kit/src/Form/InstallerSiteNameForm.php +++ b/installer_kit/src/Form/InstallerSiteNameForm.php @@ -25,17 +25,7 @@ final class InstallerSiteNameForm extends FormBase { public function buildForm(array $form, FormStateInterface $form_state): array { global $install_state; - $form['#title'] = $this->t('Give your site a name'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => $this->t('You can change this later.'), - '#suffix' => '</p>', - ]; - $form['site_name'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', '#type' => 'textfield', '#title' => $this->t('Site name'), '#required' => TRUE, @@ -47,9 +37,6 @@ final class InstallerSiteNameForm extends FormBase { '#type' => 'submit', '#value' => $this->t('Next'), '#button_type' => 'primary', - '#attributes' => [ - 'class' => ['button--next'] - ], ], ]; diff --git a/installer_kit/src/InstallerHooks.php b/installer_kit/src/InstallerHooks.php index 2bd8c00ad..b8c92c298 100644 --- a/installer_kit/src/InstallerHooks.php +++ b/installer_kit/src/InstallerHooks.php @@ -141,15 +141,6 @@ final class InstallerHooks { private static function installConfigureFormAlter(array &$form): void { global $install_state; - $form['#title'] = t('Create your account'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t('Creating an account allows you to log in to your site.'), - '#suffix' => '</p>', - '#weight' => -40, - ]; - $form['site_information']['#type'] = 'container'; // We collected the site name in a previous step. $form['site_information']['site_name'] = [ @@ -232,13 +223,6 @@ final class InstallerHooks { // advanced options. $form['settings'][$sqlite]['advanced_options']['database'] = $form['settings'][$sqlite]['database']; unset($form['settings'][$sqlite]['database']); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t("You don't need to change anything here unless you want to use a different database type."), - '#suffix' => '</p>', - '#weight' => -50, - ]; } } diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index bdeaf4c9c..0b21d8801 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -34,10 +34,76 @@ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_ InstallerHooks::formAlter($form, $form_state, $form_id); } +/** + * Implements hook_form_alter() for installer_site_name_form. + */ +function drupal_cms_installer_form_installer_site_name_form_alter(array &$form): void { + $form['#title'] = t('Give your site a name'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('You can change this later.'), + '#suffix' => '</p>', + '#weight' => -100, + ]; + $form['site_name'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + $form['actions']['submit']['#attributes']['class'] = ['button--next']; +} + +/** + * Implements hook_form_alter() for installer_recipes_form. + */ +function drupal_cms_installer_form_installer_recipes_form_alter(array &$form): void { + $form['#title'] = t('Get started'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('You can select pre-configured types of content now, or add them later.'), + '#suffix' => '</p>', + '#weight' => -100, + ]; + $form['add_ons'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + $form['add_ons']['help'] = [ + '#prefix' => '<p class="cms-installer__info">', + '#markup' => t('Don’t see what you’re looking for? You can set up customized content later.'), + '#suffix' => '</p>', + '#weight' => 100, + ]; + + $form['actions']['submit']['#attributes']['class'] = ['button--next']; +} + +/** + * Implements hook_form_alter() for install_settings_form. + */ +function drupal_cms_installer_form_install_settings_form_alter(array &$form): void { + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t("You don't need to change anything here unless you want to use a different database type."), + '#suffix' => '</p>', + '#weight' => -50, + ]; +} + /** * Implements hook_form_alter() for install_configure_form. */ -function drupal_cms_installer_form_install_configure_form_alter(array &$form, FormStateInterface $form_state): void { +function drupal_cms_installer_form_install_configure_form_alter(array &$form): void { + $form['#title'] = t('Create your account'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('Creating an account allows you to log in to your site.'), + '#suffix' => '</p>', + '#weight' => -40, + ]; + // We always install Automatic Updates, so we don't need to expose the update // notification settings. $form['update_notifications']['#access'] = FALSE; -- GitLab From a2554e8477eda043a75db483d45799813f2741cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 14:37:44 -0500 Subject: [PATCH 17/36] Allow early installer forms to be overridden --- installer_kit/src/Drush/Generators/info.twig | 7 +++ .../src/Form/InstallerFormInterface.php | 27 ++++++++++ .../src/Form/InstallerRecipesForm.php | 14 +++++- .../src/Form/InstallerSiteNameForm.php | 14 +++++- installer_kit/src/InstallerHooks.php | 49 +++++++++---------- .../drupal_cms_installer.info.yml | 3 ++ 6 files changed, 86 insertions(+), 28 deletions(-) create mode 100644 installer_kit/src/Form/InstallerFormInterface.php diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index 0abb6bccb..4a03fc297 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -21,6 +21,13 @@ distribution: # @see install_profile_info() themes: [] +# A list of forms that should be presented to the user before choosing database +# settings and installing the system. Each of these forms must implement +# \Drupal\RecipeKit\Form\InstallerFormInterface. +forms: +- '\Drupal\RecipeKit\Form\InstallerRecipesForm' +- '\Drupal\RecipeKit\Form\InstallerSiteNameForm' + recipes: # This should be a list of recipes' Composer package names that should always be # applied during the install process. diff --git a/installer_kit/src/Form/InstallerFormInterface.php b/installer_kit/src/Form/InstallerFormInterface.php new file mode 100644 index 000000000..b1a2e9764 --- /dev/null +++ b/installer_kit/src/Form/InstallerFormInterface.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\RecipeKit\Form; + +use Drupal\Core\Form\FormInterface; + +/** + * Defines an interface for forms shown to the user in the early installer. + */ +interface InstallerFormInterface extends FormInterface { + + /** + * Returns an install task definition for this form. + * + * @param array $install_state + * The current install state. + * + * @return array + * An install task definition. + * + * @see hook_install_tasks() + */ + public static function toInstallTask(array $install_state): array; + +} diff --git a/installer_kit/src/Form/InstallerRecipesForm.php b/installer_kit/src/Form/InstallerRecipesForm.php index d6ff2199b..daf38949f 100644 --- a/installer_kit/src/Form/InstallerRecipesForm.php +++ b/installer_kit/src/Form/InstallerRecipesForm.php @@ -12,7 +12,19 @@ use Drupal\Core\Render\Element\Checkboxes; * @todo Present this as a mini project browser once * https://www.drupal.org/i/3450629 is fixed. */ -final class InstallerRecipesForm extends FormBase { +final class InstallerRecipesForm extends FormBase implements InstallerFormInterface { + + /** + * {@inheritdoc} + */ + public static function toInstallTask(array $install_state): array { + return [ + 'display_name' => t('Choose add-ons'), + 'type' => 'form', + 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, + 'function' => static::class, + ]; + } /** * {@inheritdoc} diff --git a/installer_kit/src/Form/InstallerSiteNameForm.php b/installer_kit/src/Form/InstallerSiteNameForm.php index e8e5fcf42..f0ecb08c2 100644 --- a/installer_kit/src/Form/InstallerSiteNameForm.php +++ b/installer_kit/src/Form/InstallerSiteNameForm.php @@ -10,7 +10,19 @@ use Drupal\Core\Form\FormStateInterface; /** * Defines a form to set the site name. */ -final class InstallerSiteNameForm extends FormBase { +final class InstallerSiteNameForm extends FormBase implements InstallerFormInterface { + + /** + * {@inheritdoc} + */ + public static function toInstallTask(array $install_state): array { + return [ + 'display_name' => t('Name your site'), + 'type' => 'form', + 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, + 'function' => static::class, + ]; + } /** * {@inheritdoc} diff --git a/installer_kit/src/InstallerHooks.php b/installer_kit/src/InstallerHooks.php index b8c92c298..b1af14033 100644 --- a/installer_kit/src/InstallerHooks.php +++ b/installer_kit/src/InstallerHooks.php @@ -14,8 +14,7 @@ use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\RecipeRunner; use Drupal\Core\Render\Element\Password; -use Drupal\RecipeKit\Form\InstallerRecipesForm; -use Drupal\RecipeKit\Form\InstallerSiteNameForm; +use Drupal\RecipeKit\Form\InstallerFormInterface; /** * Provides hook implementations for profiles built on this kit. @@ -58,26 +57,20 @@ final class InstallerHooks { if ($key === FALSE) { return; } - // This isn't very clean, but it's the only way to positionally splice into - // an associative (and therefore by definition unordered) array. + // This isn't very clean, but it's the only way to positionally splice + // into an associative (and therefore by definition unordered) array. $tasks_before = array_slice($tasks, 0, $key, TRUE); $tasks_after = array_slice($tasks, $key, NULL, TRUE); $tasks = $tasks_before + $additions + $tasks_after; }; - $insert_before('install_settings_form', [ - 'installer_recipes_form' => [ - 'display_name' => t('Choose add-ons'), - 'type' => 'form', - 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => InstallerRecipesForm::class, - ], - 'installer_site_name_form' => [ - 'display_name' => t('Name your site'), - 'type' => 'form', - 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => InstallerSiteNameForm::class, - ], - ]); + + // Set up any pre-database information collection forms. + $forms = []; + foreach ($install_state['profile_info']['forms'] ?? [] as $class) { + assert(is_a($class, InstallerFormInterface::class, TRUE)); + $forms[$class] = $class::toInstallTask($install_state); + } + $insert_before('install_settings_form', $forms); $configure_form_task = $tasks['install_configure_form']; unset( @@ -91,13 +84,14 @@ final class InstallerHooks { 'install_configure_form' => $configure_form_task, ]); - // Set English as the default language; it can be changed mid-stream. We can't - // use the passed-in $install_state because it's not passed by reference. + // Set English as the default language; eventually, we'll support changing + // it mid-stream. We can't use the passed-in $install_state because it's not + // passed by reference. $GLOBALS['install_state']['parameters'] += ['langcode' => 'en']; // Wrap the install_profile_modules() function, which returns a batch job, and // add all the necessary operations to apply the chosen template recipe. - $tasks['install_profile_modules']['function'] = InstallerHooks::class . '::applyRecipes'; + $tasks['install_profile_modules']['function'] = static::class . '::applyRecipes'; } /** @@ -142,11 +136,14 @@ final class InstallerHooks { global $install_state; $form['site_information']['#type'] = 'container'; - // We collected the site name in a previous step. - $form['site_information']['site_name'] = [ - '#type' => 'hidden', - '#default_value' => $GLOBALS['install_state']['parameters']['site_name'], - ]; + + // If we collected the site name in a previous step, don't show it now. + if (array_key_exists('site_name', $install_state['parameters'])) { + $form['site_information']['site_name'] = [ + '#type' => 'hidden', + '#default_value' => $install_state['parameters']['site_name'], + ]; + } // Use a custom submit handler to set the site email. unset($form['site_information']['site_mail']); diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index d89321f8c..e5b6a5583 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -12,6 +12,9 @@ distribution: # injecting Stark into it. # @see install_profile_info() themes: [] +forms: + - '\Drupal\RecipeKit\Form\InstallerRecipesForm' + - '\Drupal\RecipeKit\Form\InstallerSiteNameForm' recipes: required: - drupal/drupal_cms_starter -- GitLab From 61a61d8083e4bb6b884fd1d0d63b4d05f7358d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 14:42:38 -0500 Subject: [PATCH 18/36] Minor tweaks --- installer_kit/src/Drush/Generators/info.twig | 7 +------ .../drupal_cms_installer.info.yml | 16 ++++++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index 4a03fc297..528a814d2 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -7,20 +7,15 @@ core_version_requirement: '^11.1' description: 'Provides install-time tweaks. Not to be used in production.' +{% if finish_url %} # Use this `distribution` key in order to skip the installer's profile selection step. distribution: name: {{ name }} -{% if finish_url %} install: # Set this to a URL where the user should be redirected after install. finish_url: '{{ finish_url }}' {% endif %} -# Explicitly provide an empty list of themes -- this prevents the installer from -# injecting Stark into it. It's best not to change this. -# @see install_profile_info() -themes: [] - # A list of forms that should be presented to the user before choosing database # settings and installing the system. Each of these forms must implement # \Drupal\RecipeKit\Form\InstallerFormInterface. diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index e5b6a5583..815cea617 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -2,19 +2,17 @@ name: Drupal CMS Installer type: profile core_version_requirement: '>=10.3' description: 'Provides install-time tweaks for Drupal CMS. Not to be used in production.' -# Drupal CMS isn't a distribution, but we need to use this `distribution` key in order -# to skip the installer's profile selection step. + +# Use the `distribution` key to skip the installer's profile selection step. distribution: name: Drupal CMS install: finish_url: '/admin/dashboard/welcome' -# Explicitly provide an empty list of themes -- this prevents the installer from -# injecting Stark into it. -# @see install_profile_info() -themes: [] + forms: - '\Drupal\RecipeKit\Form\InstallerRecipesForm' - '\Drupal\RecipeKit\Form\InstallerSiteNameForm' + recipes: required: - drupal/drupal_cms_starter @@ -25,6 +23,7 @@ recipes: drupal/drupal_cms_news: News drupal/drupal_cms_person: Person Profiles drupal/drupal_cms_project: Projects + libraries-override: claro/maintenance-page: css: @@ -38,3 +37,8 @@ libraries-override: core/drupal.progress: js: js/progress.js: {} + +# Explicitly provide an empty list of themes -- this prevents the installer from +# injecting Stark into it. +# @see install_profile_info() +themes: [] \ No newline at end of file -- GitLab From cd7f8e99ce1aecaf237fac504fe0127dc6c594c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 14:45:52 -0500 Subject: [PATCH 19/36] Ensure forms have #title element --- installer_kit/src/Form/InstallerRecipesForm.php | 1 + installer_kit/src/Form/InstallerSiteNameForm.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/installer_kit/src/Form/InstallerRecipesForm.php b/installer_kit/src/Form/InstallerRecipesForm.php index daf38949f..7621b9fc7 100644 --- a/installer_kit/src/Form/InstallerRecipesForm.php +++ b/installer_kit/src/Form/InstallerRecipesForm.php @@ -58,6 +58,7 @@ final class InstallerRecipesForm extends FormBase implements InstallerFormInterf ], '#type' => 'actions', ]; + $form['#title'] = ''; return $form; } diff --git a/installer_kit/src/Form/InstallerSiteNameForm.php b/installer_kit/src/Form/InstallerSiteNameForm.php index f0ecb08c2..e352b7a5d 100644 --- a/installer_kit/src/Form/InstallerSiteNameForm.php +++ b/installer_kit/src/Form/InstallerSiteNameForm.php @@ -51,7 +51,7 @@ final class InstallerSiteNameForm extends FormBase implements InstallerFormInter '#button_type' => 'primary', ], ]; - + $form['#title'] = ''; return $form; } -- GitLab From 87af3f767da3ad13d89b72e872f8ff4fc83717d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 14:52:49 -0500 Subject: [PATCH 20/36] fix readme typo --- installer_kit/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer_kit/README.md b/installer_kit/README.md index 93f6a4746..9f9ab13bf 100644 --- a/installer_kit/README.md +++ b/installer_kit/README.md @@ -1,7 +1,7 @@ # Drupal Recipe Installer Kit If you thought the Drupal CMS installer was really slick, you might have wondered how to adapt it for your own nefarious purposes. Until now, it was impossible; your only option was to completely fork the installer. -This package is a toolkit to make create install profiles that work similarly to the Drupal CMS installer -- the same user flow and functionality. But you can customize the stuff that really matters: +This package is a toolkit to create install profiles that work similarly to the Drupal CMS installer -- the same user flow and functionality. But you can customize the stuff that really matters: * Which recipes are shown to users at the beginning * Which recipes are _always_ applied * Where users should be redirected afterwards -- GitLab From d997d81d25302f6750f25f12a5851b69cc4d3585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 15:05:34 -0500 Subject: [PATCH 21/36] Fix info file template --- installer_kit/src/Drush/Generators/info.twig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index 528a814d2..51a6bcb42 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -8,12 +8,14 @@ core_version_requirement: '^11.1' description: 'Provides install-time tweaks. Not to be used in production.' {% if finish_url %} -# Use this `distribution` key in order to skip the installer's profile selection step. distribution: name: {{ name }} install: - # Set this to a URL where the user should be redirected after install. finish_url: '{{ finish_url }}' +{% else %} +# Uncomment this `distribution` section to skip the installer's profile selection step. +# distribution: +# name: {{ name }} {% endif %} # A list of forms that should be presented to the user before choosing database -- GitLab From b003a5f8aefd5ab9666c2544e1f8cde9a3ff5a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 15:11:51 -0500 Subject: [PATCH 22/36] Fix wrong NS for generator --- .../src/Drush/Generators/RecipeInstallerGenerator.php | 2 +- installer_kit/src/Drush/Generators/info.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php index 13a31375f..f6e55a5e7 100644 --- a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php +++ b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\Installer\RecipeKit\Drush\Generators; +namespace Drupal\RecipeKit\Drush\Generators; use DrupalCodeGenerator\Asset\AssetCollection as Assets; use DrupalCodeGenerator\Attribute\Generator; diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index 51a6bcb42..9fcfbb7d3 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -14,8 +14,8 @@ distribution: finish_url: '{{ finish_url }}' {% else %} # Uncomment this `distribution` section to skip the installer's profile selection step. -# distribution: -# name: {{ name }} +#distribution: +# name: {{ name }} {% endif %} # A list of forms that should be presented to the user before choosing database -- GitLab From 4f49cc3ffade74e08183141e81ae51ce5b2c16a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 15:23:02 -0500 Subject: [PATCH 23/36] Explain the "description" field in the info file --- installer_kit/src/Drush/Generators/info.twig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index 9fcfbb7d3..d71753182 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -5,6 +5,8 @@ type: profile # needs of the recipes you want to apply. core_version_requirement: '^11.1' +# Change this to whatever you like. It's only visible to users in the installer's profile +# selection step, which is skipped if you mark this profile as a distribution. description: 'Provides install-time tweaks. Not to be used in production.' {% if finish_url %} -- GitLab From bcf91c1a4a5333c7b42ecd23afe8de47fd7397d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 15:36:49 -0500 Subject: [PATCH 24/36] Change libraries-override to libraries-add, since it is distinctly different --- installer_kit/src/Drush/Generators/info.twig | 9 ++++++--- installer_kit/src/InstallerHooks.php | 2 +- .../drupal_cms_installer/drupal_cms_installer.info.yml | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig index d71753182..97d692660 100644 --- a/installer_kit/src/Drush/Generators/info.twig +++ b/installer_kit/src/Drush/Generators/info.twig @@ -37,6 +37,9 @@ recipes: # should be the user-facing names of the recipes. optional: {} -# To control the look and feel of the install process, you can override various libraries -# here, using the same syntax as you would in a theme. -libraries-override: {} +# To change the look and feel of the install process, you can add assets to various +# libraries, using a syntax similar to the `libraries-override` directive for themes. +# Relative asset paths will be resolved relative to the install profile's directory. +# Whatever is in this array will be merged recursively into the value passed to +# hook_library_info_alter(). +libraries-add: {} diff --git a/installer_kit/src/InstallerHooks.php b/installer_kit/src/InstallerHooks.php index b1af14033..949e3a4b2 100644 --- a/installer_kit/src/InstallerHooks.php +++ b/installer_kit/src/InstallerHooks.php @@ -289,7 +289,7 @@ final class InstallerHooks { public static function libraryInfoAlter(array &$libraries, string $extension): void { global $install_state; - $library_overrides = $install_state['profile_info']['libraries-override'] ?? []; + $library_overrides = $install_state['profile_info']['libraries-add'] ?? []; foreach ($library_overrides as $name => $override) { [$overridden_extension, $library_name] = explode('/', $name, 2); diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index 815cea617..5bb16635e 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -24,7 +24,7 @@ recipes: drupal/drupal_cms_person: Person Profiles drupal/drupal_cms_project: Projects -libraries-override: +libraries-add: claro/maintenance-page: css: theme: -- GitLab From f0c4731116a2737757fbcc02de62e053aab90a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 16:32:04 -0500 Subject: [PATCH 25/36] Fix bad namespaces --- .../tests/src/Functional/CommandLineInstallTest.php | 2 +- .../tests/src/Functional/InteractiveInstallTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php index 2c2f0a83a..cef2d03d0 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Drupal\Tests\drupal_cms_installer\Functional; use Drupal\Core\Test\TestSetupTrait; -use Drupal\Installer\RecipeKit\RecipeAppliedSubscriber; +use Drupal\RecipeKit\RecipeAppliedSubscriber; use Drush\TestTraits\DrushTestTrait; use PHPUnit\Framework\TestCase; use Symfony\Component\Filesystem\Filesystem; diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php index 78c33fc02..e600830e4 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php @@ -10,7 +10,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\State\StateInterface; -use Drupal\Installer\RecipeKit\RecipeAppliedSubscriber; +use Drupal\RecipeKit\RecipeAppliedSubscriber; use Drupal\FunctionalTests\Installer\InstallerTestBase; use Drupal\user\Entity\User; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -110,7 +110,7 @@ class InteractiveInstallTest extends InstallerTestBase { $this->assertNull($this->container->get(StateInterface::class)->get(RecipeAppliedSubscriber::STATE_KEY)); // The site name and site-wide email address should have been set. - // @see \Drupal\Installer\RecipeKit\Form\SiteNameForm + // @see \Drupal\RecipeKit\Form\SiteNameForm $site_config = $this->config('system.site'); $this->assertSame('Installer Test', $site_config->get('name')); $this->assertSame("hello@good.bye", $site_config->get('mail')); -- GitLab From 3deb054b50adcb4157bec2b940b73d2d1a9259b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 16:55:24 -0500 Subject: [PATCH 26/36] Use the published package --- dev.composer.json | 7 +- installer_kit/README.md | 32 -- installer_kit/composer.json | 12 - .../Generators/RecipeInstallerGenerator.php | 35 -- .../src/Drush/Generators/composer.twig | 6 - installer_kit/src/Drush/Generators/info.twig | 45 --- .../src/Drush/Generators/profile.twig | 48 --- .../src/Drush/Generators/services.twig | 5 - .../Generators/user.role.administrator.yml | 8 - .../src/Form/InstallerFormInterface.php | 27 -- .../src/Form/InstallerRecipesForm.php | 95 ----- .../src/Form/InstallerSiteNameForm.php | 66 ---- installer_kit/src/InstallerHooks.php | 374 ------------------ installer_kit/src/InstallerMessenger.php | 103 ----- installer_kit/src/RecipeAppliedSubscriber.php | 59 --- project_template/composer.json | 2 +- .../drupal_cms_installer.info.yml | 6 +- .../drupal_cms_installer.profile | 18 +- .../drupal_cms_installer.services.yml | 2 +- 19 files changed, 15 insertions(+), 935 deletions(-) delete mode 100644 installer_kit/README.md delete mode 100644 installer_kit/composer.json delete mode 100644 installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php delete mode 100644 installer_kit/src/Drush/Generators/composer.twig delete mode 100644 installer_kit/src/Drush/Generators/info.twig delete mode 100644 installer_kit/src/Drush/Generators/profile.twig delete mode 100644 installer_kit/src/Drush/Generators/services.twig delete mode 100644 installer_kit/src/Drush/Generators/user.role.administrator.yml delete mode 100644 installer_kit/src/Form/InstallerFormInterface.php delete mode 100644 installer_kit/src/Form/InstallerRecipesForm.php delete mode 100644 installer_kit/src/Form/InstallerSiteNameForm.php delete mode 100644 installer_kit/src/InstallerHooks.php delete mode 100644 installer_kit/src/InstallerMessenger.php delete mode 100644 installer_kit/src/RecipeAppliedSubscriber.php diff --git a/dev.composer.json b/dev.composer.json index 2887ba367..ee2687b85 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -56,10 +56,6 @@ "type": "path", "url": "recipes/drupal_cms_image" }, - "installer_kit": { - "type": "path", - "url": "installer_kit" - }, "news": { "type": "path", "url": "recipes/drupal_cms_news" @@ -107,8 +103,7 @@ }, "require-dev": { "drupal/core-dev": "^11.1.1", - "drupal/default_content": "^2", - "drupal/recipe-installer-kit": "*" + "drupal/default_content": "^2" }, "autoload-dev": { "files": [ diff --git a/installer_kit/README.md b/installer_kit/README.md deleted file mode 100644 index 9f9ab13bf..000000000 --- a/installer_kit/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Drupal Recipe Installer Kit -If you thought the Drupal CMS installer was really slick, you might have wondered how to adapt it for your own nefarious purposes. Until now, it was impossible; your only option was to completely fork the installer. - -This package is a toolkit to create install profiles that work similarly to the Drupal CMS installer -- the same user flow and functionality. But you can customize the stuff that really matters: -* Which recipes are shown to users at the beginning -* Which recipes are _always_ applied -* Where users should be redirected afterwards -* How the installer looks and feels - -This is for developers who want a better, recipe-based installer experience, but don't want to write a pile of complicated PHP code to bend the Drupal installer to their will. - -## How to use -First, require this package into your project: -```shell -composer require drupal/recipe-installer-kit -``` -Then, use Drush to generate a stub install profile: -```shell -drush generate recipe-kit:installer --destination=profiles/SOME_MACHINE_NAME -``` -Then go to the profile's directory and start editing the `SOME_MACHINE_NAME.info.yml` file. - -To edit the look and feel, you can create a `templates/install-page.html.twig` file by copying it from `core/modules/system/templates/install-page.html.twig`. This is more or less the only template used by the installer. You can use the info file's `libraries-override` section to alter the CSS and JavaScript from Drupal core. - -Profiles generated from this package should include this package as a Composer dependency. This means that your generated profile should have a `composer.json` that contains the following: -```json -"require": { - "drupal/recipe-installer-kit": "dev-main" -} -``` - -An example use case is [Drupal CMS's project template](https://git.drupalcode.org/project/cms), which uses this package to generate its install profile. diff --git a/installer_kit/composer.json b/installer_kit/composer.json deleted file mode 100644 index 80ddf69dc..000000000 --- a/installer_kit/composer.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "drupal/recipe-installer-kit", - "require": { - "drupal/core": ">=10.4", - "drush/drush": "^13" - }, - "autoload": { - "psr-4": { - "Drupal\\RecipeKit\\": "src" - } - } -} diff --git a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php b/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php deleted file mode 100644 index f6e55a5e7..000000000 --- a/installer_kit/src/Drush/Generators/RecipeInstallerGenerator.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\RecipeKit\Drush\Generators; - -use DrupalCodeGenerator\Asset\AssetCollection as Assets; -use DrupalCodeGenerator\Attribute\Generator; -use DrupalCodeGenerator\Command\BaseGenerator; - -#[Generator( - name: 'recipe-kit:installer', - description: 'Generates a stub install profile built on the recipe installer kit.', - templatePath: __DIR__, -)] -final class RecipeInstallerGenerator extends BaseGenerator { - - /** - * {@inheritdoc} - */ - protected function generate(array &$vars, Assets $assets): void { - $ir = $this->createInterviewer($vars); - - $vars['machine_name'] = $ir->askMachineName(); - $vars['name'] = $ir->askName(); - $vars['finish_url'] = $ir->ask('Enter a Drupal path where users should be redirected after installing.'); - - $assets->addFile('{machine_name}.info.yml', 'info.twig'); - $assets->addFile('{machine_name}.profile', 'profile.twig'); - $assets->addFile('{machine_name}.services.yml', 'services.twig'); - $assets->addFile('composer.json', 'composer.twig'); - $assets->addFile('config/install/user.role.administrator.yml', 'user.role.administrator.yml'); - } - -} diff --git a/installer_kit/src/Drush/Generators/composer.twig b/installer_kit/src/Drush/Generators/composer.twig deleted file mode 100644 index ad23892db..000000000 --- a/installer_kit/src/Drush/Generators/composer.twig +++ /dev/null @@ -1,6 +0,0 @@ -{ - "require": { - "drupal/recipe-installer-kit": "dev-main" - }, - "type": "drupal-profile" -} diff --git a/installer_kit/src/Drush/Generators/info.twig b/installer_kit/src/Drush/Generators/info.twig deleted file mode 100644 index 97d692660..000000000 --- a/installer_kit/src/Drush/Generators/info.twig +++ /dev/null @@ -1,45 +0,0 @@ -name: {{ name }} -type: profile - -# Change this to whatever your minimum required version of core is, depending on the -# needs of the recipes you want to apply. -core_version_requirement: '^11.1' - -# Change this to whatever you like. It's only visible to users in the installer's profile -# selection step, which is skipped if you mark this profile as a distribution. -description: 'Provides install-time tweaks. Not to be used in production.' - -{% if finish_url %} -distribution: - name: {{ name }} - install: - finish_url: '{{ finish_url }}' -{% else %} -# Uncomment this `distribution` section to skip the installer's profile selection step. -#distribution: -# name: {{ name }} -{% endif %} - -# A list of forms that should be presented to the user before choosing database -# settings and installing the system. Each of these forms must implement -# \Drupal\RecipeKit\Form\InstallerFormInterface. -forms: -- '\Drupal\RecipeKit\Form\InstallerRecipesForm' -- '\Drupal\RecipeKit\Form\InstallerSiteNameForm' - -recipes: - # This should be a list of recipes' Composer package names that should always be - # applied during the install process. - required: [] - - # This should be a list of recipes to show to the user as choices at the start of the - # install process. The keys should the recipes' Composer package names, and the values - # should be the user-facing names of the recipes. - optional: {} - -# To change the look and feel of the install process, you can add assets to various -# libraries, using a syntax similar to the `libraries-override` directive for themes. -# Relative asset paths will be resolved relative to the install profile's directory. -# Whatever is in this array will be merged recursively into the value passed to -# hook_library_info_alter(). -libraries-add: {} diff --git a/installer_kit/src/Drush/Generators/profile.twig b/installer_kit/src/Drush/Generators/profile.twig deleted file mode 100644 index 39120a99e..000000000 --- a/installer_kit/src/Drush/Generators/profile.twig +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -declare(strict_types=1); - -use Drupal\Core\Form\FormStateInterface; -use Drupal\RecipeKit\InstallerHooks; - -/** - * Implements hook_install_tasks(). - */ -function {{ machine_name }}_install_tasks(): array { - return InstallerHooks::installTasks(); -} - -/** - * Implements hook_install_tasks_alter(). - */ -function {{ machine_name }}_install_tasks_alter(array &$tasks, array $install_state): void { - InstallerHooks::installTasksAlter($tasks, $install_state); -} - -/** - * Implements hook_form_alter(). - */ -function {{ machine_name }}_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { - InstallerHooks::formAlter($form, $form_state, $form_id); -} - -/** - * Implements hook_library_info_alter(). - */ -function {{ machine_name }}_library_info_alter(array &$libraries, string $extension): void { - InstallerHooks::libraryInfoAlter($libraries, $extension); -} - -/** - * Implements hook_theme_registry_alter(). - */ -function {{ machine_name }}_theme_registry_alter(array &$hooks): void { - InstallerHooks::themeRegistryAlter($hooks); -} - -/** - * Preprocess function for all theme hooks. - */ -function {{ machine_name }}_preprocess(array &$variables): void { - InstallerHooks::preprocess($variables); -} diff --git a/installer_kit/src/Drush/Generators/services.twig b/installer_kit/src/Drush/Generators/services.twig deleted file mode 100644 index 8600226df..000000000 --- a/installer_kit/src/Drush/Generators/services.twig +++ /dev/null @@ -1,5 +0,0 @@ -services: - Drupal\RecipeKit\RecipeAppliedSubscriber: - autowire: true - tags: - - { name: event_subscriber } diff --git a/installer_kit/src/Drush/Generators/user.role.administrator.yml b/installer_kit/src/Drush/Generators/user.role.administrator.yml deleted file mode 100644 index ca48a58b4..000000000 --- a/installer_kit/src/Drush/Generators/user.role.administrator.yml +++ /dev/null @@ -1,8 +0,0 @@ -langcode: en -status: true -dependencies: { } -id: administrator -label: Administrator -weight: 3 -is_admin: true -permissions: { } diff --git a/installer_kit/src/Form/InstallerFormInterface.php b/installer_kit/src/Form/InstallerFormInterface.php deleted file mode 100644 index b1a2e9764..000000000 --- a/installer_kit/src/Form/InstallerFormInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\RecipeKit\Form; - -use Drupal\Core\Form\FormInterface; - -/** - * Defines an interface for forms shown to the user in the early installer. - */ -interface InstallerFormInterface extends FormInterface { - - /** - * Returns an install task definition for this form. - * - * @param array $install_state - * The current install state. - * - * @return array - * An install task definition. - * - * @see hook_install_tasks() - */ - public static function toInstallTask(array $install_state): array; - -} diff --git a/installer_kit/src/Form/InstallerRecipesForm.php b/installer_kit/src/Form/InstallerRecipesForm.php deleted file mode 100644 index 7621b9fc7..000000000 --- a/installer_kit/src/Form/InstallerRecipesForm.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -namespace Drupal\RecipeKit\Form; - -use Drupal\Core\Form\FormBase; -use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element\Checkboxes; - -/** - * Provides a form to choose the site template and optional add-on recipes. - * - * @todo Present this as a mini project browser once - * https://www.drupal.org/i/3450629 is fixed. - */ -final class InstallerRecipesForm extends FormBase implements InstallerFormInterface { - - /** - * {@inheritdoc} - */ - public static function toInstallTask(array $install_state): array { - return [ - 'display_name' => t('Choose add-ons'), - 'type' => 'form', - 'run' => array_key_exists('recipes', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => static::class, - ]; - } - - /** - * {@inheritdoc} - */ - public function getFormId(): string { - return 'installer_recipes_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state, ?array $install_state = NULL): array { - assert(is_array($install_state)); - $form['add_ons'] = [ - '#type' => 'checkboxes', - '#value_callback' => static::class . '::valueCallback', - '#options' => $install_state['profile_info']['recipes']['optional'] ?? [], - ]; - - $form['actions'] = [ - 'submit' => [ - '#type' => 'submit', - '#value' => $this->t('Next'), - '#button_type' => 'primary', - '#op' => 'submit', - ], - 'skip' => [ - '#type' => 'submit', - '#value' => $this->t('Skip this step'), - '#op' => 'skip', - ], - '#type' => 'actions', - ]; - $form['#title'] = ''; - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state): void { - global $install_state; - $install_state['parameters']['recipes'] = $install_state['profile_info']['recipes']['required'] ?? []; - - $pressed_button = $form_state->getTriggeringElement(); - // Only choose add-ons if the Next button was pressed, or if the form was - // submitted programmatically (i.e., by `drush site:install`). - if (($pressed_button && $pressed_button['#op'] === 'submit') || $form_state->isProgrammed()) { - $add_ons = $form_state->getValue('add_ons', []); - $add_ons = array_filter($add_ons); - array_push($install_state['parameters']['recipes'], ...array_values($add_ons)); - } - } - - public static function valueCallback(&$element, $input, FormStateInterface $form_state): array { - // If the input was a comma-separated string or `*`, transform it -- this is - // for compatibility with `drush site:install`. - if (is_string($input)) { - $selections = $input === '*' - ? array_keys($element['#options']) - : array_map('trim', explode(',', $input)); - - $input = array_combine($selections, $selections); - } - return Checkboxes::valueCallback($element, $input, $form_state); - } - -} diff --git a/installer_kit/src/Form/InstallerSiteNameForm.php b/installer_kit/src/Form/InstallerSiteNameForm.php deleted file mode 100644 index e352b7a5d..000000000 --- a/installer_kit/src/Form/InstallerSiteNameForm.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\RecipeKit\Form; - -use Drupal\Core\Form\FormBase; -use Drupal\Core\Form\FormStateInterface; - -/** - * Defines a form to set the site name. - */ -final class InstallerSiteNameForm extends FormBase implements InstallerFormInterface { - - /** - * {@inheritdoc} - */ - public static function toInstallTask(array $install_state): array { - return [ - 'display_name' => t('Name your site'), - 'type' => 'form', - 'run' => array_key_exists('site_name', $install_state['parameters']) ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_REACHED, - 'function' => static::class, - ]; - } - - /** - * {@inheritdoc} - */ - public function getFormId(): string { - return 'installer_site_name_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state): array { - global $install_state; - - $form['site_name'] = [ - '#type' => 'textfield', - '#title' => $this->t('Site name'), - '#required' => TRUE, - '#default_value' => $install_state['forms']['install_configure_form']['site_name'] ?? $this->t('My Drupal CMS site'), - ]; - $form['actions'] = [ - '#type' => 'actions', - 'submit' => [ - '#type' => 'submit', - '#value' => $this->t('Next'), - '#button_type' => 'primary', - ], - ]; - $form['#title'] = ''; - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state): void { - global $install_state; - $install_state['parameters']['site_name'] = $form_state->getValue('site_name'); - } - -} diff --git a/installer_kit/src/InstallerHooks.php b/installer_kit/src/InstallerHooks.php deleted file mode 100644 index 949e3a4b2..000000000 --- a/installer_kit/src/InstallerHooks.php +++ /dev/null @@ -1,374 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\RecipeKit; - -use Composer\InstalledVersions; -use Drupal\Component\Utility\NestedArray; -use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Extension\ModuleInstallerInterface; -use Drupal\Core\File\FileUrlGeneratorInterface; -use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Messenger\MessengerInterface; -use Drupal\Core\Recipe\Recipe; -use Drupal\Core\Recipe\RecipeRunner; -use Drupal\Core\Render\Element\Password; -use Drupal\RecipeKit\Form\InstallerFormInterface; - -/** - * Provides hook implementations for profiles built on this kit. - * - * @internal - * Everything in this class is internal, which means it could be changed in - * any way, or removed outright, at any time without warning. It is only meant - * to be used by profiles that were generated by this kit. You should not use - * it in your own code in any way. - */ -final class InstallerHooks { - - /** - * Implements hook_install_tasks(). - */ - public static function installTasks(): array { - // If the container can be altered, wrap the messenger service to suppress - // certain messages. - $container = \Drupal::getContainer(); - if ($container instanceof ContainerBuilder) { - $container->set('messenger', new InstallerMessenger( - \Drupal::messenger(), - )); - } - - return [ - 'uninstall_profile' => [ - // As a final task, uninstall the install profile. - 'function' => static::class . '::uninstallProfile', - ], - ]; - } - - /** - * Implements hook_install_tasks_alter(). - */ - public static function installTasksAlter(array &$tasks, array $install_state): void { - $insert_before = function (string $key, array $additions) use (&$tasks): void { - $key = array_search($key, array_keys($tasks), TRUE); - if ($key === FALSE) { - return; - } - // This isn't very clean, but it's the only way to positionally splice - // into an associative (and therefore by definition unordered) array. - $tasks_before = array_slice($tasks, 0, $key, TRUE); - $tasks_after = array_slice($tasks, $key, NULL, TRUE); - $tasks = $tasks_before + $additions + $tasks_after; - }; - - // Set up any pre-database information collection forms. - $forms = []; - foreach ($install_state['profile_info']['forms'] ?? [] as $class) { - assert(is_a($class, InstallerFormInterface::class, TRUE)); - $forms[$class] = $class::toInstallTask($install_state); - } - $insert_before('install_settings_form', $forms); - - $configure_form_task = $tasks['install_configure_form']; - unset( - $tasks['install_install_profile'], - $tasks['install_configure_form'], - ); - $insert_before('install_profile_modules', [ - 'install_install_profile' => [ - 'function' => InstallerHooks::class . '::installProfile', - ], - 'install_configure_form' => $configure_form_task, - ]); - - // Set English as the default language; eventually, we'll support changing - // it mid-stream. We can't use the passed-in $install_state because it's not - // passed by reference. - $GLOBALS['install_state']['parameters'] += ['langcode' => 'en']; - - // Wrap the install_profile_modules() function, which returns a batch job, and - // add all the necessary operations to apply the chosen template recipe. - $tasks['install_profile_modules']['function'] = static::class . '::applyRecipes'; - } - - /** - * Decorates install_install_profile(), ensuring User is installed first. - */ - public static function installProfile(): void { - // We'll need User to configure the site and administrator account. - \Drupal::service(ModuleInstallerInterface::class)->install(['user']); - - // Officially install the profile so that its behaviors and visual overrides - // will be in effect for the remainder of the install process. This also - // ensures that the administrator role is created and assigned to user 1 in - // the next step. - global $install_state; - install_install_profile($install_state); - } - - /** - * Implements hook_form_alter(). - */ - public static function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void { - switch ($form_id) { - case 'install_configure_form': - static::installConfigureFormAlter($form); - break; - - case 'install_settings_form': - static::installSettingsFormAlter($form); - break; - - default: - break; - } - } - - /** - * Implements hook_form_alter() for install_configure_form. - * - * @see \Drupal\Core\Installer\Form\SiteConfigureForm - */ - private static function installConfigureFormAlter(array &$form): void { - global $install_state; - - $form['site_information']['#type'] = 'container'; - - // If we collected the site name in a previous step, don't show it now. - if (array_key_exists('site_name', $install_state['parameters'])) { - $form['site_information']['site_name'] = [ - '#type' => 'hidden', - '#default_value' => $install_state['parameters']['site_name'], - ]; - } - - // Use a custom submit handler to set the site email. - unset($form['site_information']['site_mail']); - $form['#submit'][] = static::class . '::setSiteMail'; - - $form['admin_account']['#type'] = 'container'; - // `admin` is a sensible name for user 1. - $form['admin_account']['account']['name'] = [ - '#type' => 'hidden', - '#default_value' => 'admin', - ]; - $form['admin_account']['account']['mail'] = [ - '#type' => 'email', - '#title' => t('Email'), - '#required' => TRUE, - '#default_value' => $install_state['forms']['install_configure_form']['account']['mail'] ?? '', - '#weight' => 10, - ]; - $form['admin_account']['account']['pass'] = [ - '#type' => 'password', - '#title' => t('Password'), - '#required' => TRUE, - '#default_value' => $install_state['forms']['install_configure_form']['account']['pass']['pass1'] ?? '', - '#weight' => 20, - '#value_callback' => static::class . '::passwordValue', - ]; - - // Hide the timezone selection. Core automatically uses client-side - // JavaScript to detect it, but we don't need to expose that to the user. - // But the JavaScript expects the form elements to look a certain way, so - // hiding the fields visually is the correct approach here. - // @see core/misc/timezone.js - $form['regional_settings']['#attributes']['class'][] = 'visually-hidden'; - // Don't allow the timezone selection to be tab-focused. - $form['regional_settings']['date_default_timezone']['#attributes']['tabindex'] = -1; - - $form['actions']['submit']['#value'] = t('Finish'); - } - - public static function passwordValue(&$element, $input, FormStateInterface $form_state): mixed { - // Work around this fact that Drush and `drupal install`, which submit this - // form programmatically, assume the password is a password_confirm element. - if (is_array($input) && $form_state->isProgrammed()) { - $input = $input['pass1']; - } - return Password::valueCallback($element, $input, $form_state); - } - - /** - * Custom submit handler to update the site email. - */ - public static function setSiteMail(array &$form, FormStateInterface $form_state): void { - \Drupal::configFactory() - ->getEditable('system.site') - ->set('mail', $form_state->getValue(['account', 'mail'])) - ->save(); - } - - /** - * Implements hook_form_alter() for install_settings_form. - * - * @see \Drupal\Core\Installer\Form\SiteSettingsForm - */ - private static function installSettingsFormAlter(array &$form): void { - $sqlite = 'Drupal\sqlite\Driver\Database\sqlite'; - - // Default to SQLite, if available, because it doesn't require any - // additional configuration. - if (extension_loaded('pdo_sqlite') && array_key_exists($sqlite, $form['driver']['#options'])) { - $form['driver']['#default_value'] = $sqlite; - $form['driver']['#type'] = 'select'; - - // The database file path has a sensible default value, so move it into the - // advanced options. - $form['settings'][$sqlite]['advanced_options']['database'] = $form['settings'][$sqlite]['database']; - unset($form['settings'][$sqlite]['database']); - } - } - - /** - * Runs a batch job that applies the chosen set of recipes. - * - * @param array $install_state - * An array of information about the current installation state. - * - * @return array - * The batch job definition. - */ - public static function applyRecipes(array &$install_state): array { - // If the installer ran before but failed mid-stream, don't reapply any - // recipes that were successfully applied. - $recipes_to_apply = array_diff( - $install_state['parameters']['recipes'], - \Drupal::state()->get(RecipeAppliedSubscriber::STATE_KEY, []), - ); - - // If we've already applied all the chosen recipes, there's nothing to do. - // Since we only start applying recipes once `install_profile_modules()` has - // finished, we can be safely certain that we already did that step. - if (empty($recipes_to_apply)) { - return []; - } - - $batch = install_profile_modules($install_state); - $batch['title'] = t('Setting up your site'); - - $recipe_operations = []; - - foreach ($recipes_to_apply as $name) { - $recipe = InstalledVersions::getInstallPath($name); - $recipe = Recipe::createFromDirectory($recipe); - $recipe_operations = array_merge($recipe_operations, RecipeRunner::toBatchOperations($recipe)); - } - - // Only do each recipe's batch operations once. - foreach ($recipe_operations as $operation) { - if (in_array($operation, $batch['operations'], TRUE)) { - continue; - } - else { - $batch['operations'][] = $operation; - } - } - - return $batch; - } - - /** - * Implements hook_theme_registry_alter(). - */ - public static function themeRegistryAlter(array &$hooks): void { - global $install_state; - $profile = $install_state['parameters']['profile']; - - $template = sprintf( - '%s/templates/install-page.html.twig', - $install_state['profiles'][$profile]->getPath(), - ); - if (file_exists($template)) { - $hooks['install_page']['path'] = dirname($template); - } - } - - public static function libraryInfoAlter(array &$libraries, string $extension): void { - global $install_state; - $library_overrides = $install_state['profile_info']['libraries-add'] ?? []; - - foreach ($library_overrides as $name => $override) { - [$overridden_extension, $library_name] = explode('/', $name, 2); - if ($extension === $overridden_extension) { - $libraries[$library_name] = NestedArray::mergeDeep( - $libraries[$library_name], - static::prefixLibraryAssets($override), - ); - } - } - } - - private static function prefixLibraryAssets(array $library): array { - $profile_path = static::getProfilePath(); - - $prefix_keys = function (array $items) use ($profile_path): array { - foreach ($items as $key => $value) { - // If a library file's path starts with `/`, the library collection - // system treats it as relative to the base path. - // @see \Drupal\Core\Asset\LibraryDiscoveryParser::buildByExtension() - $items["$profile_path/$key"] = $value; - unset($items[$key]); - } - return $items; - }; - - foreach ($library as $type => $section) { - if ($type === 'js') { - $library[$type] = $prefix_keys($section); - } - elseif ($type === 'css') { - foreach ($section as $subtype => $subsection) { - $library[$type][$subtype] = $prefix_keys($subsection); - } - } - } - return $library; - } - - /** - * Preprocess function for all theme hooks. - */ - public static function preprocess(array &$variables): void { - $variables['profile_path'] = static::getProfilePath(); - } - - private static function getProfilePath(): string { - global $install_state; - $profile = $install_state['parameters']['profile']; - $profile_path = $install_state['profiles'][$profile]->getPath(); - - return \Drupal::service(FileUrlGeneratorInterface::class) - ->generateString($profile_path); - } - - /** - * Uninstalls the install profile, as a final step. - * - * @see drupal_install_system() - */ - public static function uninstallProfile(): void { - global $install_state; - \Drupal::service(ModuleInstallerInterface::class)->uninstall([ - $install_state['parameters']['profile'], - ]); - - // The install is done, so we don't need the list of applied recipes anymore. - \Drupal::state()->delete(RecipeAppliedSubscriber::STATE_KEY); - - // Clear all previous status messages to avoid clutter. - \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_STATUS); - - // Invalidate the container in case any stray requests were made during the - // install process, which would have bootstrapped Drupal and cached the - // install-time container, which is now stale (during the installer, the - // container cannot be dumped, which would normally happen during the - // container rebuild triggered by uninstalling this profile). We do not want - // to redirect into Drupal with a stale container. - \Drupal::service('kernel')->invalidateContainer(); - } - -} diff --git a/installer_kit/src/InstallerMessenger.php b/installer_kit/src/InstallerMessenger.php deleted file mode 100644 index c99b0197b..000000000 --- a/installer_kit/src/InstallerMessenger.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\RecipeKit; - -use Drupal\Core\Messenger\MessengerInterface; -use Drupal\Core\StringTranslation\TranslatableMarkup; - -/** - * Decorates the messenger to suppress or alter certain install-time messages. - * - * @internal - * Everything in this class is internal, which means it could be changed in - * any way, or removed outright, at any time without warning. It is only meant - * to be used by profiles that were generated by this kit. You should not use - * it in your own code in any way. - */ -final class InstallerMessenger implements MessengerInterface { - - private static array $reject = [ - 'Congratulations, you installed @drupal!', - ]; - - public function __construct( - private readonly MessengerInterface $decorated, - ) {} - - /** - * Adds a message to the reject list. - * - * @param string $message - * The untranslated, unformatted (i.e., containing raw placeholders) message - * that should be suppressed. - */ - public static function reject(string $message): void { - static::$reject[] = $message; - } - - /** - * {@inheritdoc} - */ - public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE): static { - $raw = $message instanceof TranslatableMarkup - ? $message->getUntranslatedString() - : strval($message); - - if (!in_array($raw, static::$reject, TRUE)) { - $this->decorated->addMessage($message, $type, $repeat); - } - return $this; - } - - /** - * {@inheritdoc} - */ - public function addStatus($message, $repeat = FALSE): static { - return $this->addMessage($message, self::TYPE_STATUS, $repeat); - } - - /** - * {@inheritdoc} - */ - public function addError($message, $repeat = FALSE): static { - return $this->addMessage($message, self::TYPE_ERROR, $repeat); - } - - /** - * {@inheritdoc} - */ - public function addWarning($message, $repeat = FALSE): static { - return $this->addMessage($message, self::TYPE_WARNING, $repeat); - } - - /** - * {@inheritdoc} - */ - public function all(): array { - return $this->decorated->all(); - } - - /** - * {@inheritdoc} - */ - public function messagesByType($type): array { - return $this->decorated->messagesByType($type); - } - - /** - * {@inheritdoc} - */ - public function deleteAll(): array { - return $this->decorated->deleteAll(); - } - - /** - * {@inheritdoc} - */ - public function deleteByType($type): array { - return $this->decorated->deleteByType($type); - } - -} diff --git a/installer_kit/src/RecipeAppliedSubscriber.php b/installer_kit/src/RecipeAppliedSubscriber.php deleted file mode 100644 index 1fa3cc692..000000000 --- a/installer_kit/src/RecipeAppliedSubscriber.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\RecipeKit; - -use Drupal\Core\Recipe\RecipeAppliedEvent; -use Drupal\Core\State\StateInterface; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Tracks our applied recipes. - * - * This is done to increase fault tolerance. On hosting plans that don't have - * a ton of RAM or computing power to spare, the possibility of the installer - * timing out or failing in mid-stream is increased, especially with a big, - * complex distribution like Drupal CMS. Tracking the recipes which have been - * applied allows the installer to recover and "pick up where it left off", - * without applying recipes that have already been applied successfully. Once - * the install is done, the list of recipes is deleted. - * - * @see \Drupal\RecipeKit\InstallerHooks::applyRecipes() - * @see \Drupal\RecipeKit\InstallerHooks::uninstallProfile() - */ -final class RecipeAppliedSubscriber implements EventSubscriberInterface { - - /** - * The state key that stores the record of our applied recipes. - * - * @var string - */ - public const STATE_KEY = 'installer.applied_recipes'; - - public function __construct( - private readonly StateInterface $state, - ) {} - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents(): array { - return [ - RecipeAppliedEvent::class => 'onApply', - ]; - } - - /** - * Reacts when a recipe is applied to the site. - * - * @param \Drupal\Core\Recipe\RecipeAppliedEvent $event - * The event object. - */ - public function onApply(RecipeAppliedEvent $event): void { - $list = $this->state->get(static::STATE_KEY, []); - $list[] = basename($event->recipe->path); - $this->state->set(static::STATE_KEY, $list); - } - -} diff --git a/project_template/composer.json b/project_template/composer.json index 76bd03727..a34c5449b 100644 --- a/project_template/composer.json +++ b/project_template/composer.json @@ -34,7 +34,7 @@ "drupal/drupal_cms_seo_tools": "^1.0.1", "drupal/drupal_cms_starter": "^1.0.1", "drupal/project_browser": "@alpha", - "drupal/recipe-installer-kit": "*", + "drupal/recipe_installer_kit": "1.x-dev@dev", "drupal/webform": "@beta", "drush/drush": "^13" }, diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index 5bb16635e..0345ef8eb 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -10,8 +10,8 @@ distribution: finish_url: '/admin/dashboard/welcome' forms: - - '\Drupal\RecipeKit\Form\InstallerRecipesForm' - - '\Drupal\RecipeKit\Form\InstallerSiteNameForm' + - '\Drupal\RecipeKit\Installer\Form\RecipesForm' + - '\Drupal\RecipeKit\Installer\Form\SiteNameForm' recipes: required: @@ -41,4 +41,4 @@ libraries-add: # Explicitly provide an empty list of themes -- this prevents the installer from # injecting Stark into it. # @see install_profile_info() -themes: [] \ No newline at end of file +themes: [] diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 0b21d8801..586dc1108 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -3,17 +3,17 @@ declare(strict_types=1); use Drupal\Core\Form\FormStateInterface; -use Drupal\RecipeKit\InstallerHooks; -use Drupal\RecipeKit\InstallerMessenger; +use Drupal\RecipeKit\Installer\Hooks; +use Drupal\RecipeKit\Installer\Messenger; /** * Implements hook_install_tasks(). */ function drupal_cms_installer_install_tasks(): array { - $tasks = InstallerHooks::installTasks(); + $tasks = Hooks::installTasks(); if (getenv('IS_DDEV_PROJECT')) { - InstallerMessenger::reject( + Messenger::reject( 'All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href=":handbook_url">online handbook</a>.', ); } @@ -24,14 +24,14 @@ function drupal_cms_installer_install_tasks(): array { * Implements hook_install_tasks_alter(). */ function drupal_cms_installer_install_tasks_alter(array &$tasks, array $install_state): void { - InstallerHooks::installTasksAlter($tasks, $install_state); + Hooks::installTasksAlter($tasks, $install_state); } /** * Implements hook_form_alter(). */ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { - InstallerHooks::formAlter($form, $form_state, $form_id); + Hooks::formAlter($form, $form_state, $form_id); } /** @@ -122,19 +122,19 @@ function drupal_cms_installer_form_install_configure_form_alter(array &$form): v * Implements hook_library_info_alter(). */ function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { - InstallerHooks::libraryInfoAlter($libraries, $extension); + Hooks::libraryInfoAlter($libraries, $extension); } /** * Implements hook_theme_registry_alter(). */ function drupal_cms_installer_theme_registry_alter(array &$hooks): void { - InstallerHooks::themeRegistryAlter($hooks); + Hooks::themeRegistryAlter($hooks); } /** * Preprocess function for all theme hooks. */ function drupal_cms_installer_preprocess(array &$variables): void { - InstallerHooks::preprocess($variables); + Hooks::preprocess($variables); } diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml index 8600226df..79adafd05 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml @@ -1,5 +1,5 @@ services: - Drupal\RecipeKit\RecipeAppliedSubscriber: + Drupal\RecipeKit\Installer\RecipeAppliedSubscriber: autowire: true tags: - { name: event_subscriber } -- GitLab From 5fa5afe04214365a5f986beb9aba0feb0edac063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 17:04:19 -0500 Subject: [PATCH 27/36] Fix tests --- .../tests/src/Functional/CommandLineInstallTest.php | 2 +- .../tests/src/Functional/InteractiveInstallTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php index cef2d03d0..6f27f82a6 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Drupal\Tests\drupal_cms_installer\Functional; use Drupal\Core\Test\TestSetupTrait; -use Drupal\RecipeKit\RecipeAppliedSubscriber; +use Drupal\RecipeKit\Installer\RecipeAppliedSubscriber; use Drush\TestTraits\DrushTestTrait; use PHPUnit\Framework\TestCase; use Symfony\Component\Filesystem\Filesystem; diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php index e600830e4..26d189c59 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php @@ -10,8 +10,8 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\State\StateInterface; -use Drupal\RecipeKit\RecipeAppliedSubscriber; use Drupal\FunctionalTests\Installer\InstallerTestBase; +use Drupal\RecipeKit\Installer\RecipeAppliedSubscriber; use Drupal\user\Entity\User; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -110,7 +110,7 @@ class InteractiveInstallTest extends InstallerTestBase { $this->assertNull($this->container->get(StateInterface::class)->get(RecipeAppliedSubscriber::STATE_KEY)); // The site name and site-wide email address should have been set. - // @see \Drupal\RecipeKit\Form\SiteNameForm + // @see \Drupal\RecipeKit\Installer\Form\SiteNameForm $site_config = $this->config('system.site'); $this->assertSame('Installer Test', $site_config->get('name')); $this->assertSame("hello@good.bye", $site_config->get('mail')); -- GitLab From 6355cf55c8c9867590e42c6ad0a731798f7c0f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 17:18:28 -0500 Subject: [PATCH 28/36] Adjust constraint --- project_template/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_template/composer.json b/project_template/composer.json index a34c5449b..c3a6f46ad 100644 --- a/project_template/composer.json +++ b/project_template/composer.json @@ -34,7 +34,7 @@ "drupal/drupal_cms_seo_tools": "^1.0.1", "drupal/drupal_cms_starter": "^1.0.1", "drupal/project_browser": "@alpha", - "drupal/recipe_installer_kit": "1.x-dev@dev", + "drupal/recipe_installer_kit": "@dev", "drupal/webform": "@beta", "drush/drush": "^13" }, -- GitLab From 5f362542f07a2d9fa8dda0587348baff2fe0fa6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 22:23:10 -0500 Subject: [PATCH 29/36] Create a distinct and true theme for the installer, which is a million times saner than the alternative --- .../drupal_cms_installer.info.yml | 15 +--------- .../drupal_cms_installer.profile | 26 ++++++++++++++++-- .../css/add-ons.css | 0 .../drupal_cms_installer_theme}/css/fonts.css | 0 .../css/gin-variables.css | 0 .../css/installer-styles.css | 0 .../drupal_cms_installer_theme.info.yml | 10 +++++++ .../drupal_cms_installer_theme.libraries.yml | 13 +++++++++ .../drupal_cms_installer_theme.theme | 15 ++++++++++ .../fonts/inter-cryllic-ext.woff2 | Bin .../fonts/inter-cyrillic.woff2 | Bin .../fonts/inter-greek-ext.woff2 | Bin .../fonts/inter-greek.woff2 | Bin .../fonts/inter-italic-cyrillic-ext.woff2 | Bin .../fonts/inter-italic-cyrillic.woff2 | Bin .../fonts/inter-italic-greek-ext.woff2 | Bin .../fonts/inter-italic-greek.woff2 | Bin .../fonts/inter-italic-latin-ext.woff2 | Bin .../fonts/inter-italic-latin.woff2 | Bin .../fonts/inter-italic-vietnamese.woff2 | Bin .../fonts/inter-latin-ext.woff2 | Bin .../fonts/inter-latin.woff2 | Bin .../fonts/inter-vietnamese.woff2 | Bin .../images/arrow-right.svg | 0 .../images/check-circle.svg | 0 .../images/chevron-down.svg | 0 .../images/drupal-cms-logo.svg | 0 .../images/info.svg | 0 .../images/translate.svg | 0 .../js/progress.js | 0 .../templates/install-page.html.twig | 2 +- 31 files changed, 63 insertions(+), 18 deletions(-) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/css/add-ons.css (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/css/fonts.css (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/css/gin-variables.css (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/css/installer-styles.css (100%) create mode 100644 project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.info.yml create mode 100644 project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.libraries.yml create mode 100644 project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-cryllic-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-cyrillic.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-greek-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-greek.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-cyrillic-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-cyrillic.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-greek-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-greek.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-latin-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-latin.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-italic-vietnamese.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-latin-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-latin.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/fonts/inter-vietnamese.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/images/arrow-right.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/images/check-circle.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/images/chevron-down.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/images/drupal-cms-logo.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/images/info.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/images/translate.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/js/progress.js (100%) rename project_template/web/profiles/drupal_cms_installer/{ => themes/drupal_cms_installer_theme}/templates/install-page.html.twig (90%) diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index 0345ef8eb..5ed1b6fc4 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -8,6 +8,7 @@ distribution: name: Drupal CMS install: finish_url: '/admin/dashboard/welcome' + theme: drupal_cms_installer_theme forms: - '\Drupal\RecipeKit\Installer\Form\RecipesForm' @@ -24,20 +25,6 @@ recipes: drupal/drupal_cms_person: Person Profiles drupal/drupal_cms_project: Projects -libraries-add: - claro/maintenance-page: - css: - theme: - css/gin-variables.css: {} - css/fonts.css: {} - css/installer-styles.css: {} - css/add-ons.css: {} - dependencies: - - core/once - core/drupal.progress: - js: - js/progress.js: {} - # Explicitly provide an empty list of themes -- this prevents the installer from # injecting Stark into it. # @see install_profile_info() diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 586dc1108..fd89bd643 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -2,6 +2,7 @@ declare(strict_types=1); +use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\RecipeKit\Installer\Hooks; use Drupal\RecipeKit\Installer\Messenger; @@ -119,6 +120,7 @@ function drupal_cms_installer_form_install_configure_form_alter(array &$form): v } /** +<<<<<<< Updated upstream * Implements hook_library_info_alter(). */ function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { @@ -133,8 +135,26 @@ function drupal_cms_installer_theme_registry_alter(array &$hooks): void { } /** - * Preprocess function for all theme hooks. + * Uninstalls this install profile, as a final step. + * + * @see drupal_install_system() */ -function drupal_cms_installer_preprocess(array &$variables): void { - Hooks::preprocess($variables); +function drupal_cms_installer_uninstall_myself(): void { + \Drupal::service(ModuleInstallerInterface::class)->uninstall([ + 'drupal_cms_installer', + ]); + + // The install is done, so we don't need the list of applied recipes anymore. + \Drupal::state()->delete(RecipeAppliedSubscriber::STATE_KEY); + + // Clear all previous status messages to avoid clutter. + \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_STATUS); + + // Invalidate the container in case any stray requests were made during the + // install process, which would have bootstrapped Drupal and cached the + // install-time container, which is now stale (during the installer, the + // container cannot be dumped, which would normally happen during the + // container rebuild triggered by uninstalling this profile). We do not want + // to redirect into Drupal with a stale container. + \Drupal::service('kernel')->invalidateContainer(); } diff --git a/project_template/web/profiles/drupal_cms_installer/css/add-ons.css b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/add-ons.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/css/add-ons.css rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/add-ons.css diff --git a/project_template/web/profiles/drupal_cms_installer/css/fonts.css b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/fonts.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/css/fonts.css rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/fonts.css diff --git a/project_template/web/profiles/drupal_cms_installer/css/gin-variables.css b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/gin-variables.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/css/gin-variables.css rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/gin-variables.css diff --git a/project_template/web/profiles/drupal_cms_installer/css/installer-styles.css b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/installer-styles.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/css/installer-styles.css rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/installer-styles.css diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.info.yml b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.info.yml new file mode 100644 index 000000000..e5f16c76f --- /dev/null +++ b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.info.yml @@ -0,0 +1,10 @@ +name: Drupal CMS Installer Theme +type: theme +core_version_requirement: '>=10.3' +description: 'Themes the Drupal CMS installer. Not to be used in production.' +base theme: claro +libraries-extend: + claro/maintenance-page: + - drupal_cms_installer_theme/maintenance-page + core/drupal.progress: + - drupal_cms_installer_theme/drupal.progress diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.libraries.yml b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.libraries.yml new file mode 100644 index 000000000..04fd5c3e3 --- /dev/null +++ b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.libraries.yml @@ -0,0 +1,13 @@ +maintenance-page: + css: + theme: + css/gin-variables.css: {} + css/fonts.css: {} + css/installer-styles.css: {} + css/add-ons.css: {} + dependencies: + - core/once + +drupal.progress: + js: + js/progress.js: {} diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme new file mode 100644 index 000000000..197964354 --- /dev/null +++ b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme @@ -0,0 +1,15 @@ +<?php + +declare(strict_types=1); + +use Drupal\Core\File\FileUrlGeneratorInterface; + +function drupal_cms_installer_theme_preprocess(array &$variables): void { + // Don't show the version of Drupal. + unset($variables['site_version']); + + $images_path = \Drupal::theme()->getActiveTheme()->getPath() . '/images'; + $images_path = \Drupal::service(FileUrlGeneratorInterface::class) + ->generateString($images_path); + $variables['images_path'] = $images_path; +} diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-cryllic-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cryllic-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-cryllic-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cryllic-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-cyrillic.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cyrillic.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-cyrillic.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cyrillic.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-greek-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-greek-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-greek.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-greek.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-cyrillic-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-cyrillic-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-cyrillic.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-cyrillic.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-greek-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-greek-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-greek.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-greek.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-latin-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-latin-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-latin.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-latin.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-vietnamese.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-vietnamese.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-italic-vietnamese.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-vietnamese.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-latin-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-latin-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-latin.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-latin.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/fonts/inter-vietnamese.woff2 b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-vietnamese.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/fonts/inter-vietnamese.woff2 rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-vietnamese.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/images/arrow-right.svg b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/arrow-right.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/images/arrow-right.svg rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/arrow-right.svg diff --git a/project_template/web/profiles/drupal_cms_installer/images/check-circle.svg b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/check-circle.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/images/check-circle.svg rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/check-circle.svg diff --git a/project_template/web/profiles/drupal_cms_installer/images/chevron-down.svg b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/chevron-down.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/images/chevron-down.svg rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/chevron-down.svg diff --git a/project_template/web/profiles/drupal_cms_installer/images/drupal-cms-logo.svg b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/drupal-cms-logo.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/images/drupal-cms-logo.svg rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/drupal-cms-logo.svg diff --git a/project_template/web/profiles/drupal_cms_installer/images/info.svg b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/info.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/images/info.svg rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/info.svg diff --git a/project_template/web/profiles/drupal_cms_installer/images/translate.svg b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/translate.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/images/translate.svg rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/translate.svg diff --git a/project_template/web/profiles/drupal_cms_installer/js/progress.js b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/js/progress.js similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/js/progress.js rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/js/progress.js diff --git a/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/templates/install-page.html.twig similarity index 90% rename from project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig rename to project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/templates/install-page.html.twig index eea39d1a7..073afab49 100644 --- a/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig +++ b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/templates/install-page.html.twig @@ -12,7 +12,7 @@ <div class="cms-installer"> <header class="cms-installer__header"> <h1 class="cms-installer__heading"> - <img src="{{ profile_path }}/images/drupal-cms-logo.svg" alt="{{ 'Drupal CMS'|t }}" /> + <img src="{{ images_path }}/drupal-cms-logo.svg" alt="{{ 'Drupal CMS'|t }}" /> </h1> </header> <div class="cms-installer__wrapper"> -- GitLab From 808a709ec36b44f1aa1623f66760d4dca5803fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 22:53:46 -0500 Subject: [PATCH 30/36] Move theme one level up for clarity --- .../drupal_cms_installer.profile | 41 ------------------ .../css/add-ons.css | 0 .../css/fonts.css | 0 .../css/gin-variables.css | 0 .../css/installer-styles.css | 0 .../drupal_cms_installer_theme.info.yml | 0 .../drupal_cms_installer_theme.libraries.yml | 0 .../theme/drupal_cms_installer_theme.theme | 20 +++++++++ .../fonts/inter-cryllic-ext.woff2 | Bin .../fonts/inter-cyrillic.woff2 | Bin .../fonts/inter-greek-ext.woff2 | Bin .../fonts/inter-greek.woff2 | Bin .../fonts/inter-italic-cyrillic-ext.woff2 | Bin .../fonts/inter-italic-cyrillic.woff2 | Bin .../fonts/inter-italic-greek-ext.woff2 | Bin .../fonts/inter-italic-greek.woff2 | Bin .../fonts/inter-italic-latin-ext.woff2 | Bin .../fonts/inter-italic-latin.woff2 | Bin .../fonts/inter-italic-vietnamese.woff2 | Bin .../fonts/inter-latin-ext.woff2 | Bin .../fonts/inter-latin.woff2 | Bin .../fonts/inter-vietnamese.woff2 | Bin .../images/arrow-right.svg | 0 .../images/check-circle.svg | 0 .../images/chevron-down.svg | 0 .../images/drupal-cms-logo.svg | 0 .../images/info.svg | 0 .../images/translate.svg | 0 .../js/progress.js | 0 .../templates/install-page.html.twig | 2 +- .../drupal_cms_installer_theme.theme | 15 ------- 31 files changed, 21 insertions(+), 57 deletions(-) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/css/add-ons.css (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/css/fonts.css (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/css/gin-variables.css (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/css/installer-styles.css (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/drupal_cms_installer_theme.info.yml (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/drupal_cms_installer_theme.libraries.yml (100%) create mode 100644 project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-cryllic-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-cyrillic.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-greek-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-greek.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-cyrillic-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-cyrillic.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-greek-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-greek.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-latin-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-latin.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-italic-vietnamese.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-latin-ext.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-latin.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/fonts/inter-vietnamese.woff2 (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/images/arrow-right.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/images/check-circle.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/images/chevron-down.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/images/drupal-cms-logo.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/images/info.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/images/translate.svg (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/js/progress.js (100%) rename project_template/web/profiles/drupal_cms_installer/{themes/drupal_cms_installer_theme => theme}/templates/install-page.html.twig (91%) delete mode 100644 project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index fd89bd643..097c0af3b 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -2,7 +2,6 @@ declare(strict_types=1); -use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\RecipeKit\Installer\Hooks; use Drupal\RecipeKit\Installer\Messenger; @@ -118,43 +117,3 @@ function drupal_cms_installer_form_install_configure_form_alter(array &$form): v '#suffix' => '</div>', ]; } - -/** -<<<<<<< Updated upstream - * Implements hook_library_info_alter(). - */ -function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { - Hooks::libraryInfoAlter($libraries, $extension); -} - -/** - * Implements hook_theme_registry_alter(). - */ -function drupal_cms_installer_theme_registry_alter(array &$hooks): void { - Hooks::themeRegistryAlter($hooks); -} - -/** - * Uninstalls this install profile, as a final step. - * - * @see drupal_install_system() - */ -function drupal_cms_installer_uninstall_myself(): void { - \Drupal::service(ModuleInstallerInterface::class)->uninstall([ - 'drupal_cms_installer', - ]); - - // The install is done, so we don't need the list of applied recipes anymore. - \Drupal::state()->delete(RecipeAppliedSubscriber::STATE_KEY); - - // Clear all previous status messages to avoid clutter. - \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_STATUS); - - // Invalidate the container in case any stray requests were made during the - // install process, which would have bootstrapped Drupal and cached the - // install-time container, which is now stale (during the installer, the - // container cannot be dumped, which would normally happen during the - // container rebuild triggered by uninstalling this profile). We do not want - // to redirect into Drupal with a stale container. - \Drupal::service('kernel')->invalidateContainer(); -} diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/add-ons.css b/project_template/web/profiles/drupal_cms_installer/theme/css/add-ons.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/add-ons.css rename to project_template/web/profiles/drupal_cms_installer/theme/css/add-ons.css diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/fonts.css b/project_template/web/profiles/drupal_cms_installer/theme/css/fonts.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/fonts.css rename to project_template/web/profiles/drupal_cms_installer/theme/css/fonts.css diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/gin-variables.css b/project_template/web/profiles/drupal_cms_installer/theme/css/gin-variables.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/gin-variables.css rename to project_template/web/profiles/drupal_cms_installer/theme/css/gin-variables.css diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/installer-styles.css b/project_template/web/profiles/drupal_cms_installer/theme/css/installer-styles.css similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/css/installer-styles.css rename to project_template/web/profiles/drupal_cms_installer/theme/css/installer-styles.css diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.info.yml b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.info.yml rename to project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.libraries.yml b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.libraries.yml similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.libraries.yml rename to project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.libraries.yml diff --git a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme new file mode 100644 index 000000000..69d6a7754 --- /dev/null +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme @@ -0,0 +1,20 @@ +<?php + +declare(strict_types=1); + +use Drupal\Core\File\FileUrlGeneratorInterface; + +/** + * Preprocess hook for all templates. + */ +function drupal_cms_installer_theme_preprocess(array &$variables): void { + static $theme_path; + + if ($theme_path === NULL) { + $theme_path = \Drupal::service(FileUrlGeneratorInterface::class) + ->generateString( + \Drupal::theme()->getActiveTheme()->getPath(), + ); + } + $variables['theme_path'] = $theme_path; +} diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cryllic-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-cryllic-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cryllic-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-cryllic-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cyrillic.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-cyrillic.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-cyrillic.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-cyrillic.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-greek-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-greek-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-greek.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-greek.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-greek.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-cyrillic-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-cyrillic-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-cyrillic.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-cyrillic.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-cyrillic.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-greek-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-greek-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-greek.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-greek.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-greek.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-latin-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-latin-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-latin.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-latin.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-latin.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-vietnamese.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-vietnamese.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-italic-vietnamese.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-italic-vietnamese.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin-ext.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-latin-ext.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin-ext.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-latin-ext.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-latin.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-latin.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-latin.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-vietnamese.woff2 b/project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-vietnamese.woff2 similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/fonts/inter-vietnamese.woff2 rename to project_template/web/profiles/drupal_cms_installer/theme/fonts/inter-vietnamese.woff2 diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/arrow-right.svg b/project_template/web/profiles/drupal_cms_installer/theme/images/arrow-right.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/arrow-right.svg rename to project_template/web/profiles/drupal_cms_installer/theme/images/arrow-right.svg diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/check-circle.svg b/project_template/web/profiles/drupal_cms_installer/theme/images/check-circle.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/check-circle.svg rename to project_template/web/profiles/drupal_cms_installer/theme/images/check-circle.svg diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/chevron-down.svg b/project_template/web/profiles/drupal_cms_installer/theme/images/chevron-down.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/chevron-down.svg rename to project_template/web/profiles/drupal_cms_installer/theme/images/chevron-down.svg diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/drupal-cms-logo.svg b/project_template/web/profiles/drupal_cms_installer/theme/images/drupal-cms-logo.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/drupal-cms-logo.svg rename to project_template/web/profiles/drupal_cms_installer/theme/images/drupal-cms-logo.svg diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/info.svg b/project_template/web/profiles/drupal_cms_installer/theme/images/info.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/info.svg rename to project_template/web/profiles/drupal_cms_installer/theme/images/info.svg diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/translate.svg b/project_template/web/profiles/drupal_cms_installer/theme/images/translate.svg similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/images/translate.svg rename to project_template/web/profiles/drupal_cms_installer/theme/images/translate.svg diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/js/progress.js b/project_template/web/profiles/drupal_cms_installer/theme/js/progress.js similarity index 100% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/js/progress.js rename to project_template/web/profiles/drupal_cms_installer/theme/js/progress.js diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/templates/install-page.html.twig b/project_template/web/profiles/drupal_cms_installer/theme/templates/install-page.html.twig similarity index 91% rename from project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/templates/install-page.html.twig rename to project_template/web/profiles/drupal_cms_installer/theme/templates/install-page.html.twig index 073afab49..d514f816b 100644 --- a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/templates/install-page.html.twig +++ b/project_template/web/profiles/drupal_cms_installer/theme/templates/install-page.html.twig @@ -12,7 +12,7 @@ <div class="cms-installer"> <header class="cms-installer__header"> <h1 class="cms-installer__heading"> - <img src="{{ images_path }}/drupal-cms-logo.svg" alt="{{ 'Drupal CMS'|t }}" /> + <img src="{{ theme_path }}/images/drupal-cms-logo.svg" alt="{{ 'Drupal CMS'|t }}" /> </h1> </header> <div class="cms-installer__wrapper"> diff --git a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme b/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme deleted file mode 100644 index 197964354..000000000 --- a/project_template/web/profiles/drupal_cms_installer/themes/drupal_cms_installer_theme/drupal_cms_installer_theme.theme +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -declare(strict_types=1); - -use Drupal\Core\File\FileUrlGeneratorInterface; - -function drupal_cms_installer_theme_preprocess(array &$variables): void { - // Don't show the version of Drupal. - unset($variables['site_version']); - - $images_path = \Drupal::theme()->getActiveTheme()->getPath() . '/images'; - $images_path = \Drupal::service(FileUrlGeneratorInterface::class) - ->generateString($images_path); - $variables['images_path'] = $images_path; -} -- GitLab From b194ef8dea8b62eba9d5d53b2af07fd1940c7ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Tue, 4 Feb 2025 23:05:10 -0500 Subject: [PATCH 31/36] Move presentational form changes into the theme --- .../drupal_cms_installer.profile | 75 ----------------- .../theme/drupal_cms_installer_theme.theme | 80 +++++++++++++++++++ 2 files changed, 80 insertions(+), 75 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile index 097c0af3b..e40d9c5fa 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.profile @@ -34,86 +34,11 @@ function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_ Hooks::formAlter($form, $form_state, $form_id); } -/** - * Implements hook_form_alter() for installer_site_name_form. - */ -function drupal_cms_installer_form_installer_site_name_form_alter(array &$form): void { - $form['#title'] = t('Give your site a name'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t('You can change this later.'), - '#suffix' => '</p>', - '#weight' => -100, - ]; - $form['site_name'] += [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - ]; - $form['actions']['submit']['#attributes']['class'] = ['button--next']; -} - -/** - * Implements hook_form_alter() for installer_recipes_form. - */ -function drupal_cms_installer_form_installer_recipes_form_alter(array &$form): void { - $form['#title'] = t('Get started'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t('You can select pre-configured types of content now, or add them later.'), - '#suffix' => '</p>', - '#weight' => -100, - ]; - $form['add_ons'] += [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - ]; - $form['add_ons']['help'] = [ - '#prefix' => '<p class="cms-installer__info">', - '#markup' => t('Don’t see what you’re looking for? You can set up customized content later.'), - '#suffix' => '</p>', - '#weight' => 100, - ]; - - $form['actions']['submit']['#attributes']['class'] = ['button--next']; -} - -/** - * Implements hook_form_alter() for install_settings_form. - */ -function drupal_cms_installer_form_install_settings_form_alter(array &$form): void { - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t("You don't need to change anything here unless you want to use a different database type."), - '#suffix' => '</p>', - '#weight' => -50, - ]; -} - /** * Implements hook_form_alter() for install_configure_form. */ function drupal_cms_installer_form_install_configure_form_alter(array &$form): void { - $form['#title'] = t('Create your account'); - - $form['help'] = [ - '#prefix' => '<p class="cms-installer__subhead">', - '#markup' => t('Creating an account allows you to log in to your site.'), - '#suffix' => '</p>', - '#weight' => -40, - ]; - // We always install Automatic Updates, so we don't need to expose the update // notification settings. $form['update_notifications']['#access'] = FALSE; - - $form['admin_account']['account']['mail'] += [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - ]; - $form['admin_account']['account']['pass'] += [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - ]; } diff --git a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme index 69d6a7754..ee4698de7 100644 --- a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme @@ -18,3 +18,83 @@ function drupal_cms_installer_theme_preprocess(array &$variables): void { } $variables['theme_path'] = $theme_path; } + +/** + * Implements hook_form_FORM_ID_alter() for installer_site_name_form. + */ +function drupal_cms_installer_theme_form_installer_site_name_form_alter(array &$form): void { + $form['#title'] = t('Give your site a name'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('You can change this later.'), + '#suffix' => '</p>', + '#weight' => -100, + ]; + $form['site_name'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + $form['actions']['submit']['#attributes']['class'] = ['button--next']; +} + +/** + * Implements hook_form_FORM_ID_alter() for installer_recipes_form. + */ +function drupal_cms_installer_theme_form_installer_recipes_form_alter(array &$form): void { + $form['#title'] = t('Get started'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('You can select pre-configured types of content now, or add them later.'), + '#suffix' => '</p>', + '#weight' => -100, + ]; + $form['add_ons'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + $form['add_ons']['help'] = [ + '#prefix' => '<p class="cms-installer__info">', + '#markup' => t('Don’t see what you’re looking for? You can set up customized content later.'), + '#suffix' => '</p>', + '#weight' => 100, + ]; + + $form['actions']['submit']['#attributes']['class'] = ['button--next']; +} + +/** + * Implements hook_form_FORM_ID_alter() for install_settings_form. + */ +function drupal_cms_installer_theme_form_install_settings_form_alter(array &$form): void { + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t("You don't need to change anything here unless you want to use a different database type."), + '#suffix' => '</p>', + '#weight' => -50, + ]; +} + +/** + * Implements hook_form_FORM_ID_alter() for install_configure_form. + */ +function drupal_cms_installer_theme_form_install_configure_form_alter(array &$form): void { + $form['#title'] = t('Create your account'); + + $form['help'] = [ + '#prefix' => '<p class="cms-installer__subhead">', + '#markup' => t('Creating an account allows you to log in to your site.'), + '#suffix' => '</p>', + '#weight' => -40, + ]; + + $form['admin_account']['account']['mail'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + $form['admin_account']['account']['pass'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; +} -- GitLab From 7d311f54fe3af26682ab969ab91122273bcefdd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Wed, 5 Feb 2025 06:22:57 -0500 Subject: [PATCH 32/36] Safeguards --- .../drupal_cms_installer.info.yml | 2 +- .../theme/drupal_cms_installer_theme.info.yml | 2 +- .../theme/drupal_cms_installer_theme.theme | 22 ++++++++++++------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index 5ed1b6fc4..52fbc10ff 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -1,6 +1,6 @@ name: Drupal CMS Installer type: profile -core_version_requirement: '>=10.3' +core_version_requirement: '^11.1' description: 'Provides install-time tweaks for Drupal CMS. Not to be used in production.' # Use the `distribution` key to skip the installer's profile selection step. diff --git a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml index e5f16c76f..945a3869b 100644 --- a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml @@ -1,6 +1,6 @@ name: Drupal CMS Installer Theme type: theme -core_version_requirement: '>=10.3' +core_version_requirement: '^11.1' description: 'Themes the Drupal CMS installer. Not to be used in production.' base theme: claro libraries-extend: diff --git a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme index ee4698de7..862be0bda 100644 --- a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme @@ -89,12 +89,18 @@ function drupal_cms_installer_theme_form_install_configure_form_alter(array &$fo '#weight' => -40, ]; - $form['admin_account']['account']['mail'] += [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - ]; - $form['admin_account']['account']['pass'] += [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - ]; + // Use isset() to guard against the possibility that core will change the + // structure of this form in a minor release. + if (isset($form['admin_account']['account']['name'])) { + $form['admin_account']['account']['mail'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + } + if (isset($form['admin_account']['account']['pass'])) { + $form['admin_account']['account']['pass'] += [ + '#prefix' => '<div class="cms-installer__form-group">', + '#suffix' => '</div>', + ]; + } } -- GitLab From f69bd6829861dd5f74331044b5a8ee17012382f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Wed, 5 Feb 2025 06:55:25 -0500 Subject: [PATCH 33/36] Use combination syntax I committed to RInK --- .../drupal_cms_installer.info.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml index 52fbc10ff..a3a4b3e3b 100644 --- a/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml +++ b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.info.yml @@ -18,12 +18,18 @@ recipes: required: - drupal/drupal_cms_starter optional: - drupal/drupal_cms_blog: Blog - drupal/drupal_cms_case_study: Case Studies - drupal/drupal_cms_events: Events - drupal/drupal_cms_news: News - drupal/drupal_cms_person: Person Profiles - drupal/drupal_cms_project: Projects + 'Blog': + - drupal/drupal_cms_blog + 'Case Studies': + - drupal/drupal_cms_case_study + 'Events': + - drupal/drupal_cms_events + 'News': + - drupal/drupal_cms_news + 'Person Profiles': + - drupal/drupal_cms_person + 'Projects': + - drupal/drupal_cms_project # Explicitly provide an empty list of themes -- this prevents the installer from # injecting Stark into it. -- GitLab From 5492b6190a29b284a683ea7b730348ec771fdcf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Wed, 5 Feb 2025 07:40:02 -0500 Subject: [PATCH 34/36] No need for preprocess to be generic --- .../theme/drupal_cms_installer_theme.theme | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme index 862be0bda..4ca6b3dfa 100644 --- a/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme @@ -5,18 +5,12 @@ declare(strict_types=1); use Drupal\Core\File\FileUrlGeneratorInterface; /** - * Preprocess hook for all templates. + * Implements template_preprocess_install_page(). */ -function drupal_cms_installer_theme_preprocess(array &$variables): void { - static $theme_path; - - if ($theme_path === NULL) { - $theme_path = \Drupal::service(FileUrlGeneratorInterface::class) - ->generateString( - \Drupal::theme()->getActiveTheme()->getPath(), - ); - } - $variables['theme_path'] = $theme_path; +function drupal_cms_installer_theme_preprocess_install_page(array &$variables): void { + $theme_path = \Drupal::theme()->getActiveTheme()->getPath(); + $variables['theme_path'] = \Drupal::service(FileUrlGeneratorInterface::class) + ->generateString($theme_path); } /** -- GitLab From e1305b5ff7109bb0d0f6b3dd92a281ffb2374e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Wed, 5 Feb 2025 07:47:30 -0500 Subject: [PATCH 35/36] Test that the theme is not installed post-install --- .../tests/src/Functional/InteractiveInstallTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php index 26d189c59..1a72dbc69 100644 --- a/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php +++ b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/InteractiveInstallTest.php @@ -123,8 +123,9 @@ class InteractiveInstallTest extends InstallerTestBase { $this->assertContains('administrator', $account->getRoles()); // The installer should have uninstalled itself. - // @see drupal_cms_installer_uninstall_myself() $this->assertFalse($this->container->getParameter('install_profile')); + // The theme used in the installer, should not be installed. + $this->assertArrayNotHasKey('drupal_cms_installer_theme', $this->config('core.extension')->get('theme')); // Ensure that there are non-core extensions installed, which proves that // recipes were applied during site installation. -- GitLab From 12e57927dea514f0bc2c114da9123948c0b3ee2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Wed, 5 Feb 2025 11:04:31 -0500 Subject: [PATCH 36/36] Use constrained version of recipe kit --- project_template/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_template/composer.json b/project_template/composer.json index c3a6f46ad..ea00d5503 100644 --- a/project_template/composer.json +++ b/project_template/composer.json @@ -34,7 +34,7 @@ "drupal/drupal_cms_seo_tools": "^1.0.1", "drupal/drupal_cms_starter": "^1.0.1", "drupal/project_browser": "@alpha", - "drupal/recipe_installer_kit": "@dev", + "drupal/recipe_installer_kit": "^1@alpha", "drupal/webform": "@beta", "drush/drush": "^13" }, -- GitLab