From f30f972a5ca558a3b651111040b17dad7da43f3c Mon Sep 17 00:00:00 2001 From: Murray Wood Date: Fri, 4 Sep 2026 18:00:33 +0800 Subject: [PATCH] Replace click-to-field parser swap with author-placed markers (#55) --- _bootstrap/index.php | 29 ++++- _build/build.transport.php | 21 ++- _build/data/transport.snippets.php | 13 ++ _build/events/events.magicpreview.php | 2 +- _build/resolvers/staleevents.resolver.php | 41 ++++++ .../magicpreview/docs/changelog.txt | 12 ++ core/components/magicpreview/docs/readme.txt | 36 +++++ .../elements/plugins/magicpreview.plugin.php | 123 +++++++++--------- .../elements/snippets/mpfield.snippet.php | 76 +++++++++++ .../magicpreview/lexicon/da/default.inc.php | 2 +- .../magicpreview/lexicon/de/default.inc.php | 2 +- .../magicpreview/lexicon/en/default.inc.php | 2 +- .../MagicPreviewCoreParser.class.php | 43 ------ .../MagicPreviewCoreParserTrait.php | 78 ----------- .../model/magicpreview/magicpreview.class.php | 45 ++++++- .../processors/resource/PreviewTrait.php | 40 ++++-- 16 files changed, 360 insertions(+), 205 deletions(-) create mode 100644 _build/data/transport.snippets.php create mode 100644 _build/resolvers/staleevents.resolver.php create mode 100644 core/components/magicpreview/elements/snippets/mpfield.snippet.php delete mode 100644 core/components/magicpreview/model/magicpreview/MagicPreviewCoreParser.class.php delete mode 100644 core/components/magicpreview/model/magicpreview/MagicPreviewCoreParserTrait.php diff --git a/_bootstrap/index.php b/_bootstrap/index.php index 285efbe..939385a 100644 --- a/_bootstrap/index.php +++ b/_bootstrap/index.php @@ -95,6 +95,15 @@ ], 'name', true)) { echo "Error creating MagicPreview Plugin.\n"; } + +if (!createObject('modSnippet', [ + 'name' => 'mpField', + 'description' => 'Returns click-to-field data attributes for a resource field or TV.', + 'static' => true, + 'static_file' => $componentPath.'/core/components/magicpreview/elements/snippets/mpfield.snippet.php', +], 'name', true)) { + echo "Error creating mpField snippet.\n"; +} $vcPlugin = $modx->getObject('modPlugin', ['name' => 'MagicPreview']); if ($vcPlugin) { if (!createObject('modPluginEvent', [ @@ -127,7 +136,7 @@ } if (!createObject('modPluginEvent', [ 'pluginid' => $vcPlugin->get('id'), - 'event' => 'OnWebPagePrerender', + 'event' => 'ContentBlocks_BeforeParse', 'priority' => 0, ], ['pluginid','event'], false)) { echo "Error creating modPluginEvent.\n"; @@ -139,6 +148,24 @@ ], ['pluginid','event'], false)) { echo "Error creating modPluginEvent.\n"; } + + // Drop event registrations older versions created that are no longer + // handled. Mirrors _build/resolvers/staleevents.resolver.php, which does + // the same on package upgrade -- keep the two lists in step. + $staleEvents = [ + 'OnWebPagePrerender', + 'OnWebPageComplete', + ]; + foreach ($staleEvents as $staleEvent) { + $stalePluginEvent = $modx->getObject('modPluginEvent', [ + 'pluginid' => $vcPlugin->get('id'), + 'event' => $staleEvent, + ]); + if ($stalePluginEvent) { + $stalePluginEvent->remove(); + echo "Removed stale modPluginEvent {$staleEvent}.\n"; + } + } } diff --git a/_build/build.transport.php b/_build/build.transport.php index ec048a1..dab90cb 100644 --- a/_build/build.transport.php +++ b/_build/build.transport.php @@ -23,7 +23,7 @@ function getSnippetContent($filename = '') { /* define version */ define('PKG_NAME','MagicPreview'); define('PKG_NAME_LOWER',strtolower(PKG_NAME)); - define('PKG_VERSION','1.7.1'); + define('PKG_VERSION','1.8.0'); define('PKG_RELEASE','pl'); /* load modx */ @@ -89,6 +89,10 @@ function getSnippetContent($filename = '') { 'type' => 'php', 'source' => $sources['resolvers'] . 'customevents.resolver.php', ], + [ + 'type' => 'php', + 'source' => $sources['resolvers'] . 'staleevents.resolver.php', + ], ] ] ); @@ -142,6 +146,21 @@ function getSnippetContent($filename = '') { $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($plugins).' plugins.'); flush(); unset($plugins,$plugin,$attributes); +/* add snippets */ +$snippets = include $sources['data'] . 'transport.snippets.php'; +if (!is_array($snippets)) { $modx->log(modX::LOG_LEVEL_FATAL,'Adding snippets failed.'); } +$attributes= [ + xPDOTransport::UNIQUE_KEY => 'name', + xPDOTransport::PRESERVE_KEYS => false, + xPDOTransport::UPDATE_OBJECT => true, +]; +foreach ($snippets as $snippet) { + $vehicle = $builder->createVehicle($snippet, $attributes); + $builder->putVehicle($vehicle); +} +$modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($snippets).' snippets.'); flush(); +unset($snippets,$snippet,$attributes); + /* now pack in the license file, readme and setup options */ $builder->setPackageAttributes([ diff --git a/_build/data/transport.snippets.php b/_build/data/transport.snippets.php new file mode 100644 index 0000000..7f87c7b --- /dev/null +++ b/_build/data/transport.snippets.php @@ -0,0 +1,13 @@ +newObject('modSnippet'); +$snippets[0]->fromArray([ + 'id' => 1, + 'name' => 'mpField', + 'description' => 'Returns click-to-field data attributes for a resource field or TV.', + 'snippet' => getSnippetContent($sources['snippets'] . 'mpfield.snippet.php'), + 'category' => 0, +], '', true, true); + +return $snippets; diff --git a/_build/events/events.magicpreview.php b/_build/events/events.magicpreview.php index 2d87a8e..3aa0d85 100644 --- a/_build/events/events.magicpreview.php +++ b/_build/events/events.magicpreview.php @@ -7,7 +7,7 @@ 'OnDocFormSave', 'OnLoadWebDocument', 'OnManagerPageBeforeRender', - 'OnWebPagePrerender', + 'ContentBlocks_BeforeParse', 'ContentBlocks_AfterParse', ]; diff --git a/_build/resolvers/staleevents.resolver.php b/_build/resolvers/staleevents.resolver.php new file mode 100644 index 0000000..909d76b --- /dev/null +++ b/_build/resolvers/staleevents.resolver.php @@ -0,0 +1,41 @@ + false, so + * dropping an event from the package does not delete an existing row. Without + * this, an upgraded install keeps firing the MagicPreview plugin on every + * front-end page render for OnWebPagePrerender, which no longer has a handler. + * + * @var modX $modx + * @var modTransportPackage $transport + * @var array $options + */ +if ($transport->xpdo) { + $modx = $transport->xpdo; + + $staleEvents = [ + 'OnWebPagePrerender', + 'OnWebPageComplete', + ]; + + switch ($options[xPDOTransport::PACKAGE_ACTION]) { + case xPDOTransport::ACTION_UPGRADE: + $plugin = $modx->getObject('modPlugin', ['name' => 'MagicPreview']); + if ($plugin) { + foreach ($staleEvents as $eventName) { + $pluginEvent = $modx->getObject('modPluginEvent', [ + 'pluginid' => $plugin->get('id'), + 'event' => $eventName, + ]); + if ($pluginEvent) { + $pluginEvent->remove(); + } + } + } + + break; + } +} +return true; diff --git a/core/components/magicpreview/docs/changelog.txt b/core/components/magicpreview/docs/changelog.txt index efa262d..b7942d9 100644 --- a/core/components/magicpreview/docs/changelog.txt +++ b/core/components/magicpreview/docs/changelog.txt @@ -1,3 +1,15 @@ +MagicPreview 1.8.0-pl +--------------------- +Released on 2026-08-14 + +- Fix click_to_field breaking Fenom (and any other custom parser) output in previews. MagicPreview no longer replaces the MODX parser during a preview render. (#55) +- BREAKING: core resource fields and TVs are no longer marked clickable automatically. Mark them in your template with the new mpField snippet, e.g.

[[*pagetitle]]

+- Add mpField snippet, usable as a MODX tag or a Fenom modifier ({'pagetitle'|mpField}) +- Add [[+mpClickToFieldAttributes]] / {$mpClickToFieldAttributes} placeholder for ContentBlocks field templates, so the attributes can go on your own element instead of an added wrapper. This also gives @PDO_FILE field templates a click target for the first time. +- ContentBlocks fields keep their automatic wrapper unless the template uses the placeholder +- Strip the ContentBlocks placeholder on every save, not only during a preview, so it is never stored in the resource content +- Only replace the parser during preview generation when ContentBlocks marking actually needs it + MagicPreview 1.7.1-pl --------------------- Released on 2026-07-10 diff --git a/core/components/magicpreview/docs/readme.txt b/core/components/magicpreview/docs/readme.txt index 7509dd4..8f6cbb3 100644 --- a/core/components/magicpreview/docs/readme.txt +++ b/core/components/magicpreview/docs/readme.txt @@ -161,3 +161,39 @@ Support & Documentation ----------------------- Full documentation is available at: https://www.modmore.com/extras/magicpreview/ + +Click to Field +-------------- + +With the magicpreview.click_to_field system setting enabled, clicking part of the +preview scrolls the resource form to the matching field. + +MagicPreview does not guess where your fields are rendered - you mark them. + +Core resource fields and TVs, with the mpField snippet: + +

[[*pagetitle]]

+
[[*mytv]]
+ +With pdoTools/Fenom, any of these work: + +

{$_modx->resource.pagetitle}

+

'pagetitle']}>...

