<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>Drupal CMS</title>
    <description>Stay informed about developments in Drupal CMS.</description>
    <link>https://www.drupal.org/project/drupal-cms</link>
    <item>
      <title>#3584002: The site exporter should try to actually determine package names, rather than assuming they all start with `drupal/`</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3584002.md</link>
      <guid isPermaLink="false">019d857f-33e5-770a-9eef-867f82070a9e</guid>
      <pubDate>Thu, 09 Apr 2026 19:40:45 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/61236c82f6045a2e6c62eaf3a35a24c676b777d8"&gt;61236c8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3584002"&gt;#3584002&lt;/a&gt; · 1 contributor · 8 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The site exporter previously assumed all non-core extensions had Composer package names starting with &lt;code&gt;drupal/&lt;/code&gt;. This confused people and created extra work when extensions used a different vendor namespace. The exporter now asks Composer for the actual package name, falling back to &lt;code&gt;drupal/&lt;/code&gt; only when it cannot be determined.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteExporter&lt;/code&gt; class in &lt;code&gt;drupal_cms_helper&lt;/code&gt; builds a list of Composer dependencies when exporting a site as a recipe. In &lt;code&gt;getExtensionRequirements()&lt;/code&gt;, the old code derived the package name with &lt;code&gt;'drupal/' . ($extension-&amp;gt;info['project'] ?? $name)&lt;/code&gt;, which is wrong for any extension whose Composer package uses a non-&lt;code&gt;drupal&lt;/code&gt; vendor prefix.&lt;/p&gt;
&lt;p&gt;The fix adds a private &lt;code&gt;getPackageName()&lt;/code&gt; method that runs &lt;code&gt;composer config name --working-dir=&amp;lt;extension path&amp;gt;&lt;/code&gt; via &lt;code&gt;Symfony\Component\Process\Process&lt;/code&gt; to read the actual package name from the extension's &lt;code&gt;composer.json&lt;/code&gt;. The PHP interpreter and Composer executable paths are statically cached across calls since they are &amp;quot;a tad expensive to compute.&amp;quot; Composer is located using &lt;code&gt;ExecutableFinder&lt;/code&gt; (with &lt;code&gt;.phar&lt;/code&gt; added as a suffix), checking both the system PATH and a locally installed &lt;code&gt;composer/composer&lt;/code&gt; package via &lt;code&gt;InstalledVersions::getInstallPath()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the process fails, the method falls back to the old &lt;code&gt;drupal/&lt;/code&gt; prefix logic and logs a warning. The author acknowledged this is a performance hit but noted that exporting a site is not performance-sensitive.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: April 9, 2026&lt;/li&gt;
&lt;li&gt;Committed: April 9, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;antiorario&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3580694: The project template should always place config outside the web root by default</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3580694.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-950cfdc8e447</guid>
      <category>Site owners</category>
      <pubDate>Tue, 24 Mar 2026 21:33:54 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/1accadea08e646807555a3b72a5441695ffbb1fe"&gt;1accade&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3580694"&gt;#3580694&lt;/a&gt; · 1 contributor · 6 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 3&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Drupal CMS project template now places the configuration sync directory outside the web root by default. New sites created from the template will store exported configuration in a &lt;code&gt;config/sync&lt;/code&gt; directory at the project root, preventing direct web access to configuration files. This follows Drupal security best practices. Existing sites are not affected.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;Affects site owners.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Config change:&lt;/strong&gt; New projects place config sync directory at &lt;code&gt;../config/sync&lt;/code&gt; (outside web root) by default&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The project template already uses a relocated web root at &lt;code&gt;web/&lt;/code&gt;. This change adds a &lt;code&gt;config/sync&lt;/code&gt; directory as a sibling to the web root, keeping configuration files outside the publicly accessible directory tree.&lt;/p&gt;
&lt;p&gt;The implementation uses the drupal-scaffold Composer plugin's file-mapping feature to append settings to &lt;code&gt;default.settings.php&lt;/code&gt;. A new &lt;code&gt;assets/append-default.settings.php.txt&lt;/code&gt; file contains PHP code that sets &lt;code&gt;$settings['config_sync_directory']&lt;/code&gt; to &lt;code&gt;realpath('../config/sync')&lt;/code&gt; if that directory exists. The template includes an empty &lt;code&gt;config/sync&lt;/code&gt; directory with a &lt;code&gt;.gitkeep&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The test suite in &lt;code&gt;SiteTemplateInstallTest.php&lt;/code&gt; was updated to mirror the new &lt;code&gt;assets/&lt;/code&gt; directory along with &lt;code&gt;composer.json&lt;/code&gt;. The test switched from using &lt;code&gt;copy()&lt;/code&gt; to &lt;code&gt;Filesystem::mirror()&lt;/code&gt; with a Finder to include both the assets directory and composer.json file.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 21, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 24, 2026 (2 days and 2 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;brianperry&lt;/li&gt;
&lt;li&gt;dorficus (HeroDevs)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3580308: Add 'Created by' to the site template listing in the installer</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3580308.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-9502f9f4db62</guid>
      <pubDate>Thu, 19 Mar 2026 15:56:56 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal task&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/5c4207fa50b170b69990e0773ecec16239be7bb8"&gt;5c4207f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3580308"&gt;#3580308&lt;/a&gt; · 2 contributors · 8 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Drupal CMS installer now displays who created each site template on the installation card. This helps site builders understand the source and authorship of available templates when choosing which one to install. Templates now show a &amp;quot;Created by&amp;quot; line with the organization or individual responsible for the template.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteTemplate&lt;/code&gt; class now includes an optional &lt;code&gt;creator&lt;/code&gt; property that stores the name of the template's creator or organization. This property is populated from the &lt;code&gt;site-templates.yml&lt;/code&gt; configuration file for contributed templates, or from a recipe's &lt;code&gt;extra.drupal_cms_installer.creator&lt;/code&gt; key for recipe-based templates.&lt;/p&gt;
