ContentBlocks ContentBlocks 2.x Developer Server Events Plugin Events
Note: This page documents MODX system events carried forward from ContentBlocks 1.x. Verify event names and signatures against your ContentBlocks 2 installation. For v2-specific events, see Server Events.
ContentBlocks provides several events that allow you to extend and customize its functionality.
Table of Contents
Quick Start
Here are the most common scenarios:
- Create custom input types: Return JavaScript and CSS URLs from
ContentBlocks_RegisterInputs. See Registering Inputs. - Modify content on save: Use
ContentBlocks_RenderContentto transform content before it’s stored - Customize templates: Use
ContentBlocks_BeforeParseandContentBlocks_AfterParseto modify how individual content is parsed
Available Events
ContentBlocks_RegisterInputs- JavaScript and CSS for custom inputsContentBlocks_RenderContent- Modify content before savingContentBlocks_RebuildContent- Hook into content rebuild processContentBlocks_AfterRebuildContent- Run code after rebuild completesContentBlocks_BeforeParse- Modify templates before renderingContentBlocks_AfterParse- Modify final output after rendering
ContentBlocks_RegisterInputs
Return JavaScript and CSS URLs for a custom input. ContentBlocks loads those files immediately after its own canvas assets.
$modx->event->output([
'inputNameHere' => [
'js' => $assetsUrl . 'js/inputs/inputnamehere.js',
'css' => $assetsUrl . 'css/inputnamehere.css',
],
]);
See Registering Inputs.
ContentBlocks_RenderContent
Not yet updated for v2
Purpose: Modify content before it’s saved to the database
When it fires:
- When a resource is saved (after content is retrieved but before HTML is generated)
- During content rebuild operations
What you get:
cbContent- The ContentBlocks content as an arraycbJson- The original JSON string from the databaseresource- The MODX resource object
What to return: An array with modified content, or nothing if no changes are needed
Common Use Cases
- Data Migration: Update field names or structure when upgrading
- Content Validation: Ensure content meets business rules
- Content Enhancement: Add metadata or transform values
Example: Adding a timestamp to all text blocks
// System Event: ContentBlocks_RenderContent
$cbContent = $scriptProperties['cbContent'];
$cbJson = $scriptProperties['cbJson'];
$resource = $scriptProperties['resource'];
$modified = false;
// Add timestamp to all text blocks
foreach ($cbContent as &$block) {
if ($block['field'] === 'text' && !empty($block['value'])) {
$block['value'] .= "\n<p class=\"timestamp\">Last updated: " . date('Y-m-d H:i:s') . "</p>";
$modified = true;
}
}
if ($modified) {
// Update the JSON string
$cbJson = $modx->toJSON($cbContent);
return [
'cbContent' => $cbContent,
'cbJson' => $cbJson
];
}
// No changes needed
return null;
Important Notes:
- Multiple plugins can run - only the first non-empty response is used
- Return
nullor nothing if you don’t want to modify the content - Always update both
cbContentandcbJsonwhen making changes
ContentBlocks_RebuildContent
Not yet updated for v2
Purpose: Run code during the content rebuild process
When it fires: During “Rebuild Content” operations, for each resource being rebuilt
What you get:
cbContent- The ContentBlocks content arrayresource- The MODX resource object being rebuilt
What to return: Nothing (return value is ignored)
Common Use Cases
- Progress Tracking: Log which resources are being processed
- Cache Management: Clear caches for specific resources
- External Integration: Update search indexes or external systems
Example: Logging rebuild progress
// System Event: ContentBlocks_RebuildContent
$resource = $scriptProperties['resource'];
$cbContent = $scriptProperties['cbContent'];
// Log the rebuild progress
$modx->log(modX::LOG_LEVEL_INFO,
'[ContentBlocks] Rebuilding resource #' . $resource->get('id') .
' (' . $resource->get('pagetitle') . ')'
);
// Optional: Clear any cached data for this resource
if ($modx->getCacheManager()) {
$modx->cacheManager->delete('resource/' . $resource->get('id'));
}
ContentBlocks_AfterRebuildContent
Not yet updated for v2
Purpose: Run code after the entire rebuild process completes
When it fires: After all resources have been processed during a rebuild operation
What you get:
total- Total number of resources processedtotal_skipped- Number of resources skipped (not using ContentBlocks)total_skipped_broken- Number skipped due to corrupted datatotal_rebuild- Number of resources successfully rebuilt
What to return: Nothing (return value is ignored)
Common Use Cases
- Cache Management: Clear or warm up caches after rebuild
- External Systems: Update search indexes or trigger external processes
Example: Sending a completion notification
// System Event: ContentBlocks_AfterRebuildContent
$total = $scriptProperties['total'];
$rebuilt = $scriptProperties['total_rebuild'];
$skipped = $scriptProperties['total_skipped'];
$broken = $scriptProperties['total_skipped_broken'];
// Log completion statistics
$modx->log(modX::LOG_LEVEL_INFO,
'[ContentBlocks] Rebuild completed: ' . $rebuilt . ' rebuilt, ' .
$skipped . ' skipped, ' . $broken . ' broken'
);
// Optional: Send notification email
if ($rebuilt > 0) {
$message = "ContentBlocks rebuild completed successfully.\n";
$message .= "Resources rebuilt: " . $rebuilt . "\n";
$message .= "Resources skipped: " . $skipped . "\n";
$message .= "Resources with errors: " . $broken;
// Send email notification (implement your email logic here)
// mail('[email protected]', 'ContentBlocks Rebuild Complete', $message);
}
ContentBlocks_BeforeParse
Not yet updated for v2
Purpose: Modify templates before they’re processed and rendered
When it fires: Before ContentBlocks processes a template (before placeholder replacement)
What you get:
tpl- The template string (HTML, @CHUNK, @FILE, etc.)phs- Array of placeholders that will be used
What to return: A new template string, or nothing if no changes needed
Common Use Cases
- Dynamic Templates: Switch templates based on conditions
- Template Injection: Add wrapper markup or modify structure
- Conditional Rendering: Use different chunks based on content
Example: Implementing a custom Twig parser
// System Event: ContentBlocks_BeforeParse
$tpl = $scriptProperties['tpl'];
$phs = $scriptProperties['phs'];
// Check if this template uses Twig syntax
if (strpos($tpl, '{{') !== false || strpos($tpl, '{%') !== false) {
// Load Twig if not already loaded
if (!class_exists('Twig\Environment')) {
require_once MODX_CORE_PATH . 'components/twig/vendor/autoload.php';
}
try {
// Create Twig environment
$loader = new \Twig\Loader\ArrayLoader(['template' => $tpl]);
$twig = new \Twig\Environment($loader);
// Add custom filters and functions
$twig->addFilter(new \Twig\TwigFilter('modx_resource', function($id) use ($modx) {
$resource = $modx->getObject('modResource', $id);
return $resource ? $resource->toArray() : null;
}));
$twig->addFunction(new \Twig\TwigFunction('modx_chunk', function($name) use ($modx) {
$chunk = $modx->getObject('modChunk', ['name' => $name]);
return $chunk ? $chunk->getContent() : '';
}));
// Render the template with ContentBlocks placeholders
$rendered = $twig->render('template', $phs);
// Return the rendered template
$modx->event->output($rendered);
} catch (Exception $e) {
// Log error and fall back to original template
$modx->log(modX::LOG_LEVEL_ERROR,
'[ContentBlocks] Twig parsing error: ' . $e->getMessage()
);
}
}
// No changes needed
return null;
Important Notes:
- You can’t modify placeholders directly - only the template
- Multiple plugins can run - only the first non-empty response is used
- Keep processing fast as this runs for every block during rendering
ContentBlocks_AfterParse
Not yet updated for v2
Purpose: Modify the final HTML output after all processing is complete
When it fires: After ContentBlocks has finished processing and rendering the template
What you get:
tpl- The final HTML output stringphs- The placeholders that were used during processing
What to return: A modified HTML string, or nothing if no changes needed
Common Use Cases
- Output Enhancement: Add CSS classes, wrap content, or inject scripts
- Content Processing: Minify HTML, add analytics tracking, or modify URLs
- Security: Sanitize output or add security headers
Example: Adding responsive wrapper and analytics tracking
// System Event: ContentBlocks_AfterParse
$output = $scriptProperties['tpl'];
$phs = $scriptProperties['phs'];
// Add responsive wrapper if specified
if (!empty($phs['responsive']) && $phs['responsive'] === 'true') {
$output = '<div class="content-block-responsive">' . $output . '</div>';
}
// Add analytics tracking for specific content types
if (!empty($phs['content_type']) && $phs['content_type'] === 'hero') {
$output .= '<script>gtag("event", "hero_view", {"content_type": "hero"});</script>';
}
// Add lazy loading to images
$output = preg_replace('/<img([^>]+)src=/', '<img$1data-src=', $output);
$output = str_replace('<img', '<img loading="lazy"', $output);
$modx->event->output($output);
Important Notes:
- This runs for every block during rendering - keep it fast
- Multiple plugins can run - only the first non-empty response is used
- This is your last chance to modify the output before it’s saved