when-not-a-module-for-that/2025-midcamp-when-not-a-module-make-and-maintain.md

32 KiB

title date draft mermaid
When There's Not a Module for That— How to Make (and Maintain) a New Module presentation for MidCamp 2025 2025-05-20 false
themeVariables
lineColor
#fff

When There's Not a Module for That— How to Make (and Maintain) a New Module

MidCamp @ DePaul University, Chicago, IL
2025 May 20, Tuesday, 11am, Room 324
#MidCamp2025

benjamin "mlncn" melançon
ben@agaric.coop

Note: They say to get a good night's sleep instead of cramming before an exam, even if you haven't studied everything you want to. I chose the wrong option. Even though this presentation was already complete and presented before, and i'd made tweaks i'd wanted to last week.

But you talk to people and you learn new things and you want to apply and share those things and that leads to talking to more people (gestures at audience) and learning more new things… don't talk to people.


Agaric Technology Collective

The Agaric logo, a blue outline mushroom.

a web development cooperative ∞ agaric.coop

presents

Note: my name's ben, i have been a worker and owner at the worker-owned Agaric cooperative since 2006.


When There's Not

a Module for That

How to Make (and Maintain)

a New Drupal Module


Presented by

Benjamin "mlncn" Melançon

 
with behind-the-scenes help from

Mauricio "dinarcon" Dinarte

Keegan "MegaKeegMan" Rankin

and other Agarics, past and present


Send complaints to ben@agaric.coop

and heckling posts to social.coop/@mlncn


Frankenstein and monster, part of a still from 1931 guillermo del toro adaptation

Note: So what do you do when there's not a module for that? You make your own. It's easy.


Regular node add form with Save button

Note: Say you're creating a site where people can relive great literature that may have been inspired by a famine. You've already created an excellent content type under Structure » Content. But somehow the usual "Save" doesn't communicate what you want when creating Frankenstein content.


frankenstein.info.yml

name: Frankenstein
description: "Rename save button for Frankenstein's content."
type: module
core_version_requirement: ^8 || ^9 || ^10 || ^11 || ^12

Note: This file tells Drupal about your module, and it is technically all you need to enable your module.


frankenstein.module

<?php
/**
 * Implements hook_form_FORM_NAME_alter() for "Frankenstein" node (add) form.
 */
function frankenstein_form_node_frankenstein_form_alter(
  &$form,
  \Drupal\Core\Form\FormStateInterface $form_state
) {
  $form['actions']['submit']['#value'] = t('Re-animate!');
}

Note: There we go, there's a module! I think we're done here. Anyone have any ideas for the next 40 minutes?

.info.yml files are not PHP. They cleverly indicate this by not starting with <?php. .module files are PHP...


Where to put these files

Folder hierarchy of custom module in Drupal

Note: Our files are in place in our local site, now we go to our create 'Frankenstein' content form and…


Regular node add form with Save button


Enable your module

At Extend (/admin/modules) or with:

drush -y en frankenstein

Note: That slide may be the most important you're going to be shown. Enable your module.


Regular node add form with Save button as reanimate

Note: Tadaaa! Now you know how to make a module!


Adam Scott staring at the camera

Note: So you're all sitting* there thinking ... sure, that's easy if you know the exact words and symbols to put in that file. And you're absolutely right. And we're going to tell you how you can figure out all of that.


A motor made of legos labeled Online Commerce Engine, a guitar made of legos labeled Music Media Gallery, and a pile of lego pieces labeled Module You Need

Note: It has often been said, Drupal is a box of legos that you can build just about anything you want with. But sometimes those legos have been put together for you. And sometimes not.


A few basic approaches give a lot of power.

Screwdriver with extensive collection of driver tips.

Note: A few specific examples will give you a lot of tools. But first, two secrets. One's good news, second is bad news.


Good News

Now you know where to paste

How do I exit the Vim editor? viewed 2.1 million times

