Drupal Planet

Berliners blog: Generate Drupal local actions from Views configuration

Generate Drupal local actions from Views configuration

On an editorial site, it is often useful to give articles, documents and other content types their own administration listings. Editors can then work with one type of content at a time, with an "Add Article" or "Add Document" button alongside the relevant list.

Maintaining a separate button definition for every listing means keeping the same relationship in two places. Whenever we add a listing or change which content type it shows, we need to remember to update its creation action too. This article shows how to avoid that duplication with a local action deriver.

When those listings are built with Views, their content-type filters already tell us which creation form belongs on each page. We can use that information to generate the buttons, keeping their definitions in step with the listings they belong to.

Illustrative mockups with example content. Each listing offers its own creation action.

A creation button for each listing

Drupal provides these buttons through local actions. To add one, we specify its label, where it links to and which page should display it. For an article listing, that could take a short YAML definition in listing_actions.links.action.yml, assuming the custom module's machine name is listing_actions:

listing_actions.add_article: title: 'Add Article' route_name: node.add route_parameters: node_type: article appears_on: - view.content.page_articles

Here, appears_on places the button on the article listing. The route name view.content.page_articles assumes that the View's ID is content and its page display's ID is page_articles, following the pattern view.{view_id}.{display_id}. Clicking it opens the node.add route, where the node_type parameter selects the article creation form.

We could add another entry for documents and continue in the same way for other listings. That is a good fit when each action needs its own wording or destination. If all the listings follow the same convention, though, we can get those values from existing configuration: the View identifies the display and content type, and the content type supplies its label.

A plugin deriver lets us build those entries from the existing configuration. It returns several plugin definitions that share one implementation—the same approach I described in my 2014 post about block derivatives. The early Drupal 8 code in that post is outdated, but the idea applies to local actions too.

Which displays qualify?

For this example, we will use a View named content, with a page display for each listing. To choose the right creation form, the deriver needs to know that a display lists exactly one content type. The configuration therefore needs to follow a few conventions:

  • Each eligible display overrides its filters and has a content-type filter named type_1.
  • That filter includes exactly one node type, is not exposed and restricts the whole listing. No OR group admits other content types.

The key type_1 identifies a particular filter in the View's configuration; the content type it selects has its own machine name, such as article. Your View may use a different filter key, for example type. To find it, export the View and look under display → your_display_id → display_options → filters in views.view.content.yml. Find the entry with entity_type: node and field: type, then use its key in place of type_1 in the PHP example. As written, the deriver expects that same key on every eligible display.

These constraints let the deriver read the filter straight from each display's stored configuration. It skips displays that inherit their filters; to support those as well, we would need to read their effective options through the Views display API.

The example also relies on a cache rebuild after configuration changes, so it fits a deployment workflow that imports configuration and then rebuilds caches. We will look at that requirement after the implementation.

Register the deriver

With those conventions in place, we can replace the individual action entries with one definition that points to the deriver. In an enabled custom module named listing_actions, put this in listing_actions.links.action.yml:

listing_actions.content_add: class: Drupal\Core\Menu\LocalActionDefault deriver: Drupal\listing_actions\Plugin\Derivative\ContentLocalActions

Local actions use YAML discovery, so this entry is how Drupal finds the deriver. The class property keeps core's LocalActionDefault as the implementation for every generated action. All we need to supply is the code that works out their labels and routes.

Read the displays and build the definitions

To load the View and its referenced content types, the deriver needs the entity type manager. The complete class uses ContainerDeriverInterface to receive that service from Drupal. Save it as src/Plugin/Derivative/ContentLocalActions.php inside the module so it matches the class named in the YAML entry.

Most of the work happens in getDerivativeDefinitions(). It loads the content View, skips displays that do not meet the conditions above and builds an action for each remaining display:

