Extension points¶
Surfaces external modules can rely on without forking the dns module. This page covers the contract; the Architecture and Writing a record type pages cover the rationale.
Plugin managers¶
plugin.manager.dns_record_type¶
Service id: plugin.manager.dns_record_type —
RecordTypeManager.
- Discovery: PHP-attribute-based via the
#[Drupal\dns\Attribute\RecordType]attribute on classes undersrc/Plugin/RecordType/of any installed module. - Contract: implementations satisfy
RecordTypeInterface. - Plugin id: matches the DNS record-type token (
A,MX,SRV, …). The ids surface in the entity'srecord_typefield's allowed values viaDnsRecord::recordTypeAllowedValues().
Add a new record type by dropping a class under
MyModule\Plugin\RecordType\ with the attribute. No service or schema
changes needed.
Hooks¶
hook_dns_record_type_info_alter(array &$definitions) runs after
discovery and before the plugin manager caches the definition map.
Use it to relabel, hide, or augment third-party plugins; the keys are
the plugin ids. Don't add new plugins via the alter hook — register
them as classes so they participate in dependency injection and the
attribute discovery cache.
hook_ENTITY_TYPE_* for dns_zone and dns_record work as for any
content entity — hook_dns_zone_predelete, hook_dns_record_presave,
hook_dns_record_view, etc. The zone-deletion-blocked-by-records
guard runs in DnsZone::preDelete(), so a predelete hook fires
after that check passes.
Views integration¶
The dns_record_rdata_value Views field handler renders one rdata key
per instance — admins (or other modules) configure the JSON key in the
field options. External modules that ship custom views referencing
rdata can include the field handler with no further setup; see the
Views customization page for the user-facing
side.
The dns_records view (configured at install) is plain config —
modules that want a different default can replace it via standard
Drupal config-import flows.
Form alters¶
DnsRecordForm is a regular ContentEntityForm, alterable with
hook_form_dns_record_form_alter and
hook_form_dns_record_<bundle>_form_alter — though dns_record
currently has a single bundle, so the bundle-specific variant
matches the generic one. The form attaches two libraries:
dns/target_preview— live BIND-resolution under the target widget.dns/prefix_overlap_warning— confirm dialog if the prefix tail duplicates the zone label.
If your module wants to skip either enhancement, detach it in a form alter:
function mymodule_form_dns_record_form_alter(&$form, $form_state): void {
$form['#attached']['library'] = array_filter(
$form['#attached']['library'] ?? [],
fn ($lib) => $lib !== 'dns/target_preview',
);
}
Access policy: the dns_zone scope¶
Per-zone capabilities are modeled via Drupal core's
Access Policy API.
The module ships
DnsZoneGrantPolicy
in the dns_zone scope; each zone's id is an identifier within that
scope. Calculated permissions for a user in (dns_zone, <zone_id>)
carry whichever capabilities apply (owner implies all; explicit
grants add more).
Third-party modules can extend the dns_zone scope with their own
access policies. The standard pattern:
final class MyProviderConstraintPolicy extends AccessPolicyBase {
public function applies(string $scope): bool {
return $scope === 'dns_zone';
}
public function alterPermissions(
AccountInterface $account,
string $scope,
RefinableCalculatedPermissionsInterface $calculated_permissions,
): void {
// Revoke `edit zone` and `edit records` on every zone this
// provider has imported as read-only — even owners shouldn't
// mutate them locally.
foreach ($calculated_permissions->getItems() as $item) {
if (!$this->isReadOnly($item->getIdentifier())) {
continue;
}
// Use removeItem / addItem to refine; see the API docs.
}
}
public function getPersistentCacheContexts(): array {
return ['user'];
}
}
Tag the service with access_policy and the processor will pick it
up automatically. The constant DnsZoneGrantPolicy::SCOPE is
guaranteed stable for third-party use.
Provider-sync architecture (planned)¶
A plugin.manager.dns_provider is on the roadmap (Cloudflare, Route
53, BIND export, …). The interface and lifecycle aren't stable yet —
no third-party module should depend on it before it lands. The
Architecture
roadmap table tracks the design sketch. When a provider plugin ships,
it'll typically also register an access policy in the dns_zone
scope (per the previous section) to enforce ops constraints —
read-only zones, records-editable-but-zone-locked, and so on.