Digital Team
About UsEdit in Gitbook
  • Welcome
  • Getting started
    • Life on the Digital team
      • Meetings
      • Communication
      • Software engineering working agreement
    • Contributing to Boston.gov
    • Using GitBook
  • Standards & best practices
    • Digital Team Release Notes
    • Working with Partners
    • Accessibility at COB
      • Developers
      • Content Editors
        • How to guide
      • Resources
      • Working with Iterators
    • Analytics and Metrics
    • Code of Conduct
    • General
    • Code reviews
    • Project Management
    • Git / GitHub
      • Contacts at Github
      • Git Command Tips
      • GitHub Service Accounts
    • Code quality
      • Automated tests & static analysis
      • Code comments
      • Style guides
        • Drupal/PHP
          • D8 Dependency Injection (DI)
        • React/TypeScript
    • Technical documentation
    • Hosting and monitoring
    • Deployment
  • Guides
    • Technology stack and technologies used
      • Web applications
    • Drupal - boston.gov
      • Custom Development & Configuration
        • On Demand Instances
          • Acquia Environment setup checklist
        • Continuous Deployment Process
        • Developer Onboarding
          • Step 1: Local Dev Environments
          • Step 2: Version control
          • Step 3: Introduction to Drupal
          • (to be sorted)
            • Development environment
              • PHP CodeSniffer
              • VSCode IDE Setup
              • AWS for Developers
              • Using Windows
            • Installation instructions
              • Typical build output
              • Lando 101
              • Verify Installation
                • Local Patterns installation
              • Windows install
              • PhpStorm settings configurations
          • Step 4: Site Building in Drupal 8
        • Site Development Notes
          • Git Best Practices - Drupal
          • Drupal Cache
          • Drupal Config
          • Custom Modules
            • Custom Themes
              • Front-end Theme (bos_theme)
                • Site Breadcrumbs
              • Back-end Theme (bos_admin)
            • Adding Templates to Custom Modules
            • Custom Content Types
              • D7 -> D8 Conversion
              • Content Editor UX
                • Content Moderation
              • In-page Navigation Menu
            • Custom Paragraphs
              • D7 -> D8 Conversion
            • Custom Taxonomies
            • WebApps
          • Drupal UX-specific
            • Image Styles & UX
            • Example Content Pages
          • PHPStorm IDE
        • CKEditor
      • Drupal Apps/Content Types
        • Budget Website
        • Building Housing
          • BH Drupal Entities
          • BH Map Webpage
          • BH Property Webpage
            • BH Project Timeline
          • BH Salesforce Sync
            • Salesforce Contributed Module
        • Contact Form
        • Election results
        • Google reCAPTCHA
        • My Neighborhood Lookup
        • Metrolist
        • Metrolist (Drupal)
        • Project Tracker
          • Content Types (& Paragraphs)
          • Taxonomies
          • Views
          • Developer Notes
      • Drupal Features & Components
        • Single Sign On (SSO)
          • Drupal SAML Knowledgebase
          • SamlAuth
        • Maps on boston.gov
        • Charts on boston.gov
          • Quick Overview
          • Chart Data
          • Chart Configuration
          • Advanced Concepts and Techniques
          • Charts on boston.gov (legacy)
          • Useful Resources
      • Drupal micro-services (API end-points)
        • Integrating with Boston.gov
        • Assessing Forms Endpoint
        • Bos311 API
        • Cityscore
          • Knowledge Base
        • PDF Manager Module
        • PostMark Email Services
          • Postmark Knowledgebase
        • Upaknee Email List Services
        • Public Notices
        • Site Alerts
          • CodeRed Subscription
      • Drupal - Weekly Maintenance
      • Drupal - Periodic Maintenance
    • Digital Webapps
      • Libraries and Tools
        • Emotion
        • Storybook
        • Rollbar
      • Services
        • AWS-hosted Microservices
          • SQL Proxy API (DBConnector)
            • Developer Notes
          • PDFToolkit API (DB Connector)
            • Developer Notes
      • Webapps - Maintenance
      • Webapps
        • Boston Family Days
        • Property Tax Calculator
        • Access-Boston
          • Updating IAMDIR/Group Management/LDAP certificates
          • Node Server
          • Portal App Tile Configurations
          • Ownership of Concerns
          • Updating SAML Certificates
          • Applications/Services
            • Group Management
            • Confirm ID/ID Verification
            • Preferred Name
        • Sanitation Scheduling
        • Registry-Certs
          • Marriage Intention
      • DevOps
        • New service setup
          • Non-Monorepo Service Setup
        • Service Configuration
          • Editing a project’s configuration using Cyberduck
        • Managing AWS
          • Production Overview
          • AWS Bastion Access
          • Terraform
            • Updating the ECS cluster AMI
          • Restarting an ECS service
          • Encrypting service configuration for S3
          • Mounting AWS SFTP as a Drive (Mac)
        • Webapp Deployment
          • Deploy to AWS 2021
            • Deploy Tool (cob_ecrDeploy)
    • Fleet - Pattern Library
      • Patterns Library Architecture
      • Icon Library Architecture
      • Developers
        • Local Development for Drupal Developers
      • Patterns Library Maintenance
    • Legacy Website - cityofboston.gov
      • Animal Control
        • Dog Licenses
      • No Tow
        • Street Sweeping Reminders
        • Street Occupancy Alerts
        • Towing Alerts
        • Towing Search
        • Subscription Search
        • Proposed Restructure
          • Backend
        • Reillys Notes
      • Workers' Compensation Inquiry Form
      • Streetbook
      • Legacy Website - Maintenance
        • Animal Control Maintenance
        • Assessing Online (AOL) Maintenance
          • Knowledge-base
          • Disclosure Period
          • Annual PDF Initialization
          • Database Tables
        • No-Tow Maintenance
    • AgilePoint
      • AgilePoint: Adding Users
      • Migrating AGP Applications from one platform to another
    • The Hub - hub.boston.gov
      • The Hub - Maintenance
    • Maintenance
      • Updating SSL Certificates
    • Redirects
      • Redirecting from cityofboston.gov
      • URL redirects versus URL aliases - Drupal
      • DNS Redirects
    • Decommissioned Apps or Services
      • Archived Forms Information
      • CodeRed
      • Drupal 7
        • Deployment (2019)
          • Why do we peer-review pull-requests ?
      • Rentsmart
      • SnowStats
      • Ruby
    • Weglot translation
      • What to do in Weglot
      • What to do on the website or page
        • Softr
        • Drupal Powered Pages
        • Custom Pages
  • Projects
    • Project: Patterns Library Cleanup
      • Project: Refactoring Legacy CSS
        • Strategy
        • Regression Testing
        • Maintenance
    • Project: Upaknee
    • Project: Everbridge API + UI
    • Project: 311 CRM Upgrade
      • Project: City Worker Upgrade to City Worker 5
      • Project: Lagan 311 CRM upgrade to 15r4
    • Project: Fleet (Pattern Library Design System)
    • Project: Monorepo Decoupling
    • Inactive projects
      • Project: 311 (Salesforce Upgrade)
      • Project: Access Boston
        • General/Historical Documentation
          • Edit Config and Upload Icons
        • Processes
          • Process: Adding New Icon to Access Boston Website
          • Process: Non-icon Access Boston Feature or Bug Requests
          • Self-Service
      • Project: Alexa Skill
      • Project: Assessing Online
        • 2022 Notes
      • Project: Boards and commissions
      • Project: City Hall Appointment Scheduler
      • Project: CityScore
      • Project: Mobile Apps
      • Project: Permit Finder
      • Project: Public Notice Signage
      • Project: Registry Suite
        • Birth certificates
        • Marriage Certificates
        • Marriage Intention
        • Death Certificates
      • Project: Work With U.S. Digital Response Team
      • Project: TDM Points App
      • Project: Translation on boston.gov
  • External resources
    • Learning resources
    • Reference links
    • Applications and extensions
