diff --git a/project_template/composer.json b/project_template/composer.json index fd08374a6f7e3979e70e78bbad6f0545ba8bafee..ea00d5503495149483359013483d4ffc2a66d997 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": "^1@alpha", "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 bd7a5238a5909ae49eb56d19b99e9f877b0b792b..a3a4b3e3b3724ad97249f102fa101e9aadf22721 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,13 +1,36 @@ 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.' -# 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' + theme: drupal_cms_installer_theme + +forms: + - '\Drupal\RecipeKit\Installer\Form\RecipesForm' + - '\Drupal\RecipeKit\Installer\Form\SiteNameForm' + +recipes: + required: + - drupal/drupal_cms_starter + optional: + '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. # @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 1ad09758421945a2913b7db86529893603a5f7c3..e40d9c5faa01ad6e4fa10bd74057fbfaf1f35590 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,347 +2,43 @@ 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; - -const SQLITE_DRIVER = 'Drupal\sqlite\Driver\Database\sqlite'; +use Drupal\RecipeKit\Installer\Hooks; +use Drupal\RecipeKit\Installer\Messenger; /** * 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'); + $tasks = Hooks::installTasks(); - // 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(), - )); + if (getenv('IS_DDEV_PROJECT')) { + 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>.', + ); } - - return [ - 'drupal_cms_installer_uninstall_myself' => [ - // As a final task, this profile should uninstall itself. - ], - ]; + return $tasks; } /** * 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' => 'drupal_cms_installer_install_myself', - ], - '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'] = '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); + Hooks::installTasksAlter($tasks, $install_state); } /** - * 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, - ]; - } +function drupal_cms_installer_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { + Hooks::formAlter($form, $form_state, $form_id); } /** * 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; - +function drupal_cms_installer_form_install_configure_form_alter(array &$form): void { // 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); -} - -/** - * 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(). - */ -function drupal_cms_installer_library_info_alter(array &$libraries, string $extension): void { - global $install_state; - // 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(); - - 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"] = []; - } -} - -/** - * 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(). - */ -function drupal_cms_installer_theme_registry_alter(array &$hooks): void { - global $install_state; - $installer_path = $install_state['profiles']['drupal_cms_installer']->getPath(); - - $hooks['install_page']['path'] = $installer_path . '/templates'; -} - -/** - * Preprocess function for all pages in the installer. - */ -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; - $images_path = $install_state['profiles']['drupal_cms_installer']->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/drupal_cms_installer.services.yml b/project_template/web/profiles/drupal_cms_installer/drupal_cms_installer.services.yml index 40478993ccd15756811606db8e0bbd48b93ab995..79adafd058fa05be48b7630feb20f259863bf8ce 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\RecipeKit\Installer\RecipeAppliedSubscriber: autowire: true tags: - { name: event_subscriber } diff --git a/project_template/web/profiles/drupal_cms_installer/src/Form/RecipesForm.php b/project_template/web/profiles/drupal_cms_installer/src/Form/RecipesForm.php deleted file mode 100644 index 38c0de6dc4b8074f4411ce1b89c04696ebc902ad..0000000000000000000000000000000000000000 --- a/project_template/web/profiles/drupal_cms_installer/src/Form/RecipesForm.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -namespace Drupal\drupal_cms_installer\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; - -/** - * 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 RecipesForm extends FormBase { - - /** - * {@inheritdoc} - */ - public function getFormId(): string { - return 'drupal_cms_installer_recipes_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state): 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>', - ]; - - $form['add_ons'] = [ - '#prefix' => '<div class="cms-installer__form-group">', - '#suffix' => '</div>', - '#type' => 'checkboxes', - '#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'] ?? []); - - foreach ($optional_recipes as $name) { - $recipe = $cookbook_path . '/' . basename($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']['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', - '#value' => $this->t('Skip this step'), - '#op' => 'skip', - ], - '#type' => 'actions', - ]; - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state): void { - global $install_state; - $install_state['parameters']['recipes'] = ['drupal_cms_starter']; - - $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/project_template/web/profiles/drupal_cms_installer/src/Form/SiteNameForm.php b/project_template/web/profiles/drupal_cms_installer/src/Form/SiteNameForm.php deleted file mode 100644 index 445ca9764497e5b46a8064821727eefdca183449..0000000000000000000000000000000000000000 --- a/project_template/web/profiles/drupal_cms_installer/src/Form/SiteNameForm.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\drupal_cms_installer\Form; - -use Drupal\Core\Form\FormBase; -use Drupal\Core\Form\FormStateInterface; - -/** - * Defines a form to set the site name. - */ -final class SiteNameForm extends FormBase { - - /** - * {@inheritdoc} - */ - public function getFormId(): string { - return 'drupal_cms_installer_site_name_form'; - } - - /** - * {@inheritdoc} - */ - 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, - '#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', - '#attributes' => [ - 'class' => ['button--next'] - ], - ], - ]; - - 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/project_template/web/profiles/drupal_cms_installer/src/MessageInterceptor.php b/project_template/web/profiles/drupal_cms_installer/src/MessageInterceptor.php deleted file mode 100644 index 1f5958065c472ace0502d0d6edbdc481f451a8d6..0000000000000000000000000000000000000000 --- a/project_template/web/profiles/drupal_cms_installer/src/MessageInterceptor.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\drupal_cms_installer; - -use Drupal\Core\Messenger\MessengerInterface; -use Drupal\Core\StringTranslation\TranslatableMarkup; - -/** - * Decorates the messenger to suppress or alter certain install-time messages. - */ -final class MessageInterceptor implements MessengerInterface { - - private 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>.'; - } - } - - /** - * {@inheritdoc} - */ - public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE): static { - $raw = $message instanceof TranslatableMarkup - ? $message->getUntranslatedString() - : strval($message); - - if (!in_array($raw, $this->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/project_template/web/profiles/drupal_cms_installer/src/RecipeAppliedSubscriber.php b/project_template/web/profiles/drupal_cms_installer/src/RecipeAppliedSubscriber.php deleted file mode 100644 index d9babe3371edc458fd75921bc6daa507ed9c125f..0000000000000000000000000000000000000000 --- a/project_template/web/profiles/drupal_cms_installer/src/RecipeAppliedSubscriber.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\drupal_cms_installer; - -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_cms_installer_apply_recipes() - * @see drupal_cms_installer_uninstall_myself() - */ -final class RecipeAppliedSubscriber implements EventSubscriberInterface { - - /** - * The state key that stores the record of our applied recipes. - * - * @var string - */ - public const STATE_KEY = 'drupal_cms_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/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php b/project_template/web/profiles/drupal_cms_installer/tests/src/Functional/CommandLineInstallTest.php index e70bf3a883d47360e0e689f5a0dd9616b7d190a2..6f27f82a694a9c1c08ea6a72f65f87ba764026e2 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\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 10e629b4632d35e72cf14625cd1af7bf094b45c8..1a72dbc693b9f39fc8af72afe67e0bbea21b6365 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\drupal_cms_installer\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\drupal_cms_installer\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')); @@ -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. diff --git a/project_template/web/profiles/drupal_cms_installer/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/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/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/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/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/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/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/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/theme/drupal_cms_installer_theme.info.yml b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..945a3869ba1042098b97d0b35b1e271e9a4db4cd --- /dev/null +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.info.yml @@ -0,0 +1,10 @@ +name: Drupal CMS Installer Theme +type: theme +core_version_requirement: '^11.1' +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/theme/drupal_cms_installer_theme.libraries.yml b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.libraries.yml new file mode 100644 index 0000000000000000000000000000000000000000..04fd5c3e37f5df60a4230b863047e4e1a68a67eb --- /dev/null +++ b/project_template/web/profiles/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/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 0000000000000000000000000000000000000000..4ca6b3dfa4454d1c6dc2bbdfdb16a00348e012a0 --- /dev/null +++ b/project_template/web/profiles/drupal_cms_installer/theme/drupal_cms_installer_theme.theme @@ -0,0 +1,100 @@ +<?php + +declare(strict_types=1); + +use Drupal\Core\File\FileUrlGeneratorInterface; + +/** + * Implements template_preprocess_install_page(). + */ +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); +} + +/** + * 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, + ]; + + // 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>', + ]; + } +} diff --git a/project_template/web/profiles/drupal_cms_installer/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/templates/install-page.html.twig b/project_template/web/profiles/drupal_cms_installer/theme/templates/install-page.html.twig similarity index 80% rename from project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig rename to project_template/web/profiles/drupal_cms_installer/theme/templates/install-page.html.twig index f5d3676bfedebe9038cf889523f6bd7c93040b37..d514f816b37eb51a541e25e067e4360d1d133ebe 100644 --- a/project_template/web/profiles/drupal_cms_installer/templates/install-page.html.twig +++ b/project_template/web/profiles/drupal_cms_installer/theme/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="{{ theme_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 %} diff --git a/recipes/drupal_cms_starter/composer.json b/recipes/drupal_cms_starter/composer.json index 1ea92aa545d2903e7593f03ea796f05ea972d309..69d71064591e3dd0158aebe4d7fb8ce2a63f3f0d 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" }