Note: And knowing where to paste unleashes the power of Stack Overflow / Stack Exchange sites. (yes, the same family of sites that has helped a couple million developers figure out how to quit vim.) Knowing where to put this code unleashes the power of Stack Overflow. You now know enough to be dangerous. And i hope i have definitively slayed (slewn?) the mythology that module-making is a mystic domain of Drupal druids.


Bad News

That simple form alter has hidden gotchas

 

  • As written, it only applies to the create (node/add) form— not the edit form.
  • There's a dozen variations of the humble form alter hook, and all are valid.

Note: With our dirty secrets acknowledged, on with the show. We'll show the edit form hook later on!


The Show

  • Prelude: How To Make a Module
  • Act One: How Not To Make a Module
  • Act Two: How Not To Have to Make a Whole Module
  • Act Three: How To Figure Out How To Make a Module
  • Encore: How To Contribute Your Module

Act One

How Not To Make a Module

Muppet Show curtain


Hierarchy of Code Usage

Stop at the first layer configurable to your needs.

  1. Drupal core code
  2. Broadly supported contributed module code
  3. Less-supported or patched contributed module code
  4. Code you have written and contributed
  5. Custom code you have written and hidden from the world

Note: Why not to make a module.


When not to make a module?

Turtle with golf ball on spring contraption on back.

Note: You can do anything with a module! Why wouldn't you make one every day?


…when there's an easier way

Ginger Howard completing her swing after hitting a golf ball

Note: find a contrib module that does it. Should it be in a template? If it's very specific to the display on your site, you should probably do it in the theme layer. But if it's at all data and you might ever want to switch themes, or want to allow site builders or site managers to disable or change settings, get that in a module.


Alternatives to a module:

  • Template override
  • String override
  • ECA (Event Condition Action, or now Rules is also stable, but i like ECA)

Note: There are a couple of modules for string overrides, or they can go in settings.php. ECA is not necessarily faster than making a module, especially once you know how to make a module, but it exposes the logic to non-developers better.


When building a Drupal site, “there’s a module for that” can be the sweetest words you can hear.

Note: So let's start there.


Is there already a module for that?

drupal.org/project/project_module

Modules link


Modules search

Note: I definitely recommend changing core compatibility to something reasonable, Drupal 10, to start. And Status to "All projects", not just Full, non-sandbox projects, if you're ready to write a module. A sandbox may be a great start. And then allow any version. Then put in your key words and search. Remove the version— perhaps your best bet is porting an existing module. Use lots of different keywords.


  • Do the same searches on an Internet-wide search with “Drupal” and “module” included as keywords.
  • Ask in IRC: #drupal-support, #drupal, and #drupal-contribute (in that order and over some time, even a few days)
  • Er, i meant the Slack equivalents #support, #general, #contribute, see https://www.drupal.org/slack

Take note of every word that you use when searching for a module.

Note: This is how people in the same situation will find your module. Currently we are pouring our notes into Logseq, and they'll be public soon.


There's a module that almost does exactly what you want
  • Contribute a patch!
  • Port it to modern Drupal 10/11!

Note: Even if your patch is not accepted nor is there any way given to build on the module, you can fork the module— maintain your own copy. Indeed, this is the standard way of contributing now, with an Issue Fork.