Powered by GitBook
On this page

Was this helpful?

Export as PDF
  1. Guides
  2. Drupal - boston.gov
  3. Custom Development & Configuration
  4. Site Development Notes
  5. Custom Modules

Adding Templates to Custom Modules

When you make an html.twig file and add it to the templates folder of a custom theme you are pretty much done (after refreshing caches!). The Drupal theme rendering processes detect the template and uses it in preference to any template of the same name from a parent or default theme. You don't really have to do anything more than add the file and refresh cache.

But, if you add a template to a custom module -even if your intent is just to override a theme default template (e.g.field.html.twig) or to provide a suggested template, there are a few extra things you must do.

Using the example of a custom content type (node) called "node_landing_page", the steps below fully implement a template to be used to render the nodes full display.

Note: Drupal automatically generates the suggestion fornode__landing_page__full which can be used for rendering the "default" (i.e. "full") display.

You can generate other suggestions using the hook_theme_suggestions_hook hook.

  1. Create the twig template you wish to use, and give it a name that matches an existing Drupal theme suggestion with ".html.twig" as the extension. In rare cases you may want to create a new template suggestion. Do this by returning an array of suggestions from ahook_theme_suggestions_hook()in your custom module (see last example below). Convention is to name the template using an "entity breadcrumb" style, with "--"'s between entities and no spaces.

  2. Save the template file in a folder called templates in your custom modules root folder. In our example docroot/modules/custom/node_landing_page/templates . - You could organize files by creating a sub-folder tree - but if you do, you will then have to specify the path to your template in the hook_theme - see step 3 below.

  3. In the hook_theme of your module you must define your new template. This hook is read by the Drupal core theme engine and loaded into a template cache (aka register). Whenever a change is made to this hook you need to clear all caches to load your changes into the cache. In hook_theme return an assoc array with key-value pair nested arrays for each template you wish to define. - The outer keys (template-keys) should be one for each of the templates you are defining. Keep it simple and traceable by setting the the template-key name to be the template filename without the ".html.twig". Important: Replace all "-"'s with "_"'s in the template-key string. (in our example the template-key is node__landing_page_full) - The value for the key (template-key) is an array with a required base_hook and several other optional fields. The base_hook should define the entity type this template is used to render (in our case node but other common entities we theme are field, region, block, paragraph, taxonomy_term ) . [optional] The render element defaults to elements if not specified. [optional] If you wish to use a template file which is not the same name as the suggestion (with "_"'s replaced with "-"'s) then you must specify its name in the template field. Omit the "html.twig" extension. This could be useful if you want 2 display to share the same template. [optional] If you want to use a custom path to the template file (i.e. not the default templates folder) then use the path field. (see bos_link_collections_theme in boston.gov for example) (see "Our Example hook_theme" below for the complete hook)

  4. [optional] Once the cache is cleared you can then catch pre-process events using hook_preprocess_hook in our example this would be node_landing_page_preprocess_node (to catch all node pre-process events) or node_landing_page_preprocess_node__landing_page__full (to catch only this new template pre-process events) - notice that the hook uses the template-key defined in the hook_theme array.

  5. [optional] You can also catch template_preprocess_hook events (in our example this is template_preprocess_node__landing_page__full). This hook is commonly used to create a content variable which contains all the rendered (or renderable) elements of the elements (or whatever the field is named in the templates render element) array.