&lt;p&gt;The constructor was updated to accept the &lt;code&gt;creator&lt;/code&gt; parameter and pass it through to the template preprocessor. The &lt;code&gt;fromRecipe()&lt;/code&gt; method was also updated to extract creator information from the recipe's extra metadata.&lt;/p&gt;
&lt;p&gt;The template card rendering adds a new &amp;quot;Created by&amp;quot; line in the description area, styled with smaller text and lighter color to distinguish it from the main description. The Twig template checks if a creator is set and displays it using a translatable string. CSS adjustments ensure proper spacing between the template name, creator attribution, and description text.&lt;/p&gt;
&lt;p&gt;A default screenshot file was also added to serve as a fallback when templates don't provide their own screenshot, and the &lt;code&gt;$screenshot&lt;/code&gt; parameter now defaults to this file if not explicitly provided.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 19, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 19, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3580130: Add a generic AGENTS.md to the project template</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3580130.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94f8022f59e8</guid>
      <pubDate>Thu, 19 Mar 2026 02:18:51 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/d6f67335b202e01c4e0776f1d8bf5b9a0da168b8"&gt;d6f6733&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3580130"&gt;#3580130&lt;/a&gt; · 1 contributor · 10 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;New Drupal projects now include an AGENTS.md file that provides guidance for AI coding assistants. This file documents common commands, workflows, and best practices for working with a Drupal site using DDEV and Composer. It helps AI agents understand how to start the local environment, install modules, manage configuration, and follow project conventions. The file is intended as a starting point that can be customized for specific projects.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;AGENTS.md&lt;/code&gt; file is added to the &lt;code&gt;project_template&lt;/code&gt; directory and provides machine-readable guidance for AI coding assistants working with Drupal projects. The file covers four main sections: local DDEV environment commands, common Drupal workflows (adding modules, running updates, importing and exporting configuration), guardrails to prevent common mistakes (like committing secrets or vendor directories), and references to DDEV and Drupal configuration management documentation.&lt;/p&gt;