+ +ContentBlocks fields are wrapped automatically and need no changes. To place the +attributes on your own element instead, use the placeholder - the wrapper is then +skipped for that field: + +

[[+value]]

+

{$value}

+ +ContentBlocks field templates using @CHUNK are a special case: parse() passes the +field data to the chunk as properties, so write the attributes directly: + +

+ +The snippet and placeholder produce nothing outside a manager preview, so they are +safe to leave in live templates and do not appear on public share links. Outside a +preview the ContentBlocks placeholder is removed as the field is parsed, so it is +not stored in the resource content either. The @CHUNK form above is the exception: +its attributes are written by hand and are always present in the saved content. diff --git a/core/components/magicpreview/elements/plugins/magicpreview.plugin.php b/core/components/magicpreview/elements/plugins/magicpreview.plugin.php index 517cb44..136ed20 100644 --- a/core/components/magicpreview/elements/plugins/magicpreview.plugin.php +++ b/core/components/magicpreview/elements/plugins/magicpreview.plugin.php @@ -14,6 +14,8 @@ define('MAGICPREVIEW_FILTER_ALLOW', 'Allow Listed Only'); define('MAGICPREVIEW_RESOURCE_ENABLED', 'Yes'); define('MAGICPREVIEW_RESOURCE_DISABLED', 'No'); + define('MAGICPREVIEW_CB_PLACEHOLDER_MODX', '[[+mpClickToFieldAttributes]]'); + define('MAGICPREVIEW_CB_PLACEHOLDER_FENOM', '{$mpClickToFieldAttributes}'); } $path = $modx->getOption('magicpreview.core_path', null, $modx->getOption('core_path') . 'components/magicpreview/'); @@ -266,17 +268,15 @@ $service->applyPreviewData($modx->resource, $data); } - if ($modx->getOption('magicpreview.click_to_field', null, false)) { - // No restoration needed — the request ends after the page is rendered. - $modx->getParser(); - if (!class_exists('MagicPreviewCoreParser', false)) { - require_once $service->config['modelPath'] . 'magicpreview/MagicPreviewCoreParser.class.php'; - } - $modx->parser = new MagicPreviewCoreParser($modx); - + if ($service->isClickToFieldActive()) { $modx->regClientStartupHTMLBlock('