David submitting issue with patch](https://www.drupal.org/project/comment_notify/issues/2850935)


David fixing a reported comment notify issue all himself.](https://www.drupal.org/project/comment_notify/issues/2976478)

Note: This strategy is not without risks. David was made a maintainer of Comment Notify, and now routinely fixes issues there all himself. Still, that's generally better for everyone than to make your own module with duplicate functionality.


Act Two

How Not To Have to Make a Whole Module

Muppet Show curtain


Be a part of something bigger

(but not as big as all of Drupal)

Commerce

ECA - Event Condition Action

Extra Field

Search API

Note: Build on or with some major —and with luck join a community of people invested in a part of Drupal and willing to share passion and knowledge.


Become a co-maintainer

Guide to Co-maintaining Modules - drupal.org/node/1705758


Look for opportunities to plug in

No, literally, look for plugins.

In src/Plugin of whatever modules you are operating in the vicinity of.

Note: Doing your own module by extending an existing plugin or copying and modifying an existing plugin is probably the highest impact to effort ratio.


drupal.org/project/bef_links_filter

Search API's friends include Facets and Better Exposed Filters

BEF Links Filter is a better_exposed_filters plugin in about 250 lines of code altogether.

Note: By extending BEF Links module, we were able to get a lot of power— with a much better user experience, thanks to our ~250 lines of code altogether.


Screenshot of worldpece.org/analyize showing "Annotation for Transparent Inquiry (ATI)" and another result alongside a right-hand column with an opened fieldset for "Question set" with a search box labeled "Filter" and a ten options starting with "Reading Qual Data Repository Platforms" and "Decolonizing the University" and ending in a muted "Show all" button.

Screenshot of worldpece.org/analyize showing an opened fieldset for "Question set" with a search box filled in with "tech" and two options, "Analyzing Representations of 'Local Knowledge' in Tech Development" and "Archive Ethnography: Technics".

Note: It is the small text on the right that is important in the first screenshot; in the second it is zoomed in and filtered by the word "tech".


bef_links_filter/src/Plugin/better_exposed_filters/filter/LinksFilter.php

<?php

namespace Drupal\bef_links_filter\Plugin\better_exposed_filters\filter;

use Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\Links;

Off the edge there our namespace ends in filter and we are using BEF's filter\Links class.


Which means we are outright extending the filter 'Links' from Better Exposed Filter itself:

/**
 * Filterable list of facet links.
 *
 * @BetterExposedFiltersFilterWidget(
 *   id = "bef_links_filter",
 *   label = @Translation("Filterable links"),
 * )
 */
class LinksFilter extends Links {

Note: Our module is directly inheriting everything that Better Exposed Filter's Links filter does. (That we are adding JavaScript filtering to that filter is an unfortunate confusion.)


Which means we get all the actual facet links for free, and we can add on options.

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration(): array {
    return parent::defaultConfiguration() + [
      'minimum_to_show_text_filter' => 5,
      'initial_shown' => 10,
      'allow_show_all' => TRUE,
    ];
  }

Note: The main thing to note there is parent:: which you will use when extending things often.


Act Three

How To Figure Out How To Make a Module

Muppet Show curtain


When to make a module

Lego heart with a missing piece

Note: You've identified to the best of your ability that there's definitely not something out there that does what you want to do. I personally do not advocate introspection at this point: Why am i trying to do something that no one else is trying to do? Credit: lego heart is credited to Llama-Muffin-Kelly on DeviantArt but only see it on Pinterest


Steal, and keep stealing


A large button with the label "+ Add content block"

Note: If you decide your module should have one of those button links you see at the top right of a bunch of pages in Drupal's administration, you don't even need to know that they are called "Action Links" and find a tutorial on making them. You can just go look in the code of a module you know has them, grep for the label, and steal the code around it.


grep -nHIRs "Add content block" web/core/ with a half-dozen results, including web/core/modules/block_content/block_content.links.action.yml:9 and a bunch of results in tests.

Note: Grep. Even when i am fully using an IDE for everything else, i grep in the regular command line when looking for text in a file. This habit is probably fueled more by failing to tell the IDE to index Drupal core and contrib modules more than anything else.


"block_content_add_action:
route_name: block_content.add_page
title: 'Add content block'
appears_on:
- block.admin_library
- entity.block_content.collection
class: \Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction"


Building blocks

  • Hooks
    • Form alter
    • Node insert
  • Plugins
    • Block
    • Formatter
  • Services
    • Route subscriber
    • Event subscriber

Note: The three main ways to mess with modern: hooks, plugins, and services, with a couple examples in each. Both examples of what technically to do and how to do general things. For example: Doing something with a form? You want hook_form_alter() or one of its variants.


How to figure out how to make a module (recap)

Learning to learn

  • Drupal documentation
  • Drupal contributed modules
  • Drupal core and its modules
  • Debugger
  • Drupal forums and issues
  • Drupal.StackExchange.com
  • The rest of the Internet
  • Code search / grep

Note: Where to start if i don't cover it in this session. And the debugger to more quickly find methods and functions, or even step through code, in contributed modules, core, and the module you're working on!


Building a Contrib-Worthy Modern Drupal (10, 11, 12…) Module

What module to make?

  • One you need.
  • One not already made!

Note: To summarize: ^^


Write up what you plan to do

groups.drupal.org/contributed-module-ideas

Note: Is one place, but your own blog or


File issues for your own modules.

Note: It helps you think through what you are planning to do, it makes features and bugs more discoverable by other people, if you do not get to it right away there is a chance someone else will (in a way that they coordinate with you!), and you (and anyone else who helps) get Drupal.org issue credit for that (you get no credit for contributing the actual module).


Cat waving paws at mirror with the text Magic Portal: Activate! superimposed.

Note: Hooks are magic portals that let any module appear in another part of Drupal and do something. More technically, a hook is tied to some sort of Drupal event and is an opportunity for our module to take action. There were 251+ hooks in Drupal 7 core. After a concerted effort in the development of Drupal 8 to remove hooks and replace them with more modern and widespread (outside Drupal) programming practices such as plugins and services... There are now 290 hooks in Drupal 11, up two from 288 hooks in Drupal 8.8. To be fair, we moved some big contributed modules like Views into Drupal core ... but even after that, we are still adding hooks sligtly faster than deprecating and removing them. Hooks are still very much a part of Drupal. Every contributed module can provide hooks, too.


Hooks work by naming convention.

To implement a hook, take the 'hook' part off the hook name and replace it with your module's (machine) name.

Or do it the new way!


Resources

Most of these links go to a page on Drupal's API reference which is also a great place to go for an overview of what tools are available to you as you pick through the pieces Drupal offers and assemble your module.

api.drupal.org/api/drupal/core!core.api.php/group/extending


Key hooks

All the hooks

Note: A note on all these links: I've linked to the Drupal 11.0 version. These hooks almost all work in Drupal 10.x also. Drupal.org inadvertently for quite a while treated 8.2 as the end-all-and-be-all version of Drupal; this has been mitigated and is being fixed but Google is still likely to take you to 8.2 when searching for documentation on a hook. Just switch to the version you're using, or the latest version of Drupal if you are open to upgrading.

That 'all the hooks' link also shows how to implement hooks in the modern method.


Bonus tip

Don't bother to support older versions

In your *.info.yml

core_version_requirement: ^10.2 || ^11

Note: And i'm not talking about Drupal 7, 6, 5, or 4.7. Modern Drupal starts with Drupal 8, but don't bother with Drupal 10 if you want to use the new cool object-oriented style hooks that came in Drupal 11.1. Some good stuff came in Drupal 10.2, so this exact line is common right now, and if you are making a new module you would not be excluding anybody really to start with this.


Read Drupal Core Release Notes

drupal.org/project/drupal/releases

Screenshot of several releases of Drupal 11

Note: Bonus bonus tip. "This is a a feature minor release of Drupal 11" are the sorts of words you are looking for, the "New features" tag. You can also read release notes in alpha, beta, and release candidate versions to see what's coming; dev releases are rolling and do not have release notes. The insecure tag is simply because there have been security patches since then but has no bearing on the information in the release notes generally.


The Rest of the Internet

Note: Get your notes on the Internet so you can find them again! This happens to me constantly.


Copy

or have technology copy for you

drupal.org/project/module_builder

Note: Shows how a lot of this is boilerplate. Drupal Console used to be the go-to for this; https://www.drupal.org/project/ckeditor_youtube/ is simple and used by many, it was a pure Drupal Console creation to start and has slowly gotten more complex. I hear there's some other tools out there that will burn venture capitalist cash to write code for you, unfortunately accellerating the boiling of our planet and ripping most all of creative humanity off, without credit, while doing so.


Field Formatter

Note: @TODO bring in, cleaned up and not confusing


In class InotherwordsListFormatter we extend EntityReferenceLabelFormatter

  public function viewElements(FieldItemListInterface $items, $langcode) {
    $inotherwords = oxford_comma_list($elements, $oxford_comma_settings);
    $elements[0] = [
      '#theme' => 'inotherwords_series_wrapped__list' . $theme_suggest,
      '#text_before' => self::processPlural($text_before, $single),
      '#items' => $inotherwords,
      '#text_after' => self::processPlural($text_after, $single),
    ];

<?php

namespace Drupal\inotherwords\Plugin\Field\FieldFormatter;

use function Agaric\OxfordComma\oxford_comma_list;

Note: That function that is central


Why contribute?

  • Right thing to do
  • I asked you to
  • People may contribute to it
  • Someone may take it over and upgrade it or improve it greatly
  • You are slightly more likely to find your own code

Note: Other people are also slightly more likely to find your code. If you have the energy, blog about your module. After that you can apply to add your feed to the Drupal planet. Updating your published date so your old posts get seen one time is acceptable.


Workflow buttons issue: Add a configure link in Extend page.

Note: Someone may make a tiny improvement that makes everyone's lives better.


Nice touches

workflow_buttons.info.yml

 type: module
 description: 'Provide workflow buttons for content moderation instead of a select dropdown of states.'
 core_version_requirement: ^8 || ^9 || ^10 || ^11
+configure: workflow_buttons.settings

How to develop while sharing the same code you're working on andusing

Make a development release (your current branch, like 1.0.x or 2.0.x)

Your project root composer.json:

{
    "require": {
        "drupal/core": "^11",
        "drupal/tomselect": "^2.0.x-dev@dev",
        "drush/drush": "*"
    },
    "repositories": {
        "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    }
}

Note: You have no idea how much easier this is now, i used to warn people that three pages of gibberish would be worth it.


Add this to a ~/.gitconfig file (or the equivalent on your OS).

# Git push helper via https://www.jvt.me/posts/2019/03/20/git-rewrite-url-https-ssh/
[url "ssh://git@github.com/"]
	pushInsteadOf = https://github.com/
[url "ssh://git@gitlab.com/"]
	pushInsteadOf = https://gitlab.com/
[url "ssh://git@git.drupal.org/"]

Examples for developers project

drupal.org/project/examples

Note: It's like it was written just for you.


  • Drupal.org/planet to keep up to date with what is going on in the Drupal world
  • Drupal.tv for recording of talks at different conferences Drupical.com to find out about events happening in your city. The official Drupal API documentation. The Drupal Slack to get community support. A video series by OSTraining on YouTube. Drupalize.Me for high quality (paid) material. UnderstandDrupal.com shameless plug

Maintaining

Note: Isn't that the most anyone can ask of us these days, to maintain?


Join #maintainers channel in Drupal Slack

via Greg Boggs who notes that he and Damien McKenna provide free support to module maintainers

Note: That is the effective takeway from this entire section, because most of the tips in this section come from people in that channel.

I hate to recommend people get on proprietary services, but Slack at least is not as far as i know run by baddies actively colluding to throw orphans and widows off of social security.


  1. Thank people for their contributions.
  2. Use "Plan" issues to coordinate releases, and use the "parent" field to cross-reference releases. This helps people see what you are planning, and what release a specific change is added to.
  3. Add test coverage step by step to make sure you don't accidentally break something.
  4. Go slowly.

Add test coverage

Testing a Drupal module - drupal.org/testing

Join the #testing channel in Drupal Slack.

Note: My opinion— don't let not having tests stop you from sharing.


You can start / leave your module in "Sandbox" mode

You do not have to make your module a full project right away.

(but i do)

Note: I generally make it a full project as soon as i have a full plan for what it needs to do and have started writing code.


“Lord, give me the confidence of a mediocre white man.” — Sarah Hagi

Note: I hereby grant you this confidence.

https://lithub.com/ijeoma-oluo-on-the-pervasive-impact-of-white-mediocrity/


Be explicit about your level of support

Maintenance status radio buttons field showing Actively maintained, Minimally maintained, Seeking co-maintainer(s), and Seeking new maintainer options.

Note: Don't think about this too much when starting development; 'Actively maintained' is fine. Remember to update this when your module is doing what you need it to do— do you want to keep adding features, do you hope somebody else will take over maintenance entirely?

There is another option there, 'Unsupported', but please do not select that after contributing a module unless after starting development you find another module that does what you were trying to do better.


commit based on your own opinion

— cmlara

Note: Be willing to commit based on your own opinion, don’t let issues languish waiting for the community to test, if you feel it’s good and you gave a bit of time (or feel it doesn’t need it) be willing to commit it on your own accord (also valid for “maintainer adjusted on commit”)

From Maintainers slack.


Be a part of something bigger, part 2

  • Integrating a 3rd-party library (JavaScript, PHP) or service?
    • Remember you can file support & feature requests there!
  • Want your module to respect people's human rights? (And not violate some good laws?)
    • Make it accessible. You won't be alone.

Join the fantastic community of people working for accessibility in Drupal

Note: There is more in Drupal (and beyond!) than specific modules and ecosystems; there are people working on fantastic initiatives.


Release!


Write and/or generate release notes

drupal-mrn.dev

Note: This is great, i wrote something similar for Drutopia but we were on GitLab which accepted Markdown at the time, so i had actually been generating markdown. Which reminds me there is a tool from that era i do use all the time…


Have a README.md file

gitlab.com/drutopia/writeme will generate a simple README from your composer.json.

Note: This way you add your description, issue queue link, funding source, etc to composer.json and add them to your README for free.


Choose Drupal security coverage for your module

Apply for the permission to opt into security advisory coverage

drupal.org/node/1011698 has the steps.


Note: Yet another community within the community you can join! Drupal helps a lot with security automatically, follow these tips and ask for clarification when needed.


Be a part of something, part 3

Go to more camps!

Check for PHP and Symfony meetups and events.

Join local user groups, or the nearest (geographically or topical) one that hosts virtual events.

Agaric Show & Tell

agaric.coop/show

Thursdays 2pm Central


Don't be afraid to make mistakes.

We're all human 🙂

— lleber (Luke Alan Leber)

Note: Not to take away from that important and universal sentiment, but Luke's Slack status is "Mad Scientisting Things" (Applications Developer @ Penn State)


Little rabbit in big grass agaric.coop/wtnamft · ASK@AGARIC.COOP

Note: It's a big wild world out there. Bite off a piece you can chew. Links for all of this and more on the session page.


To be continued…

Note: Let me know what you build!

ben@agaric.coop

social.coop/@mlncn


Free Palestine

#LandBack

Note: Land acknowledgement. Pairing these intentionally. Leonard Peltier was finally freed this year. If a Native Americans were to respond to 400 years of dispossession and , and political parties rallied for more active extermination, would you join that, or would you be working for Land Back?

There is more than enough resources and land for us all to be comfortable. We need to work for human rights for all at the expense of privileges for a few.

Maybe work for justice now so there is no decision to be made if there is sudden violence; you already know you are working on the right thing.


Agaric

a web technology collective

ask@agaric.coop