&lt;p&gt;The file assumes a standard Composer-based Drupal installation with DDEV for local development. It uses simple bullet-point format with concrete command examples that AI agents can parse and execute. All commands are prefixed with &lt;code&gt;ddev&lt;/code&gt; to run in the containerized environment.&lt;/p&gt;
&lt;p&gt;The tagging script was updated to transform the file for the cPanel template variant, which uses the current directory as webroot instead of a &lt;code&gt;web/&lt;/code&gt; subdirectory. This transformation strips &lt;code&gt;web/&lt;/code&gt; prefixes from paths mentioned in the AGENTS.md file to match the cPanel project structure.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 19, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 19, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;scott falconer (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3579194: Add the partner templates to the installer</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3579194.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94e5d0e70a55</guid>
      <pubDate>Wed, 18 Mar 2026 17:24:28 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal task&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/37d8c2ad508395e981c39dcbd1e595eb1df5b49c"&gt;37d8c2a&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3579194"&gt;#3579194&lt;/a&gt; · 3 contributors · 10 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 4&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS now includes six site templates in the installer: Meridian Charter School (a paid template), Archimedes, Convivial Gov, Healthcare, Pulse, and Local. These templates provide ready-to-use starting points for education, government, healthcare, and local council websites. Site builders can now select from these professionally designed templates during installation to quickly launch sites tailored to specific sectors.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;site-templates.yml&lt;/code&gt; file in the &lt;code&gt;drupal_cms_installer&lt;/code&gt; module now defines six site templates instead of an empty array. Each template entry specifies its name, description, screenshot URL, package name, and related links. One template (Meridian Charter School) includes purchase information with pricing, purchase URL, and a validation URL for license key verification.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ContentLoader&lt;/code&gt; class was updated to exclude &lt;code&gt;easy_email&lt;/code&gt; entities from export, alongside the existing &lt;code&gt;search_api_task&lt;/code&gt; exclusion. This prevents email entities from being inadvertently included in content exports when creating site templates.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;SiteTemplate::createFromRecipe()&lt;/code&gt; method now reads an optional &lt;code&gt;links&lt;/code&gt; array from the recipe's extra metadata under the &lt;code&gt;drupal_cms_installer&lt;/code&gt; key, allowing recipe-based templates to define their own related links.&lt;/p&gt;
&lt;p&gt;CSS changes were made to improve the installer UI. The arrow-up-right icon that was previously only shown for premium template links is now displayed for all external links in template descriptions. The max-width restriction on template descriptions was removed, and some CSS selectors were consolidated between the add-ons and premium template stylesheets.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;site-templates.yml&lt;/code&gt; file is always pulled from the default branch of the repository, so it was removed from the 2.1.x release branch during the cherry-pick to avoid conflicts.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 13, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 18, 2026 (4 days and 2 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;mherchel (Dripyard)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3579163: Add support for listing paid site templates in the installer</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3579163.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94e34423fd91</guid>
      <pubDate>Tue, 17 Mar 2026 22:08:57 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/689f58926839a8c55a71b617679c1df6dde3c90d"&gt;689f589&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3579163"&gt;#3579163&lt;/a&gt; · 3 contributors · 43 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 4&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Drupal CMS installer can now list and install paid site templates. During installation, site builders can browse premium templates, view pricing and purchase links, and install them using a license key provided by the vendor. When a paid template is selected, the installer opens a purchase URL in a new tab, then prompts for a license key that authorizes Composer to download the package from the vendor's repository. This infrastructure supports multiple site template vendors offering commercial starter kits through the installer.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The installer's site template infrastructure was extended to represent both free and paid templates through a &lt;code&gt;SiteTemplate&lt;/code&gt; value object. This object encapsulates metadata including name, description, screenshot, package information, pricing, purchase URLs, and repository details.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;SiteTemplateForm&lt;/code&gt; reads template definitions from a remotely curated &lt;code&gt;site-templates.yml&lt;/code&gt; file, and supports a per-site override via &lt;code&gt;sites/default/site-templates.php&lt;/code&gt; for hosts or testing scenarios. Templates can specify an alternate Composer repository and an authorization type (currently only &lt;code&gt;bearer&lt;/code&gt; tokens are supported).&lt;/p&gt;
&lt;p&gt;When a paid template is selected, the installer collects a license key through a modal dialog with client-side UUID formatting. The key is validated by attempting to query the package from the vendor's repository. If successful, Composer is configured with the bearer token in &lt;code&gt;auth.json&lt;/code&gt; and the repository is added to &lt;code&gt;composer.json&lt;/code&gt;. The installer then proceeds with normal template installation.&lt;/p&gt;
&lt;p&gt;The implementation includes comprehensive test coverage through kernel tests for the &lt;code&gt;SiteTemplate&lt;/code&gt; class, functional tests for the installer flow, and a build test that simulates installing a paid template from a protected repository using a test HTTP server.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 13, 2026&lt;/li&gt;
&lt;li&gt;First commit: March 14, 2026 (1 day later)&lt;/li&gt;
&lt;li&gt;Last commit: March 17, 2026 (3 days and 14 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;mherchel (Dripyard)&lt;/li&gt;
&lt;li&gt;andyg5000 (Dripyard)&lt;/li&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This feature was developed through close collaboration between Acquia (phenaproxima) and Dripyard (mherchel, andyg5000), with design review from pameeela. The work progressed through multiple merge requests over several days, with phenaproxima building the backend infrastructure and validation logic, mherchel implementing UI/UX and styling, andyg5000 adding server-side license validation, and pameeela contributing style refinements. The implementation required updating a core patch to make profile build tests discoverable by PHPUnit.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3579729: Add an `overwrite` option to `drush site:export`</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3579729.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94efa4f6944c</guid>
      <pubDate>Tue, 17 Mar 2026 16:37:23 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/73ee652134af85e6021a435573ec8db1c62b5c97"&gt;73ee652&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3579729"&gt;#3579729&lt;/a&gt; · 1 contributor · 6 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drush site:export&lt;/code&gt; command now supports an &lt;code&gt;--overwrite&lt;/code&gt; option that allows you to re-export a site to an existing directory. Previously, the command would fail if the destination directory already existed, requiring manual deletion before exporting again. This makes it easier to iterate on site exports during development without manually cleaning up directories between runs.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteExportCommand&lt;/code&gt; class in the &lt;code&gt;drupal_cms_helper&lt;/code&gt; module now accepts an &lt;code&gt;--overwrite&lt;/code&gt; option. When this option is provided, the command skips the check that would normally fail if the destination directory exists.&lt;/p&gt;
&lt;p&gt;The underlying &lt;code&gt;SiteExporter::copyBaseRecipe()&lt;/code&gt; method was also updated to exclude &lt;code&gt;config&lt;/code&gt; and &lt;code&gt;recipe.yml&lt;/code&gt; from the base recipe when mirroring files, in addition to the existing &lt;code&gt;content&lt;/code&gt; exclusion. This ensures these key files are always regenerated during export. The &lt;code&gt;delete&lt;/code&gt; option was removed from the &lt;code&gt;Filesystem::mirror()&lt;/code&gt; call, which means overwriting will only update files that are part of the export, not delete everything in the destination first. This preserves any additional files in the destination directory (like &lt;code&gt;.git&lt;/code&gt; directories from version control).&lt;/p&gt;
&lt;p&gt;The test coverage confirms that overwriting replaces exported files like &lt;code&gt;recipe.yml&lt;/code&gt; while leaving untouched files like &lt;code&gt;.git&lt;/code&gt; directories intact.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 17, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 17, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3579616: The site:export command should always use drupal_cms_site_template_base as a base, if available</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3579616.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94ea95b66010</guid>
      <pubDate>Tue, 17 Mar 2026 02:17:21 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/c72081fdf12a56ca0555404b5c928d61c2f03844"&gt;c72081f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3579616"&gt;#3579616&lt;/a&gt; · 1 contributor · 7 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The site export command now defaults to using the Drupal CMS site template starter kit as a base, making it easier to create new site templates. Running &lt;code&gt;drush site:export&lt;/code&gt; with no arguments produces a complete, working recipe with documentation, tests, and a screenshot. The exported recipe is self-contained and does not depend on other recipes, simplifying the site template creation workflow for DrupalCon Chicago and the Drupal CMS marketplace.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;site:export&lt;/code&gt; command previously allowed exporting to any base recipe and attempted to merge changes into existing recipe files. This approach was overcomplicated and error-prone for the primary use case: creating new site templates for the Drupal CMS marketplace.&lt;/p&gt;
&lt;p&gt;The command now automatically uses &lt;code&gt;drupal_cms_site_template_base&lt;/code&gt; (the site template starter kit) as the default base if available. Both the Drush command and the UI export controller check for the starter kit's existence using a new &lt;code&gt;SiteExporter::getRecipePath()&lt;/code&gt; method, which determines where Composer installs recipes by parsing the project's &lt;code&gt;composer.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The export process now completely overwrites &lt;code&gt;recipe.yml&lt;/code&gt; and &lt;code&gt;composer.json&lt;/code&gt; rather than merging changes. The generated &lt;code&gt;recipe.yml&lt;/code&gt; contains only the site name, type, installed extensions, and config actions—no references to other recipes. The &lt;code&gt;composer.json&lt;/code&gt; file is rebuilt from scratch with requirements for all installed extensions, removing any starter kit development dependencies.&lt;/p&gt;
&lt;p&gt;This produces monolithic site templates that are self-contained and ready to publish. The export includes all content, configuration, documentation, tests, and supporting files from the starter kit (README, CI configuration, screenshots), but excludes the starter kit's sample content.&lt;/p&gt;
&lt;p&gt;The installer form no longer stores the selected base template in state, since the export functionality now handles this automatically.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 16, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 17, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3560518: Insecure credential storage used by drupal_cms_ai recipe as default</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3560518.md</link>
      <guid isPermaLink="false">019d857f-33e3-726a-abae-0f39c9cb2755</guid>
      <category>Module maintainers</category>
      <pubDate>Mon, 16 Mar 2026 20:10:59 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal task&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/5df700f60b7752dc5885fdd96ee269402a0ad5ec"&gt;5df700f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3560518"&gt;#3560518&lt;/a&gt; · 6 contributors · 84 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 11&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Drupal CMS AI recipe now uses the Easy Encryption module to protect API keys and other sensitive credentials. Previously, credentials were stored in plain text within Drupal configuration, where they could be exported to version control or database backups. Easy Encryption automatically intercepts key creation and transparently encrypts values at rest using libsodium sealed box encryption. Encryption keys are stored outside the web root when possible, falling back to Drupal state when write access is unavailable. This happens automatically when applying the AI recipe or creating keys through the UI, with no additional steps required from site owners.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;Affects module maintainers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New API:&lt;/strong&gt; New &lt;code&gt;easy_encryption&lt;/code&gt; module dependency provides transparent encryption of credentials stored via the Key module.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drupal_cms_ai&lt;/code&gt; recipe adds a dependency on the Easy Encryption module to secure credentials by default. Easy Encryption provides a Key provider plugin that intercepts key creation and automatically upgrades Configuration (plaintext) providers to Easy Encrypted providers. It uses libsodium's sealed box design (&lt;code&gt;sodium_crypto_box_seal&lt;/code&gt;), which allows encryption with a public key and decryption only with the corresponding private key stored on the server.&lt;/p&gt;
&lt;p&gt;When a new Key entity with the Configuration provider is created, Easy Encryption transparently switches it to use encrypted storage. The module attempts to write encryption keys to a directory outside the web root with restrictive permissions (0700). If that fails, it falls back to storing the encryption key in Drupal's state system, which is not exported with configuration. A warning appears in the status report when this fallback is active.&lt;/p&gt;
&lt;p&gt;The sealed box design supports future workflows where credentials can be encrypted off-server using only the public key, then deployed to production where only the private key can decrypt them. This aligns with patterns used in Symfony's secrets management.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;drupal_cms_ai&lt;/code&gt; recipe was refactored to create a single &lt;code&gt;ai&lt;/code&gt; key entity with an encrypted provider, replacing the previous approach of separate plaintext keys per AI provider. The recipe uses an environment variable default for the API key input, allowing non-interactive application via environment variables.&lt;/p&gt;
&lt;h2&gt;Upgrade&lt;/h2&gt;
&lt;p&gt;No code changes are required for existing Drupal CMS sites. The Easy Encryption module is added as a dependency and works transparently.&lt;/p&gt;
&lt;p&gt;For sites applying the &lt;code&gt;drupal_cms_ai&lt;/code&gt; recipe after this change, API keys will be encrypted automatically. For sites that already applied the recipe before this change, existing keys remain in plaintext configuration. Site owners can manually re-create keys to benefit from encryption, or use Easy Encryption's export/import functionality to transfer keys between environments.&lt;/p&gt;
&lt;p&gt;When moving a site between environments, encryption keys must be transferred separately from configuration exports. Easy Encryption provides an admin UI at &lt;code&gt;/admin/config/system/easy-encryption&lt;/code&gt; to export and import encryption keys. See the Easy Encryption documentation for transfer workflows.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: November 28, 2025&lt;/li&gt;
&lt;li&gt;First commit: December 6, 2025 (7 days later)&lt;/li&gt;
&lt;li&gt;Last commit: March 16, 2026 (3 months and 6 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;mxr576 (Pronovix)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;longwave (Full Fat Things)&lt;/li&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This issue underwent significant evolution over nearly four months. The original proposal called for using the Key module with file-based storage for encryption keys. An early implementation attempted to set up AES encryption with keys stored in a &lt;code&gt;.keychain&lt;/code&gt; directory outside the web root, but was reverted after concerns that storing encryption keys in plaintext on the filesystem provided only security theater.&lt;/p&gt;
&lt;p&gt;Extensive discussion with security team members and cryptography experts led to the conclusion that no zero-configuration solution can provide perfect security. The team requested formal security review, but learned that the Drupal security team reviews reported vulnerabilities rather than proposed architectures. This left the architectural decisions to the Drupal CMS team.&lt;/p&gt;
&lt;p&gt;After exploring options including HSM integration, external key management services, and hosting provider partnerships, the team settled on Easy Encryption, a new module created specifically to address this credential security challenge. The module was developed in a sandbox, then promoted to a full project with extensive documentation and architecture decision records.&lt;/p&gt;
&lt;p&gt;A key debate centered on whether the state storage fallback constituted acceptable security versus security theater. The consensus was that encrypting credentials in configuration and storing the encryption key in state is meaningfully better than plaintext credentials in exportable configuration, even if it does not defend against a fully compromised server. This pragmatic approach acknowledges that Drupal CMS targets marketers and small site owners who would otherwise have no credential protection at all.&lt;/p&gt;
&lt;p&gt;The final implementation was committed to Drupal CMS 2.x and 2.1.x, with documentation follow-up deferred to a separate issue.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3578624: Fix utility page content template config</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3578624.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94dcef8c1b00</guid>
      <pubDate>Wed, 11 Mar 2026 21:59:33 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Major bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/9c81398001ed996b578a65efe3a757b1ee70450c"&gt;9c81398&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3578624"&gt;#3578624&lt;/a&gt; · 3 contributors · 12 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 3&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS 2 using the Mercury starter template displayed an error on utility pages like the Privacy Policy page. The error occurred because the heading component in the page content template was configured with &amp;quot;No URL&amp;quot; as a default value, which the component rejected as an invalid URL format. The fix changes this default to an empty string, eliminating the error while still allowing URLs to be added when needed.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;canvas.content_template.node.page.full.yml&lt;/code&gt; configuration file in the &lt;code&gt;drupal_cms_starter&lt;/code&gt; recipe defined a heading component with a &lt;code&gt;url&lt;/code&gt; input value of &amp;quot;No URL&amp;quot;. The Mercury heading component validates URL inputs and throws a runtime error when it receives &amp;quot;No URL&amp;quot; as a value, treating it as an invalid URL format.&lt;/p&gt;
&lt;p&gt;The fix changes the default &lt;code&gt;url&lt;/code&gt; input from &amp;quot;No URL&amp;quot; to an empty string in the configuration. An empty string is accepted by the component's validation, allowing pages to render without errors. The component still supports the URI format with autocomplete widget functionality when a URL is actually provided.&lt;/p&gt;
&lt;p&gt;This was a configuration issue in the starter recipe rather than a code bug. The initial &amp;quot;No URL&amp;quot; value was likely intended as a placeholder but did not match what the component expected for an empty state.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 11, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 11, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;roaguicr&lt;/li&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3578145: Strip site name &amp; email from the site:export</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3578145.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94d96b2bd38a</guid>
      <pubDate>Tue, 10 Mar 2026 02:22:25 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal task&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/2fc20b44988bf5718f3d44e6a818e3ce2a80bcbc"&gt;2fc20b4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3578145"&gt;#3578145&lt;/a&gt; · 2 contributors · 11 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drush site:export&lt;/code&gt; command in Drupal CMS now excludes the site name and email address from exported site templates. These values are typically collected during installation and vary between sites, so they should not be part of a reusable template. This ensures that exported site templates are cleaner and can be applied to new sites without carrying over site-specific information.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteExporter&lt;/code&gt; class now explicitly unsets the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;mail&lt;/code&gt; keys from the &lt;code&gt;system.site&lt;/code&gt; configuration before writing the recipe YAML file. This prevents site-specific values collected during installation from being included in exported site templates.&lt;/p&gt;
&lt;p&gt;The change also includes a fix for a related bug where user 1 could be statically cached with stale field definitions during command-line installation with a monolithic site template. The &lt;code&gt;RecipeSubscriber::onApply()&lt;/code&gt; method now accepts the &lt;code&gt;RecipeAppliedEvent&lt;/code&gt; parameter and resets the user storage cache for user 1 when applying a Site-type recipe during CLI installation. This prevents errors when the user account is modified by &lt;code&gt;SiteConfigureForm&lt;/code&gt; during installation.&lt;/p&gt;
&lt;p&gt;Test coverage was added to verify that the site name and mail are not present in the exported recipe configuration.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 10, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 10, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3577923: SiteExporter should remove recipe dependencies</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3577923.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94d420b4f5e7</guid>
      <pubDate>Mon, 09 Mar 2026 13:01:54 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal task&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/577686e05525d603ecfba2932f44fbf5174c0e48"&gt;577686e&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3577923"&gt;#3577923&lt;/a&gt; · 1 contributor · 6 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS now exports sites as truly standalone packages. When exporting a configured site as a recipe, the exporter now removes references to any recipes that were originally applied during setup. This eliminates redundant dependencies and makes exported sites cleaner and faster to install. The exported recipe contains all the configuration and content from those original recipes, so keeping them as dependencies was unnecessary and confusing.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteExporter&lt;/code&gt; class creates monolithic recipes that include all configuration from a site. Previously, if a site was built from recipes (like Drupal CMS starter kits), the exporter preserved the &lt;code&gt;recipes&lt;/code&gt; key in &lt;code&gt;recipe.yml&lt;/code&gt; and the corresponding Composer dependencies in &lt;code&gt;composer.json&lt;/code&gt;. This was redundant because the exported recipe already includes everything those base recipes provided.&lt;/p&gt;
&lt;p&gt;The change refactors the export flow. The &lt;code&gt;updateRecipe()&lt;/code&gt; method now returns the list of recipe names extracted from the &lt;code&gt;recipes&lt;/code&gt; key before removing it from &lt;code&gt;recipe.yml&lt;/code&gt;. This list is passed to &lt;code&gt;updateComposerJson()&lt;/code&gt;, which filters out matching packages from the requirements. The matching uses &lt;code&gt;basename()&lt;/code&gt; to compare the recipe name from &lt;code&gt;recipe.yml&lt;/code&gt; against package names.&lt;/p&gt;
&lt;p&gt;The implementation also reorders operations: &lt;code&gt;updateComposerJson()&lt;/code&gt; now runs after &lt;code&gt;updateRecipe()&lt;/code&gt; so it can receive the recipe list. Test coverage was expanded to verify recipes are removed from both files and that starter kit-specific configuration is cleaned up.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 9, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 9, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This issue was opened and resolved by phenaproxima in a single day. The contributor manually tested the changes and confirmed solid test coverage before merging to both the 2.x and 2.0.x branches. The straightforward execution suggests this was a clear improvement identified during Drupal CMS development.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3575283: Add the ability to export a site template as a ZIP file</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3575283.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-949a7bcf6bf5</guid>
      <pubDate>Mon, 09 Mar 2026 00:30:57 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/dfd34d6c83764de4f9f968629d3781b45a40137f"&gt;dfd34d6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3575283"&gt;#3575283&lt;/a&gt; · 4 contributors · 24 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 6&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS now provides an internal route that exports the current site as a downloadable ZIP file containing a recipe. This makes it possible to capture a customized site's configuration and content for reuse. The route is not exposed in the UI by default, allowing individual site templates to decide where and when to make it available. Sites based on the starter kit can optionally include this export capability.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drupal_cms_helper&lt;/code&gt; module adds a new route at &lt;code&gt;/admin/config/development/site-export&lt;/code&gt; that returns a ZIP archive containing a site recipe. The route controller &lt;code&gt;ExportController&lt;/code&gt; uses the existing &lt;code&gt;SiteExporter&lt;/code&gt; service to generate the recipe, which includes configuration, content, and metadata.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;SiteExporter::export()&lt;/code&gt; method now accepts an optional &lt;code&gt;$base&lt;/code&gt; parameter to copy files from a base recipe template. During installation, when a user selects the starter kit template, the installer stores its path in state. The export controller retrieves this path to include template-specific files like README and CI configuration in the exported archive.&lt;/p&gt;
&lt;p&gt;The route requires the &lt;code&gt;administer site configuration&lt;/code&gt; permission but is intentionally not linked anywhere in the UI. Individual site templates can add menu links or other UI elements to expose this functionality where appropriate. The &lt;code&gt;.htaccess&lt;/code&gt; file is removed from the exported config directory since site templates are meant to be shared.&lt;/p&gt;
&lt;p&gt;The implementation went through several iterations, first attempting to add a button to the config export form, then reverting due to UX concerns, and finally settling on an invisible route that templates can expose as needed.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: February 23, 2026&lt;/li&gt;
&lt;li&gt;First commit: February 23, 2026&lt;/li&gt;
&lt;li&gt;Last commit: March 9, 2026 (13 days and 8 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;thejimbirch (Kanopi Studios)&lt;/li&gt;
&lt;li&gt;penyaskito (Acquia)&lt;/li&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;The change took a winding path to its final form. Initially committed with a button in the config management UI, it was quickly reverted after UX review found that location confusing. Discussion then explored creating a separate module, but the maintainer preferred keeping the functionality in &lt;code&gt;drupal_cms_helper&lt;/code&gt; alongside existing dev tooling.&lt;/p&gt;
&lt;p&gt;The breakthrough came when the team decided to create an invisible route that individual site templates could expose through menu links. This approach ensures only sites meant for customization and export would show the feature. Over eight commits and two weeks, the implementation was refined to remove unnecessary access checks and properly integrate with the installer's template selection.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3577570: SiteExporter should not set a version in the exported composer.json</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3577570.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94c929aa2bfe</guid>
      <pubDate>Fri, 06 Mar 2026 15:47:38 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/8c4a8fe8e03bb327da9f7fa50800819ecf7bcd9a"&gt;8c4a8fe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3577570"&gt;#3577570&lt;/a&gt; · 1 contributor · 7 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The site export tool now leaves version numbers out of the exported &lt;code&gt;composer.json&lt;/code&gt; file. Previously, it would set a default version of &lt;code&gt;1.0.0&lt;/code&gt; if none was defined. This caused problems when trying to install development branches of exported recipes using Composer. The packaging pipeline will now handle version numbering instead, allowing dev branches to work correctly.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteExporter&lt;/code&gt; class previously set a default &lt;code&gt;version: 1.0.0&lt;/code&gt; in the exported &lt;code&gt;composer.json&lt;/code&gt; if no version was already present. This was done alongside setting a default package name to ensure the recipe was installable by Composer.&lt;/p&gt;
&lt;p&gt;However, hardcoding a version in the exported recipe made development branches uninstallable. Composer expects dev branches to follow specific version constraints, and a static &lt;code&gt;1.0.0&lt;/code&gt; conflicts with those expectations.&lt;/p&gt;
&lt;p&gt;The fix removes the line that sets a default version (&lt;code&gt;$data['version'] ??= '1.0.0'&lt;/code&gt;), delegating version assignment to the packaging pipeline. The exporter still sets a default package name based on the export directory name, which remains required for Composer compatibility.&lt;/p&gt;
&lt;p&gt;The change also includes updates to &lt;code&gt;DefaultContentSubscriber&lt;/code&gt; to handle taxonomy term references with target ID &lt;code&gt;0&lt;/code&gt; (terms at the vocabulary root), preventing export failures. This is a workaround for a separate issue tracked in the codebase.&lt;/p&gt;
&lt;p&gt;Additionally, when an extension package version cannot be determined, the exporter now falls back to a wildcard constraint (&lt;code&gt;*&lt;/code&gt;) with a warning, rather than skipping the requirement entirely.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 6, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 6, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This issue was opened and resolved on the same day (March 6, 2026) by phenaproxima. The problem was discovered when exported recipes could not be installed from development branches, blocking workflow. The fix was straightforward once identified: simply stop setting a default version and let the packaging infrastructure handle it. The rapid turnaround suggests this was a critical blocker for the Drupal CMS project's recipe export functionality.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3576540: Authenticated users should not have permission to access the dashboard by default, because there's nothing for them to do there</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3576540.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94b6cee52f39</guid>
      <pubDate>Fri, 06 Mar 2026 14:14:26 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/bb5f8747e1c7cb4847f2dbe310b0e303e6d37cce"&gt;bb5f874&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3576540"&gt;#3576540&lt;/a&gt; · 4 contributors · 25 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 4&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS no longer grants dashboard access to regular authenticated users by default. Previously, the admin UI recipe gave all authenticated users permission to view the dashboard, even though they had no administrative tasks to perform there. This created a confusing experience where users were redirected to an empty dashboard. The dashboard permission is now granted only to content editors, who actually have work to perform there. Regular authenticated users will no longer see or be redirected to the dashboard unless a site explicitly grants them that permission.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drupal_cms_admin_ui&lt;/code&gt; recipe previously granted six permissions to the authenticated user role, including &lt;code&gt;view welcome dashboard&lt;/code&gt;. This caused authenticated users to be redirected to a dashboard they couldn't use.&lt;/p&gt;
&lt;p&gt;The fix removes &lt;code&gt;view welcome dashboard&lt;/code&gt; from the authenticated role and instead grants it to the &lt;code&gt;content_editor&lt;/code&gt; role. However, the two recipes that need to coordinate (&lt;code&gt;drupal_cms_admin_ui&lt;/code&gt; and &lt;code&gt;drupal_cms_content_type_base&lt;/code&gt;) cannot depend on each other without creating undesirable coupling between foundational recipes.&lt;/p&gt;
&lt;p&gt;The solution introduces a new &lt;code&gt;grantPermissionsIfExist&lt;/code&gt; config action plugin. This action works like the core &lt;code&gt;grantPermissions&lt;/code&gt; action but silently ignores permissions that don't exist. The &lt;code&gt;drupal_cms_content_type_base&lt;/code&gt; recipe uses this action to grant dashboard permissions to content editors only if those permissions are available (meaning the dashboard recipe has been applied). The recipe YAML uses the &lt;code&gt;?&lt;/code&gt; prefix for optional config targeting.&lt;/p&gt;
&lt;p&gt;A core issue &lt;a href="https://www.drupal.org/i/3577548"&gt;#3577548&lt;/a&gt; was opened to potentially add this action to core. The current implementation is marked internal and acts as a polyfill.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 2, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 6, 2026 (4 days and 2 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;rajab natshah (Vardot)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;penyaskito (Acquia)&lt;/li&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;The issue evolved from a simple permission removal request into a more nuanced solution that preserves recipe independence. Initial discussion focused on whether to remove all the authenticated user permissions or just redirect behavior. The product owner confirmed that regular authenticated users don't need dashboard access by default and questioned why these permissions existed at all.&lt;/p&gt;
&lt;p&gt;The key insight came when the architecture lead explained that granting permissions to authenticated users was an &amp;quot;end-run&amp;quot; around making &lt;code&gt;drupal_cms_content_type_base&lt;/code&gt; depend on &lt;code&gt;drupal_cms_admin_ui&lt;/code&gt;. This allowed the content editor role (which inherits from authenticated) to get foundational permissions without explicit dependencies. The solution needed to remove the confusing dashboard permission while preserving this dependency isolation pattern.&lt;/p&gt;
&lt;p&gt;The final approach introduced the &lt;code&gt;grantPermissionsIfExist&lt;/code&gt; action, allowing lightweight optional integration between recipes. This maintained architectural separation while solving the user experience problem. The issue was resolved within four days from opening to commit.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3577313: The installer should allow site templates to scaffold files</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3577313.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94c0e8e8d015</guid>
      <pubDate>Thu, 05 Mar 2026 16:28:03 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/4b00c23c74783e5ac6c271f1e2488456921d1c85"&gt;4b00c23&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3577313"&gt;#3577313&lt;/a&gt; · 1 contributor · 5 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Drupal CMS installer now allows site templates to provide additional files in the project root directory. Site templates can scaffold files like AGENTS.md to give AI assistants instructions for working with a specific site template. This happens automatically when a site template is applied during installation. The installer adds the template package to Composer's scaffold allow-list so these files can be placed where they need to go.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The installer now executes two Composer commands when requiring a recipe package. First, it adds the package name to the &lt;code&gt;extra.drupal-scaffold.allowed-packages&lt;/code&gt; configuration using &lt;code&gt;composer config&lt;/code&gt; with the &lt;code&gt;--merge&lt;/code&gt; and &lt;code&gt;--json&lt;/code&gt; flags. This allows the package to provide scaffold files. Second, it requires the package as before.&lt;/p&gt;
&lt;p&gt;A new &lt;code&gt;ComposerExecutor&lt;/code&gt; class was extracted to centralize Composer command execution. It handles finding the PHP executable, locating the Composer binary, setting the project root as the working directory, and automatically adding the &lt;code&gt;--no-interaction&lt;/code&gt; flag. The original inline Process execution was refactored to use this helper.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;_drupal_cms_installer_require_recipe()&lt;/code&gt; function now makes two separate calls to &lt;code&gt;ComposerExecutor::execute()&lt;/code&gt;. The scaffold configuration is updated before requiring the package, ensuring any scaffold operations defined in the recipe's &lt;code&gt;composer.json&lt;/code&gt; run during the requirement step.&lt;/p&gt;
&lt;p&gt;This change is specific to the Drupal CMS installer profile and does not affect standard Drupal core installation or general recipe application outside this context.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 5, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 5, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This feature was implemented in a single day by phenaproxima. The issue was opened and committed on March 5, 2026. The implementation was straightforward with minimal discussion required, reflecting clear requirements and a well-understood solution path.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3576882: Use focal point widget on image and SVG forms</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3576882.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94bb5b2e754c</guid>
      <pubDate>Thu, 05 Mar 2026 04:58:54 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/8eaabedc35679909a1168eb6c70703a1197f12d9"&gt;8eaabed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3576882"&gt;#3576882&lt;/a&gt; · 2 contributors · 15 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 4&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS Media forms now consistently display the name field and hide the path field across all media types and form modes. Image media types now use the Focal Point widget on all forms, not just the default form. The source field (like the image upload field) appears on default forms for all media types and on media library forms only for image-based media. This standardizes the editing experience and ensures focal point selection works properly when adding or editing images.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drupal_cms_media&lt;/code&gt; recipe was not properly configuring form displays for media types. Static configuration files existed for media library form modes but were incomplete and inconsistent. The fix removes the static configuration files and instead uses recipe config actions to programmatically configure all media form displays.&lt;/p&gt;
&lt;p&gt;The recipe now uses wildcard patterns (&lt;code&gt;core.entity_form_display.media.*.*&lt;/code&gt;) to ensure the &lt;code&gt;name&lt;/code&gt; field is always visible and the &lt;code&gt;path&lt;/code&gt; field is always hidden on all media forms. A second action targets image media forms specifically (&lt;code&gt;core.entity_form_display.media.image.*&lt;/code&gt;) to configure the &lt;code&gt;field_media_image&lt;/code&gt; field to use the &lt;code&gt;image_focal_point&lt;/code&gt; widget type.&lt;/p&gt;
&lt;p&gt;The test coverage was expanded to verify that every media form display follows these rules: name field visible, path field hidden, source field visible based on media type and form mode, and focal point widget used for image media. The test iterates through all form displays and validates each configuration programmatically rather than relying on static snapshots.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 4, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 5, 2026 (1 day and 2 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;pameeela (Technocrat)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;mortona2k&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This issue was resolved quickly, within one day from report to commit. The reporter identified that the media forms were not using focal point and that the configuration was not persisting, suspecting interference from the core image recipe. The assignee discovered the media configuration was a &amp;quot;mess&amp;quot; and worked with the reporter to establish clear rules for normalizing all media form displays. After initial implementation, the reporter tested and found the media library forms were not being configured. The final solution replaced static config files with programmatic recipe actions and added comprehensive test coverage to prevent regressions.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3577153: SiteExporter repeats indexed arrays, potentially bloating recipe.yml to tens of thousands of lines</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3577153.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94bd2dc02e60</guid>
      <pubDate>Thu, 05 Mar 2026 03:36:45 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Major bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/7edee58ed5086bf412ed875b9417f2f01281fd20"&gt;7edee58&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3577153"&gt;#3577153&lt;/a&gt; · 1 contributor · 6 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Site Exporter in Drupal CMS Helper had a bug that caused recipe files to grow exponentially when exported multiple times. Each time a recipe was exported, user permissions and other indexed array values were duplicated instead of being replaced. This could cause &lt;code&gt;recipe.yml&lt;/code&gt; files to balloon to tens of thousands of lines, making them unmanageable and potentially causing performance issues. The fix ensures that config actions are completely replaced on each export rather than merged, preventing this duplication.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SiteExporter&lt;/code&gt; class was using &lt;code&gt;NestedArray::mergeDeep()&lt;/code&gt; to combine new config actions with existing ones in the recipe file. This method preserves integer keys, which is problematic for indexed arrays like user permissions. When merging &lt;code&gt;['permission_a']&lt;/code&gt; with &lt;code&gt;['permission_b']&lt;/code&gt;, the result was &lt;code&gt;['permission_a', 'permission_b']&lt;/code&gt; instead of just &lt;code&gt;['permission_b']&lt;/code&gt;. Each subsequent export would add another copy of all permissions, causing exponential growth.&lt;/p&gt;
&lt;p&gt;The fix replaces the deep merge with a simple array union operator: &lt;code&gt;$actions + ($recipe['config']['actions'] ?? [])&lt;/code&gt;. This ensures that new config actions completely overwrite existing ones for the same config object or entity, while preserving any config actions that aren't being updated. The test verifies that when a recipe contains one permission and the site has a different permission, the export replaces rather than merges the permissions list.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: March 5, 2026&lt;/li&gt;
&lt;li&gt;Committed: March 5, 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;This issue was opened and fixed by phenaproxima on the same day, March 5, 2026. The fix went through two commits, both on the same day. The rapid turnaround suggests this was discovered during active development or testing of the Drupal CMS Helper module, likely while working with recipe exports. The straightforward nature of the fix and the clean test coverage indicate the root cause was quickly identified and addressed.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3574824: When doing internal development, add cweagans/composer-patches to Package Manager's list of trusted plugins</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3574824.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-94906145b568</guid>
      <pubDate>Sun, 01 Mar 2026 15:22:14 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal bug report&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/ffdc9a3bd3a278b02c2814c15231dd06fe4b693a"&gt;ffdc9a3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3574824"&gt;#3574824&lt;/a&gt; · 2 contributors · 16 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 3&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Drupal CMS developers encountered an error when applying recipes through the Project Browser UI. The system blocked operations because &lt;code&gt;cweagans/composer-patches&lt;/code&gt; was not recognized as a trusted Composer plugin. This only affected developers working on Drupal CMS itself, not production sites. The fix adds internal development configuration that automatically trusts the patches plugin and enables direct-write mode for Package Manager when working in the Drupal CMS development environment.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The Drupal CMS project uses &lt;code&gt;cweagans/composer-patches&lt;/code&gt; as a development dependency. When developers tried to apply recipes through Project Browser, Package Manager rejected the operation because this plugin was not in the trusted list. The initial approach used environment variables and a config override service in the &lt;code&gt;drupal_cms_helper&lt;/code&gt; module to detect DDEV projects by name. The final solution moved this logic to a standalone service class (&lt;code&gt;PackageManagerSettings&lt;/code&gt;) registered through a custom &lt;code&gt;services.yml&lt;/code&gt; file in &lt;code&gt;sites/default&lt;/code&gt;. The Composer scaffold configuration copies this file from &lt;code&gt;scripts/services.yml&lt;/code&gt; during installation. The service implements both &lt;code&gt;ConfigFactoryOverrideInterface&lt;/code&gt; to add the patcher to &lt;code&gt;package_manager.settings&lt;/code&gt; trusted plugins list and &lt;code&gt;EventSubscriberInterface&lt;/code&gt; to enable &lt;code&gt;package_manager_allow_direct_write&lt;/code&gt; on kernel request. This approach isolates development tooling from the shipped module code and only affects the internal development environment.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: February 20, 2026&lt;/li&gt;
&lt;li&gt;First commit: February 20, 2026&lt;/li&gt;
&lt;li&gt;Last commit: March 1, 2026 (8 days and 6 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;thejimbirch (Kanopi Studios)&lt;/li&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;phenaproxima quickly diagnosed the issue and implemented multiple iterations to find the cleanest solution. They moved from an environment-variable-based approach to a scaffold-based service registration pattern that keeps development infrastructure separate from production code. thejimbirch reported the bug with clear reproduction steps and confirmed the fix worked.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>#3574664: The `site:export` command should be able to export on top of another recipe</title>
      <link>https://github.com/dbuytaert/drupal-digests/blob/main/issues/drupal-cms/3574664.md</link>
      <guid isPermaLink="false">019d857f-33e4-728b-bbba-948ce321ec76</guid>
      <pubDate>Fri, 27 Feb 2026 19:33:22 +0000</pubDate>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project:&lt;/strong&gt; Drupal CMS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type:&lt;/strong&gt; Normal feature request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status:&lt;/strong&gt; Fixed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diff:&lt;/strong&gt; &lt;a href="https://git.drupalcode.org/project/drupal_cms/-/commit/867047915339821e056e5c2c47f5f96062c44a36"&gt;8670479&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discussion:&lt;/strong&gt; &lt;a href="https://www.drupal.org/node/3574664"&gt;#3574664&lt;/a&gt; · 1 contributor · 12 comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Followers:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The site export command in Drupal CMS now allows you to build on top of an existing recipe template, preserving important scaffolding like CI configuration, license files, and documentation. This makes it easier to share custom site configurations while keeping best practices intact. The command also fixes several export bugs that affected menu hierarchies and front page paths, ensuring exported recipes work correctly when shared with others.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;No upgrade or configuration changes required.&lt;/p&gt;
&lt;h2&gt;Technical details&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;drush site:export&lt;/code&gt; command gains a &lt;code&gt;--base&lt;/code&gt; option that accepts a path to an existing recipe. When specified, the command uses Symfony Filesystem to mirror the base recipe's files (excluding version control and content) into the destination, then exports the current site configuration on top. Files with a &lt;code&gt;.example&lt;/code&gt; suffix are automatically renamed to their active form.&lt;/p&gt;
&lt;p&gt;Three export bugs are also fixed with temporary shims pending core releases. First, &lt;code&gt;system.site&lt;/code&gt; config now exports front page paths as aliases rather than system paths by using &lt;code&gt;AliasManagerInterface&lt;/code&gt; during the transform. Second, &lt;code&gt;DefaultContentSubscriber&lt;/code&gt; ensures menu link content entities export their parent dependencies by loading parent menu links by UUID and adding them to export metadata. Third, the command prevents overwriting existing destinations by checking for directory existence upfront.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;GenericConfigurationListener&lt;/code&gt; class changes from readonly to mutable to support the conditional front page transformation. Test coverage validates base recipe copying, file renaming, content exclusion, and the menu link dependency fix.&lt;/p&gt;
&lt;h2&gt;Contribution&lt;/h2&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Opened: February 19, 2026&lt;/li&gt;
&lt;li&gt;First commit: February 19, 2026&lt;/li&gt;
&lt;li&gt;Last commit: February 27, 2026 (7 days and 4 commits later)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key contributors&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;phenaproxima (Acquia)&lt;/li&gt;
&lt;li&gt;andyg5000 (Dripyard)&lt;/li&gt;
&lt;li&gt;vishalkhode (Acquia)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Collaboration&lt;/h3&gt;
&lt;p&gt;phenaproxima authored, reviewed, and committed this feature independently over nine days across four commits. The issue originated from feedback by Andy from Dripyard, who reported the export bugs affecting site template creators. phenaproxima acknowledged the urgency of unblocking site template development and merged the changes with confidence after adding comprehensive test coverage.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;This content is AI-generated and may contain errors. See &lt;a href="https://github.com/dbuytaert/drupal-digests/"&gt;Drupal Digests&lt;/a&gt; for more.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>