public function getDerivativeDefinitions($base_plugin_definition): array { $this->derivatives = []; $view = $this->entityTypeManager->getStorage('view')->load('content'); if (!$view || !$view->status()) { return $this->derivatives; } foreach ($view->get('display') as $display_id => $display) { if ($display['display_plugin'] !== 'page') { continue; } $options = $display['display_options']; if (($options['enabled'] ?? TRUE) === FALSE) { continue; } // Only use filters explicitly overridden for this display. if ($options['defaults']['filters'] ?? TRUE) { continue; } $filter = $options['filters']['type_1'] ?? []; $types = $filter['value'] ?? []; if (($filter['entity_type'] ?? NULL) !== 'node' || ($filter['field'] ?? NULL) !== 'type') { continue; } if (($filter['operator'] ?? NULL) !== 'in' || !empty($filter['exposed']) || count($types) !== 1) { continue; } $node_type = $this->entityTypeManager->getStorage('node_type')->load(reset($types)); if (!$node_type) { continue; } $this->derivatives[$display_id] = [ 'title' => $this->t('Add @label', [ '@label' => $node_type->label(), ]), 'route_name' => 'node.add', 'route_parameters' => [ 'node_type' => $node_type->id(), ], 'appears_on' => ['view.content.' . $display_id], ] + $base_plugin_definition; } return $this->derivatives; }

The array near the end of the method contains the same values as our first YAML example. The content type supplies the label and creation-form parameter, while the display ID determines where the action appears. Adding $base_plugin_definition carries over shared properties, including the action class we registered earlier.

Using the display ID as the array key also gives each action a distinct derivative ID. Drupal combines it with the base plugin ID, so the action for page_articles becomes:

listing_actions.content_add:page_articles

That ID identifies the action itself. Its appears_on route is still view.content.page_articles, and its destination is still node.add with the article type as a parameter—just as in the static definition.

Who can see the button?

Once Drupal has these definitions, it can decide which actions to show on a page. It does this by checking access to each destination route with its parameters. For the article action, that means checking whether the current user may open node.add for the article content type, separately from whether they may view the listing.

This is why the deriver contains no current-user permission checks. Drupal caches its definitions, so making discovery depend on the user who triggered it could leave other users with the wrong set of actions. Access belongs in the later step, when Drupal builds the buttons for the current page.

When the configuration changes

Caching also means that the deriver does not reread the View on every request. If a display's filter or ID changes, or a content type gets a new label, the stored action definitions need to be regenerated.

For this example, a full cache rebuild is the point at which those changes take effect. Rebuild after installing the files and after importing changed configuration, using Drupal's "Clear all caches" action at Configuration → Development → Performance or your environment's Drush cache-rebuild command. This refreshes both the definitions and the rendered output.

The same applies when editing the configuration through the UI. If those edits need to take effect automatically, the integration must respond to the relevant configuration changes, call clearCachedDefinitions() on plugin.manager.menu.local_action and invalidate the affected rendered output. Those handlers are not included in the accompanying class.

With this in place, adding another listing that follows the same filter convention also gives it the appropriate creation action after the next cache rebuild. There is no separate button definition to maintain.

berliner Thu, 09/24/2026 - 00:05 Tags

Droptica: Drupal multisite in the AI era: when shared code is not enough

Sharing Drupal code across country sites does not sync product specifications, documents, or company claims.

Drupal multisite vs multilingual teams must decide where authoritative facts live before AI assistants encounter contradictory pages. Maciej Lukianski compares multisite, one multilingual Drupal, and Domain Access for market-aware publishing.

LakeDrops Drupal Consulting, Development and Hosting: Eight Posts on the Fun Part. One on the Bill.

Eight Posts on the Fun Part. One on the Bill. Photo by Kamil Foatov on Unsplash Jürgen Haas Wed 23 Sep 2026 - 15:00

In Post 8 I described my monthly billing run. One client never gets an invoice: the Drupal community, which has had almost all of my working time since July 2025. This post is that invoice. The eight posts before it showed the fun part: the Modeler API, the Workflow Modeler, test and replay, the ECA Guide, orchestration. This one shows the ledger underneath: 87 actively maintained drupal.org projects, the Gin admin theme, the Admin theme subsystem in core, 293 public projects on the LakeDrops GitLab, a calendar of weekly and monthly community meetings, two DrupalCons a year. Innovation gets applause; maintenance gets a green badge. Every funding conversation so far was about a feature. Nobody has offered to sponsor a security release. Since July 2025 the revenue has been zero, paid for by earlier years. Dries' cost-allocation posts explain why. The menu at the end has prices: sponsor maintenance through Open Collective, a service agreement, hiring for ECA work, funding the next innovation. Total: roughly €10,000 a month.

Webpro Company blog: Who provides Drupal development in Estonia and how do you choose the right partner?

Estonia has several development partners with strong Drupal experience, but they are not directly interchangeable. Building a large new digital platform, maintaining an existing Drupal website and taking over a legacy project require different kinds of teams. The first step in choosing the right partner is to define the problem you actually need to solve. Who provides Drupal development in Estonia? Drupal development in Estonia is provided by both larger full-service software companies and smaller specialist teams. Based on public service pages, references, Drupal.org profiles and public procurement records from recent years, visible providers include ADM Interactive, Trinidad Wiseman, Web Expert, Krabu Grupp and Krabu Tech, Mearra, Limegrow, Revelan and WebPro. This is not a ranking…

Webpro Company blog: Who actually controls your company website?

A partner-managed website is not a problem as long as your company can take control of all critical access and assets when needed. Gaps usually become visible only when you need to change developers or restore the website quickly. Your company may own the domain while someone else controls access The first things to check are the domain and DNS. Your company should know who the domain is registered to, where it is managed and which email address receives renewal notices and other important messages. The same applies to DNS. If only the current development partner or a former employee can access it, even a simple server migration can become difficult. DNS can affect services beyond the website, so records should not be changed blindly during a partner transition. The point is not that a…

Drupal AI Initiative: The countdown is on: sovereignty on the Enterprise AI Summit

The Enterprise AI Summit takes place in one week, on 28 September aboard the SS Rotterdam, and the countdown is a good moment to properly introduce a session that's been on the agenda for a while: sovereign AI with Julien Blanchez.

Digital sovereignty has moved from a policy discussion into a boardroom question. Regulators, procurement teams and public sector organisations are asking the same thing in different ways: can we use world-class AI technology while keeping control over where our data lives, who can access it, and under what conditions?

With over a decade at Google working on data protection, security and digital sovereignty for large, highly regulated organisations, Julien will walk us through what's driving rising sovereignty expectations and how to keep access to leading AI technology on your own terms.

He joins a day full of similar questions answered with real numbers. The European Personnel Selection Office deployed a RAG-powered instant answer engine inside Drupal in under eight weeks, running in all 24 EU official languages, with 90% fewer repeat support questions and zero hallucinations on manual review. The American Diabetes Association will share what happened when editorial teams got real AI tools in their hands, including the honest lessons that came with it. And Moritz Arendt takes on a question that sits right next to Julien's: can AI strengthen digital communities, or does it risk hollowing them out?

With just days left, there’s still time to check the full agenda and save your seat. Tickets and details are available on the Enterprise AI Summit page.

Droptica: Drupal vs WordPress enterprise: AI-era content operations

Choosing a CMS for a multilingual product catalogue is a content-operations decision, not a plugin shootout.

Drupal vs WordPress enterprise teams must compare field-level translation, moderation, entity APIs, and AI-ready outputs before assistants read conflicting specs. Maciej Lukianski explains when WordPress still wins, when Drupal fits connected complexity, and how to pilot migration without guessing.

Omega8.cc: New Engine, Same Keys

The database under a hosted Drupal or Backdrop estate has to change generations one day, and that is the day most operators find out what their migration tooling was hiding. On a BOA box the move across Percona generations is a rehearsed road: a readiness check names the one account which would block the whole box, the single-account mover carries an account to the newer server with passwords, PHP versions and search indexes intact and the old box relaying traffic until DNS moves, and the whole-server mover refuses to cross a version at all, on purpose, because it replicates rather than dumps. Every transfer and every cutover is a dry run first, every dry run is spent on use, and the watchdog which rescues stuck databases stands down for exactly the minutes it would otherwise rescue you from your own migration.

The Drop Times: What EU Digital Identity Wallets Could Mean for Drupal

European Digital Identity Wallets are moving from technical framework and pilot programmes towards services that people will be able to use across the European Union. The European Commission says Member States must make wallets available to citizens, residents, and businesses by the end of 2026. For Drupal teams, that makes digital identity less a future policy topic and more an application-architecture question.

The wallet model changes what an online service can ask a user to prove. Instead of treating identity as one complete profile, users can present particular identity data or attestations and disclose only the information required for a transaction. Commission figures say more than 550 companies and public authorities across 26 Member States, Norway, Iceland, and Ukraine are participating in large-scale pilots covering government services, banking, education, health, transport, telecommunications, and other uses.

The relevant role for a Drupal application is the relying party. Under the European framework, organisations that use wallets for digital services must register their intended use and indicate the data they plan to request. That puts the request itself inside a defined trust and registration model rather than leaving every application to decide independently what identity information it wants to collect.

The technical exchange is also more specific than adding another login provider. For remote presentation flows, the EUDI architecture uses OpenID for Verifiable Presentations to carry requests and wallet responses between wallets and relying parties. A Drupal service could potentially use a validated result from that layer for authentication, account linking, permissions, eligibility checks, or workflow decisions, but the material reviewed for this issue does not establish a production-ready Drupal integration that provides those functions today.

Data handling may prove more consequential than authentication itself. A wallet may be able to prove that a person has a particular attribute without requiring the service to collect a larger identity record, and the European framework is designed around data minimisation and selective disclosure. The application still has to decide what happens next: whether the verified attribute becomes persistent Drupal user data, whether only a derived result is retained, or whether the information can disappear once the transaction has been completed.

Those decisions sit above the wallet protocol. Drupal already provides accounts, permissions, fields, workflows, access rules, logging, and application-specific data models, but none of those automatically determine which verified identity attributes an organisation should retain. Connecting a verifier to Drupal therefore solves only part of the problem; the rest belongs to application design, data governance, and the legal requirements of the service being built.

The EUDI technical framework is still developing as deployment approaches. Version 3.0.0 of the EUDI Wallet Architecture and Reference Framework, published in July 2026, updates Wallet-Relying Party registration and introduces the concept of Relying Party Services alongside other changes. For Drupal projects in European government, education, and other identity-sensitive environments, the question is not simply how Drupal accepts a wallet, but which verified facts the application actually needs after the wallet has done its job.

Follow The DropTimes on LinkedIn, X, Bluesky, and Facebook, or join #thedroptimes on Drupal Slack.

Kazima Abbas wrote and curated this issue of Editor’s Pick.

Droptica: Drupal multilingual websites: how AI cuts translation workload while your team stays in control

Every update on a multilingual Drupal site must reach each market language - not just the source page.

Drupal multilingual websites can use AI-assisted translation drafts inside Content Translation workflows so editors review and publish without copying text between tools. Maciej Lukianski explains shared fields, editorial ownership, document links, and keeping localized pages consistent for people and AI search.

Drupal blog: The AWS Migration: Building the Next Chapter of Drupal’s Infrastructure

This is the first post in a three-part series about the impact of the AWS Open Source Credits program on the Drupal community. Part one covers our testing infrastructure. Part two will cover the community programs we host beyond code. Part three will cover what the AI era is doing to open source infrastructure, and what we're building next.

Drupal is one of the open web's longest-running content management projects, in continuous development since 2001, and a recognized Digital Public Good. It runs a large share of the public web: government portals, universities, hospitals, cultural institutions, and international organizations, including the European Commission and agencies across the UN system. The Drupal Association is the small non-profit that keeps the project's home online. We build the tools that enable our community to build Drupal.

Since 2025, the AWS Open Source Credits program has supported the infrastructure behind that work. This series is our thank you, and impact report about what that support has made possible.

The scale

Drupal's collaboration happens on git.drupalcode.org, our self-hosted GitLab instance and one of the largest in open source. It hosts 42,382 contributed projects: 39,003 modules, 2,400 themes, and 979 distributions, alongside Drupal core itself.

The Drupal community opens more than 5,300 merge requests every month, about 65,000 a year, from 8,000 unique contributors annually. Drupal core alone has 15,256 issue forks.

Every one of those merge requests runs through continuous integration (CI) before it can ship. Our GitLab CI runners autoscale on AWS, accounting for roughly a third of our entire cloud footprint. In the last year they executed ~4.1 million jobs, consuming ~159.8 thousand compute hours. That's 438 hours of compute per day. (Data from Aug 26, 2025 – Aug 26, 2026).

Velocity is a contributor experience challenge

Here's what autoscaling CI means for a community like ours. A contributor in Singapore, Mumbai, or Rotterdam pushes a change and gets test results back in minutes. There is no fixed pool of test machines, no queue where contributors wait on each other, no rationing of who gets to test what.

If elastic capacity, funded by credits, means those contributors have never had to deal with longer queues, capped concurrency, or asking volunteers to test less..

We care about this because contributor velocity is essential to the success of Drupal. The Drupal Association engineering team is small, just 4 people. We don't build Drupal; thousands of people do that. Our job is to make sure the tools are available and fast when they need them.

Velocity and safety are the same system

It would be easy to frame fast CI as a developer-happiness story and stop there. But for a project with Drupal's footprint, that same infrastructure is a safety system.

In the last twelve months, the Drupal Security Team has coordinated 163 security advisories across core and contributed projects. Every fix behind those advisories was developed, tested, and released through the same AWS-powered pipeline as any other change. The sites that depend on those fixes find out through us too: about 667,000 Drupal sites check in with our update infrastructure every week to learn whether they need to act. That's a conservative floor, since it only counts sites that phone home.

We also operate Drupal Steward, a protective service that shields sites during the most dangerous window in security response: the hours between a public advisory and a site's own patching. AWS powers that too.

Drupal powers governments, public utilities, and health systems worldwide, making our testing pipeline critical public infrastructure. Every automated test directly protects the security and stability of the websites people rely on daily.

A smooth transition

For two decades, much of Drupal's infrastructure ran on donated hosting at the Oregon State University Open Source Lab, which has been a quiet hero to dozens of open source projects. As the lab restructures, we have been migrating essential services to AWS. Credit support meant that migration happened without cutting contributor capacity, and without asking a small non-profit engineering team to do more with less at the worst possible moment.

Our thanks to the OSU Open Source Lab for twenty years of partnership, and to AWS for making this next chapter possible.

What's next in this series

While compute numbers show our scale, the human impact matters just as much., In our next post: the community programs we host that have nothing to do with code, and why we think that infrastructure matters just as much.

The Drupal Association is a 501(c)(3) non-profit. If your organization depends on Drupal, or on the health of open source infrastructure generally, you can support our work.

BloomIdea: Mautic Audiences for Drupal: personalisation built on the segments you already maintain

Your marketing team already knows who the VIPs are. They maintain the segment in Mautic, they send it a campaign every month, they add and remove people as orders and behaviour change. Then somebody asks for a VIP banner on the website, and the website turns out to know nothing about any of it. So "VIP" gets built a second time, as a Drupal role or a checkbox on the user: two definitions of the same audience, maintained by two teams, drifting apart from the first week.

The second problem arrives with the first. Personalisation on Drupal usually means varying the page per visitor, and a page that varies per visitor is a page nobody else can be served, so the banner meant to lift conversion ends up slowing the site down for everyone.

We build and run Mautic-driven marketing for our e-commerce clients, so we kept meeting both problems on the same projects. Mautic Audiences is our answer, now on drupal.org as free software (GPL-2.0-or-later) for Drupal 10.3 and 11, covered by Drupal's security advisory policy. It reads the segments and tags you already maintain in Mautic and turns them into native Drupal primitives: block visibility, Twig, tokens, JavaScript, Views, Search API and, since 1.1, a field.

What editors can build without a developer

Place any block, scroll to Visibility, pick Mautic segment, type vip. Save. That is the whole configuration, and it covers most of what people actually ask for:

  • A VIP banner that appears the moment Mautic adds someone to the segment. No Drupal save, no cache flush.
  • A homepage hero per traffic source: three blocks, one each for newsletter, instagram and paid-search, each gated on its own segment.
  • Hiding what someone already has. The newsletter signup block disappears for visitors already tagged subscriber.
  • Coupon-aware touches for visitors carrying a coupon:* tag, fired client-side so the page itself stays cacheable at the edge.
  • Cross-channel campaigns. Mautic sends the email, and the Drupal page the link lands on recognises the segment it was sent to. One audience definition, two channels.

Anonymous visitors count: someone carrying Mautic's tracking cookie has an audience with no login, which is what lets a campaign link open a personalised page at all. Editors check their work by appending ?ma_preview_segments=vip to any URL, which shows the site as a member of that segment sees it.

Themers get the same decision as a one-liner that brings its own cache metadata, and there is a token for metatag patterns and a JavaScript call for whatever has to happen after the page is served:

{% if is_in_segment('vip') %} <p>Free shipping today, your VIP perk.</p> {% endif %} Content that knows who it is for

The module started by answering "who is this visitor?". Version 1.1 adds the other half, "who is this content for?", in a bundled sub-module.

Add the Mautic audience field to any bundle and editors pick, from the real list of segment names, who a piece of content is meant for. By default that is a targeting hint templates and listings read. Per field, you can turn on Restrict viewing to the selected Mautic segments, and content with a segment picked is then refused to everyone outside it. Content with nothing picked stays for everyone.

A gate that only closes the page is not a gate, so the sub-module also ships a Views filter that keeps whichever rows each visitor is entitled to, and a Search API processor that adds the condition at query time rather than keeping items out of the index. Its README lists what none of it can cover, exports, feeds, custom code that loads entities without an access check, because a promise of restriction with holes in it is worse than no promise.

The interesting part is what it refuses to expose

Segment names and tags are business intelligence: cancelled-subscription, risk:churn-90d, coupon:VIP-50. Ship them to the browser and you have published how you categorise your customers, and sometimes a working coupon code, to anyone with DevTools open.

So the client-side API answers questions instead of listing facts. It will tell you whether this visitor is in a segment you name; it will not tell you which segments exist or which ones they are in. The endpoint that returns an actual list starts empty and returns only what an editor has allowlisted. The field type has no formatter at all, and reading its values takes an administrative permission, which covers JSON:API, REST and Views alike.

It stays out of your render path

Audiences are read from local storage on every render, never from the Mautic API, so a slow Mautic never sits in front of a page, and an outage degrades to an empty audience rather than an error. Cache contexts key on the resolved audience rather than on the visitor, so two people in the same segments share a cache entry and a page with two gated blocks has four variants, not one per visitor. Anonymous personalisation does cost you Drupal's Internal Page Cache on the pages that use it, which docs/cdn.md deals with head on.

Get it composer require drupal/mautic_audiences drush en mautic_audiences

It needs Advanced Mautic Integration for API connectivity, a Mautic instance with API credentials, and PHP 8.2 or later. A bundled sub-module wires Klaro into the resolver, so visitors who have not consented resolve to an empty audience without any code.

The project page, the issue queue and the documentation are at drupal.org/project/mautic_audiences. If you run Drupal Commerce, Commerce Mautic Connect pushes the abandoned carts, customer metrics and coupon tags into Mautic that this module reads back. Issues and merge requests are very welcome.

Pages