Heratio Help Center article. Category: Technical.
Heratio AHG Plugin Architecture
Version: 2.8.2 Last Updated: February 2026
1. Architecture Overview
Heratio Dual-Mode
The Heratio migration adds an alternative rendering path. When .heratio_enabled exists, AHG plugin routes are served by heratio.php (HTTP Kernel + Blade rendering) instead of Symfony. Base Heratio routes continue through index.php unchanged. See HERATIO_MIGRATION.md for full details.
2. Plugin Types and Responsibilities
2.1 Plugin Categories
2.2 Module Ownership Rules
3. Extension Loading Flow
4. Extension Surface API
4.1 AhgHooks - Event System
// Register a hook (in plugin Configuration)
AhgHooks::register('record.view.sidebar', function($record) {
return ['html' => '<div>My Panel</div>'];
}, priority: 10);
// Trigger hooks (in action/template)
$panels = AhgHooks::trigger('record.view.sidebar', $record);
// Filter hooks (modify value through chain)
$title = AhgHooks::filter('record.title', $record->title, $record);
4.2 AhgCapabilities - Feature Detection
// Register capability (in plugin Configuration)
AhgCapabilities::register('iiif', 'ahgIiifPlugin', [
'version' => '3.0',
'auth' => true
]);
// Check capability (anywhere)
if (AhgCapabilities::has('iiif')) {
// Show IIIF viewer
}
4.3 AhgPanels - UI Slots
// Register panel (in plugin Configuration)
AhgPanels::register('informationobject', 'condition-status', [
'title' => 'Condition',
'position' => 'sidebar',
'component' => 'condition/statusComponent'
]);
// Get panels for position (in template)
$panels = AhgPanels::forPosition('informationobject', 'sidebar', $record);
4.4 AhgSectorProfile - Sector Configuration
// Register sector (in Museum plugin)
AhgSectorProfile::register('museum', [
'name' => 'Museum',
'standard' => 'museum procedures',
'labels' => [
'extent' => 'Dimensions',
'scopeAndContent' => 'Object Description'
],
'vocabularies' => [
'materialType' => ['Oil paint', 'Bronze', 'Marble']
]
]);
// Get label for current sector
$label = AhgSectorProfile::getLabel('extent', 'Extent');
5. Directory Structure Standards
6. Cross-Plugin Link Guards
6.1 The Problem
Sector plugins (Gallery, Museum, DAM) and shared menus (theme, reports) contain links to features provided by optional capability plugins (Collections Procedure, Provenance, GRAP, etc.). When those plugins are disabled, the links become broken (404 errors or blank pages).
6.2 The ahg_is_plugin_enabled() Helper
A cached helper function checks whether a plugin is enabled at runtime:
// Located in: ahgUiOverridesPlugin/lib/helper/AhgLaravelHelper.php
function ahg_is_plugin_enabled(string $pluginName): bool
{
static $cache = [];
if (isset($cache[$pluginName])) {
return $cache[$pluginName];
}
try {
$cache[$pluginName] = DB::table('atom_plugin')
->where('name', $pluginName)
->where('is_enabled', 1)
->exists();
} catch (\Exception $e) {
$cache[$pluginName] = false;
}
return $cache[$pluginName];
}
Performance: Uses a static cache. Each plugin name is queried only once per request.
6.3 Template Usage Pattern
Every template that uses ahg_is_plugin_enabled() must include the helper:
<?php require_once sfConfig::get('sf_plugins_dir')
. '/ahgUiOverridesPlugin/lib/helper/AhgLaravelHelper.php'; ?>
Then wrap optional plugin links:
<?php if (ahg_is_plugin_enabled('ahgSpectrumPlugin')): ?>
<a href="<?php echo url_for(['module' => 'spectrum', 'action' => 'index']); ?>">
SPECTRUM Procedures
</a>
<?php endif; ?>
6.4 Plugin Name Reference
Templates use module names for routing but plugin names for enable checks:
6.5 Guarded Templates
| Plugin | Templates with Guards |
|---|---|
| ahgGalleryPlugin | gallery/templates/indexSuccess.php (sidebar + admin dropdown) |
| ahgMuseumPlugin | museum/templates/indexSuccess.php, museum/templates/conditionReportSuccess.php, cco/templates/conditionReportSuccess.php, cco/templates/_actions.php |
| ahgDAMPlugin | dam/templates/dashboardSuccess.php |
| ahgThemeB5Plugin | templates/_glamDamMenu.php |
| ahgReportsPlugin | reports/templates/_reportsMenu.php |
6.6 Rules for Developers
- Always guard cross-plugin links - If a template links to a module owned by another optional plugin, wrap the link with
ahg_is_plugin_enabled(). - Use the correct plugin name - Module names differ from plugin names (see mapping above).
- Include the helper - Every template using the function must
require_oncethe AhgLaravelHelper. - Never guard links to core/infrastructure plugins - ahgDisplayPlugin, ahgThemeB5Plugin, ahgSettingsPlugin, and ahgSecurityClearancePlugin are always enabled.
7. CI Architecture Checks
The bin/check-architecture.sh script enforces:
| Check | Rule | Severity |
|---|---|---|
| Cross-Plugin Guards | Links to optional plugins use ahg_is_plugin_enabled() |
WARNING |
| Check | Rule | Severity |
|---|---|---|
| Theme Actions | ahgThemeB5Plugin has no actions/*.php | ERROR |
| Module Ownership | Each module has exactly one action owner | ERROR |
| Absolute Paths | No /usr/share/nginx in PHP files | ERROR |
| Asset Location | Assets only under web/ | ERROR |
| DB Location | SQL only under database/ | ERROR |
| Zero-byte Files | No empty files (except .gitkeep) | ERROR |
| Sector Plugins | No core module actions in sector plugins | ERROR |
Part of the Heratio AHG Framework - v2.8.2