Developing and extending code

Developers planning to add custom code, configuration, or contributed functionality should start by reading this page.

Content on this page

Configuration: what can and cannot be modified

The Drupal Kit includes a variety of configuration ranging from content type settings to block layout to the default Layout Builder settings.

Developers considering modifying the Drupal Kit should first understand two broad categories configuration: configuration that ITS updates periodically, and default installation configuration which is not changed subsequently.

ITS-managed configuration

This configuration is inserted into the database when a site is first installed, but also receives updates through new Drupal Kit releases. Examples include the Flex HTML text format, Flex Page Editor role, media types, layouts, and block types. Developer-introduced configuration changes could result in reverted settings or even data loss. For this reason, developers should avoid modifying this configuration, or do so using a safer method such as Drupal’s Configuration Override system (see Use Drupal’s Config Override system for modifications.)

Developers can identify ITS-managed configuration by searching for a specific definition within the web/profiles/custom/utexas/modules/custom directory of a site codebase (equivalent in GitHub: https://github.austin.utexas.edu/eis1-wcs/utdk_profile/tree/develop/modules/custom ).

Default configuration

Configuration of this type is also created when a site is first installed, but ITS does not modify this configuration in subsequent releases. Examples include the defaults provided by Drupal core’s “standard” installation, and the initial Google CSE search setting. Generally speaking, it can be safely modified on sites after installation. In exceptional cases, ITS will provide updates to this configuration for existing sites; such changes will always appear in the release notes.

List of ‘Default configuration’ by machine name

Machine name

Initial configuration

addtoany.settings.yml

Sets display for social media sharing and which entity types it is available on.

block.block.breadcrumbs.yml

Places the ‘Breadcrumbs’ block in the ‘Breadcrumbs’ region of Forty Acres.

block.block.footer.yml

Places the ‘Footer menu’ block in the ‘Footer middle’ region of Forty Acres.

block.block.header.yml

Places the ‘Header menu’ block in the ‘Header secondary’ region of Forty Acres.

block.block.main.yml

Places the ‘Main navigation’ block in the ‘Primary menu’ region of Forty Acres.

block.block.main_page_content.yml

Places the ‘Main page content’ block in the ‘Content’ region of Forty Acres.

block.block.messages.yml

Places the ‘Messages’ block in the ‘Highlighted’ region of Forty Acres.

block.block.page_title.yml

Places the ‘Page title’ block in the ‘Content’ region of Forty Acres.

block.block.primary_admin_actions.yml

Places the ‘Primary admin actions’ block in the ‘Highlighted’ region of Forty Acres.

block.block.required_links_block.yml

Places the ‘Required Links’ block in the ‘Footer Right’ region of Forty Acres.

block.block.search_form.yml

Places the ‘Search form’ block in the ‘Header tertiary’ region of Forty Acres.

block.block.site_branding_footer.yml

Places the ‘Site branding (footer)’ block in the ‘Footer left’ region of Forty Acres.

block.block.site_branding_header.yml

Places the ‘Site branding (header)’ block in the ‘Header primary’ region of Forty Acres.

block.block.tabs.yml

Places the ‘Action tabs’ block in the ‘Highlighted’ region of Forty Acres.

block.block.user_account_menu.yml

Places the ‘User account menu’ block in the ‘Secondary menu’ region of Forty Acres.

block_content.type.basic.yml

Defines a ‘Basic’ block type.

core.base_field_override.node.page.promote.yml

Removes the ‘Promoted to front page’ option from node forms.

core.entity_form_display.block_content.basic.default.yml

Defines the edit form for the ‘Basic’ block type.

core.entity_form_display.node.page.default.yml

Defines the edit form for the ‘Basic’ page type.

core.entity_view_display.block_content.basic.default.yml

Defines the default display for the ‘Basic’ block type.

core.entity_view_display.node.page.default.yml

Defines the default display for the ‘Basic’ page type.

core.entity_view_display.node.page.teaser.yml

Defines the teaser display for the ‘Basic’ page type.

editor.editor.basic_html.yml

Defines CKEditor toolbar for the ‘Basic HTML’ text format.

editor.editor.full_html.yml

Defines CKEditor toolbar for the ‘Full HTML’ text format.

entity_clone.cloneable_entities.yml

Defines which entity types are eligible for cloning.

entity_clone.settings.yml

Defines the behavior for how different entity types should be cloned.

features.bundle.utexas.yml

Registers a bundle type with the Features module.

field.field.block_content.basic.body.yml

Defines a ‘body’ field on the ‘Basic’ block type.

field.field.node.page.body.yml

Defines a ‘body’ field on the ‘Basic’ page type.

filter.format.basic_html.yml

Defines text filters for the ‘Basic HTML’ text format.

filter.format.full_html.yml

Defines text filters for the ‘Full HTML’ text format.

filter.format.restricted_html.yml

Defines text filters for the ‘Restricted HTML’ text format.

media_library.settings.yml

Exposes the ‘Advanced UI’ for the media library.

node.type.page.yml

Defines a ‘Page’ node type.

pathauto.pattern.pathauto_node.yml

Sets the automatic URL pattern for nodes to ‘[node:title]’

search.page.google_cse_search.yml

Sets a default Google Programmable Search ID covering *.utexas.edu .

search.settings.yml

Defines configuration options for the Drupal search interface.

system.menu.header.yml

Registers a ‘Header menu’ block.

views.view.moderated_content.yml

Installs an administrative View listing content that has a moderation state defined.

workflows.workflow.standard_workflow.yml

Defines ‘Draft’ ‘Published’ and ‘Archived’ states for content moderation and which node types they can be used on.

xmlsitemap.settings.node.page.yml

Sets the default priority and change frequency for ‘Page’ nodes in the XML sitemap.

xmlsitemap.settings.node.utexas_flex_page.yml

Sets the default priority and change frequency for ‘Flex Page’ nodes in the XML sitemap.

Use Drupal’s Config Override system for modifications

Developers are free to modify the configuration in a Drupal Kit site by adding content types, Views, and contributed modules, as well as overriding the behavior of existing configuration. However, since ITS provides periodic configuration updates to sites, a developer who modifies existing configuration will inherently risk creating a conflict between the modification and a future update.

A scenario that illustrates this risk: the Drupal Kit configures the CKEditor “Styles dropdown” plugin to allow content editors to apply styles to HTML elements rich text areas. Through the Drupal UI, a well-meaning developer could update this configuration to provide additional style options for their content editors. In a subsequent release of the Drupal Kit, ITS could provide additional style options for general use across all Drupal Kit sites. This configuration update could overwrite the developer’s customization.

Based on team experience, the best way to avoid this situation is to use Drupal’s relatively new Configuration Override system. This system allows developers to define configuration overrides in PHP code, loaded when the site bootstraps. These definitions take precedence over configuration stored in the database.

In the scenario above, a developer could use the Configuration Override system to append the additional CKEditor style options to whatever is defined in the database configuration. This way, regardless of how ITS updates the configuration, the developer’s customizations remain.

Below are four functional code examples of configuration overrides developed by ITS:

Install and manage contributed modules/themes

Drupal recommends using Composer to add modules and themes. Developers unfamiliar with this approach can start by reading https://www.drupal.org/docs/develop/using-composer/using-composer-to-install-drupal-and-manage-dependencies#adding-modules

Composer supports different methodologies for managing updates to dependencies. For some dependencies, developers may want to update to the next significant version whenever they run the composer update command. For others, developers may want to set an exact version number and only update the version by explicitly changing the version requirement number. A full discussion of version requirement strategies can be found at https://www.drupal.org/docs/develop/using-composer/using-composer-to-install-drupal-and-manage-dependencies#specify-version

Add a shareable custom module/theme

You may want to create code you can use across multiple sites. For example, you might have an organization-specific Drupal theme that provides common styling for all your Drupal Kit sites.

The recommended way to achieve this is to host the project on https://packagist.org and retrieve it as you would any other Composer package.

To help distinguish this from other types of packages, use the composer/installers type key. Currently, that project provides the following list of types:

drupal-core
drupal-module
drupal-theme
drupal-library
drupal-profile
drupal-database-driver
drupal-drush
drupal-custom-theme
drupal-custom-module
drupal-custom-profile
drupal-drupal-multisite
drupal-console
drupal-console-language
drupal-config

By setting the type to drupal-custom-theme for your custom theme that will be shared across multiple sites, when you retrieve it via Packagist, it will be installed in web/themes/custom, rather than web/themes/contrib.

  1. Create a custom Drupal theme as you normally would

  2. Add a composer.json file using composer init in the theme directory.

  3. During the prompt for Package name (<vendor>/<name>):, use a vendor that represents your organization, and a name that represents the project (the vendor name must not already be in use on https://packagist.org). For example, if your organization is the School of Whimsy, you could use the vendor sow, and the name sow_theme, resulting in sow/sow_theme.

  4. During the prompt for Package Type (e.g. library, project, metapackage, composer-plugin) []:, enter drupal-custom-theme

  5. Host your theme on a publicly available Git repository service such at GitHub, GitLab, or Bitbucket.

  6. Submit your theme as a composer project on https://packagist.org.

Add a site-specific custom module/theme

The sections above explain where the Drupal Kit places contributed modules and themes and where it places publicly-shared custom modules and themes. Developers may also need to add custom code specific to a single site. This type of code should be placed in separate directories named after the site. To summarize:

Type of code

Defined in

Location in build

Contributed code from drupal.org
composer.json
modules/contrib
themes/contrib
themes/contrib
Custom code, shared publicly
composer.json
modules/custom
themes/custom
profiles/custom
Custom code, site-specific
Codebase (version control)
modules/<site-namespace>
themes/<site-namespace>
profiles/<site-namespace>

To illustrate this convention, take a site for the University’s School of Whimsy, which has chosen the site namespace sow. The following directory tree illustrates that the site:

  • is retrieving two contributed modules (restui and views_data_export)

  • is retrieving two non-modified custom modules from Packagist (utexas_qualtrics_filter and utexas_event)

  • has added two custom modules directly to the codebase (sow_reporting and sow_news):

└── web
    └── modules
        ├── contrib
           ├── restui
           └── views_data_export
        ├── custom
           └── utexas_qualtrics_filter
           └── utexas_event
        └── sow
            ├── sow_reporting
            └── sow_news

For developers using Git for version control, this pattern is enforced by directives in the default .gitignore. A site-specific custom module placed in modules/custom, for example, will not display as eligible for version control.

Add third-party libraries

Developers sometimes need to add third-party libraries that are dependencies of a Drupal module. For example, the Colorbox module requires the jQuery plugin Colorbox. Drupal modules are usually designed around the expectation that such libraries are located in the web/libraries/ directory. Module documentation usually provides generic instructions about downloading required third-party libraries and placing them there.

However, to work properly with the Drupal Kit, developers need to use a specific method for placing these libraries in the correct location. The Drupal Kit’s default setup tells Git version control not to commit third-party code: the web/libraries directory is explicitly excluded from version control through .gitignore directives. Do not change this. Changing the .gitignore directives could causes problems with the Pantheon Integrated Composer build process. Instead, third-party libraries should be added as Composer requirements, which will be retrieved when the codebase is built.

The Composer code repository Asset Packagist hosts most third-party libraries used with Drupal, and the Drupal Kit includes Asset Packagist as an endpoint in sites’ default composer.json file. The following steps show how to add a third-party library from Asset Packagist to the codebase:

  1. Confirm that the library is available using the search on https://asset-packagist.org . If the library is not available on Asset Packagist, it can be added as a package repository. For guidance, email drupal-kit-support@utlists.utexas.edu .

  2. Run composer require <vendor>/<name> (e.g., composer require bower-asset/colorbox)

  3. Verify the library is placed in the codebase in web/libraries/<name>.

  4. Commit the changes to the composer.json and composer.lock files only.

Remove all Pantheon components from codebase

The Drupal Kit template codebase includes elements specific to use with Pantheon. Developers hosting their site elsewhere may remove those components. Directly after creating your initial project with composer create-project utexas/utdk-project, run the following commands:

  1. Remove scaffolded Pantheon files:

rm pantheon.upstream.yml
rm web/sites/default/default.services.pantheon.preproduction.yml
rm web/sites/default/settings.pantheon.php
  1. Remove the Composer package that adds those scaffolding files:

(cd upstream-configuration && composer remove pantheon-systems/drupal-integrations --no-update)
  1. Remove Composer directives related to that scaffolding:

composer config --json extra.drupal-scaffold.allowed-packages '["utexas/utdk_profile"]'
composer config --json extra.drupal-scaffold.file-mapping '{"[project-root]/.editorconfig": false, "[project-root]/.gitattributes": false}'
composer config --json extra.installer-paths '{"web/core": ["type:drupal-core"], "web/libraries/{$name}": ["type:drupal-library"], "web/modules/contrib/{$name}": ["type:drupal-module"], "web/profiles/contrib/{$name}": ["type:drupal-profile"], "web/themes/contrib/{$name}": ["type:drupal-theme"], "drush/Commands/contrib/{$name}": ["type:drupal-drush"], ".docksal": ["type:utexas-development"]}'
composer update

Secure sensitive credentials

Drupal provides a robust system for codifying and deploying configuration. For developers using this system, extra care should be taken to not expose sensitive credentials such as API keys or external integration passwords. Some common modules that store these types of credentials include:

The Drupal configuration management ecosystem provides various methods to secure these credentials, such as Config Ignore, Config Exclude and Credential Mask. In addition, the following practices help keep sensitive credentials secure:

  • If using Git for version control, add a .gitignore directive so that a given configuration file (e.g., /config/sync/smtp.settings.yml) cannot be added to version control, regardless of how configuration management is being used.

  • Store sensitive credentials outside the database. The design in this Pantheon support page can be used on Pantheon and most other platforms.