Our Example template file:

templates/node--landing-page--full.html.twig
{#
/**
 * @file
 *
 * @see template_preprocess_landing_page()
 */
#}
<article{{ attributes }}>
  {% if (title_prefix or title_suffix or display_submitted or unpublished or preview or (not page)) and title %}
    <header>

      {{ title_prefix  }}

      {{ title_suffix  }}

      {% if unpublished %}
        <mark class="watermark">Unpublished</mark>
      {% elseif preview %}
        <mark class="watermark">Preview</mark>
      {% endif %}

    </header>
  {% endif %}

  {{ content }}

</article>

Our Example hook_theme:

node_landing_page.module
/**
 * Implements hook_theme().
 */
 function node_landing_page_theme() {
  return [
    'node__landing_page__full' => [
      'base_hook' => 'node',
      'render element' => 'elements',
      'template' => 'node--landing-page--full',
      'path' => drupal_get_path('module', 'node_landing_page') . '/templates/node/'
    ],
  ];
}

Our Example hook_preprocess_hook (version 1):

node_landing_page.module
/**
 * Implements hook_preprocess_HOOK().
 */
function node_landing_page_preprocess_node(&$variables) {
  if (isset($variables["node"]) && is_object($variables["node"]) && $variables["node"]->getType() == "landing_page") {
    $variables["attributes"]->addClass("full-display");
    $variables["unpublished"] = 0;
  }
}

Our Example hook_preprocess_hook (version 2):

node_landing_page.module
/**
 * Implements hook_preprocess_HOOK().
 */
function node_landing_page_preprocess_node__landing_page__full(&$variables) {
  $variables["attributes"]->addClass("full-display");
  $variables["unpublished"] = 0;
}

Our Example template_preprocess_hook:

node_landing_page.module
function template_preprocess_node__landing_page__full(array &$variables) {
  foreach (Element::children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
}

Our Example hook_theme_suggestions_hook:

node_landing_page.module
/**
 * Implements hook_theme_suggestions_HOOK().
 *
 * This is used to create a cut-down node when site_alert is called from
 * the page display of a view.
 */
function node_landing_page_theme_suggestions_node(array $variables) {
  if ($variables["elements"]["#region"] == "site_alert") {
    return ["node__no_wrapper"];
  }
}
PreviousBack-end Theme (bos_admin)NextCustom Content Types

Last updated 6 years ago

Was this helpful?