A collection of powerful Twig filters for modifying HTML
Clone or download

README.md

Retcon 2 plugin for Craft CMS 3.x

Retcon is a tiny Craft CMS plugin adding a series of powerful Twig filters for modifying HTML content. Here are some of the things Retcon can do:

  • Add attributes (e.g. class="foobar", style="color:red;" or data-foo)
  • Append values to existing attributes
  • Remove attributes completely (e.g. removing all inline style attributes)
  • Transform inline images (it even uses Imager, if installed)
  • Add srcset or lazyloading to inline images (again, using Imager if installed)
  • Remove or unwrap DOM nodes
  • Wrap DOM nodes (e.g. wrap all <span> tags in a <p>)
  • Extract DOM nodes (e.g. remove everything except <img> tags)
  • Inject strings or HTML content
  • Change tag names (e.g. change all occurrences of <p> to <span>, or change div.foobar to p.foobar)

...and much more!

Requirements

This plugin requires Craft CMS 3.0.0-RC1 or later.

Installation

To install the plugin, follow these instructions.

  1. Open your terminal and go to your Craft project:

cd /path/to/project

  1. Add Retcon as a dependency to your project using Composer:

composer require mmikkel/retcon

  1. In the Control Panel, go to Settings → Plugins and click the “Install” button for Retcon, or use the craft executable:

./craft install/plugin retcon

Alternatively, Retcon can be installed from the Craft CMS Plugin Store inside the Craft Control Panel.

This plugin will be free forever.

How does it work?

Retcon uses PHP's native DOMDocument class to parse and modify HTML. Additionally, Masterminds' HTML5 library is used for HTML5 support, and Symfony's DomCrawler and CssSelector components are used to enable the powerful jQuery-like selector syntax.

Changes in Retcon 2.x

Except for the replace method being removed, Retcon 2.x should be completely backwards compatible with Retcon 1. These are the big changes:

  • Symfony's DomCrawler and CssSelector components have been added for much more powerful selector capabilities (almost all CSS selectors work). Retcon is basically jQuery now! Note: all existing selectors in your code will still work, if you're upgrading from Retcon 1.x.

  • Retcon now has full HTML5 support, courtesy of Masterminds.

  • The autoAlt method now uses the Asset's title attribute for the alt attribute, if Retcon is fed HTML with Craft CMS reference tags.

  • The transform, lazy and srcset now takes an optional $selector parameter ('img' by default)

  • The lazy method has new default values for its $className and $attributeName parameters. The new defaults are 'lazyload' and 'src' (i.e. 'data-src'). This change is done to mirror the defaults in the popular lazysizes library.

  • The new removeEmpty method removes empty nodes (i.e., nodes that have no text content). Those pesky clients hitting Enter again and again won't know what hit them.

  • The replace method has been removed. So sorry, it just didn't belong...

Basic usage

Retcon exposes a series of different methods for modifying HTML. Most methods take a selector parameter (i.e. the selector(s) for the elements you want to modify, e.g. 'img', 'p' or 'div.foobar'), and some take additional parameters for further configuration.

Note that it doesn't matter if your HTML is from a WYSIWYG field (Redactor or CK Editor) or just a regular ol' string. If it's HTML, Retcon will eat it.

Twig filters

All of Retcon's methods are exposed as Twig filters, which is how Retcon is primarily meant to be used. For example, if you wanted to add a classname 'image' to all images in a Redactor field called body, here's how that'd look:

{{ entry.body | retconAttr('img', { class: 'image' }) }}

Note that for the Twig filters, the prefix retcon is added to the method name – i.e. the attr method becomes the retconAttr filter, the transform method becomes the retconTransform filter, etc.

Filter tag pair

For use cases where your HTML is not in a field or variable, the filter tag pair syntax works nicely – the following example adds rel="noopener noreferrer" to all <a> tags with target="_blank":

{% filter retconAttr('a[target="_blank"]:not([rel])', { rel: 'noopener noreferrer' }) %}
    {# A whole bunch of HTML in here #}
    ....
{% endfilter %}

Catch-all filter

Being Twig filters, chaining multiple Retcon methods will of course work:

{{ entry.body | retconChange('h1,h2,h4,h5,h6', 'h3') | retconAttr('h3', { class: 'heading') }}

Another option is to use the "catch-all" filter retcon, which takes a single array containing the names of the methods you want to run in sequence, and their parameters:

{{ entry.body | retcon([
    ['change', 'h1,h2,h4,h5,h6', 'h3'],
    ['attr', 'h3', { class: 'heading' }]
]) }}

PHP

If you want to use Retcon in a Craft plugin or module, all methods are also available through the mmikkel/retcon/Retcon::$plugin->retcon service (note that unlike the Twig filters, the retcon prefix is missing from the service method names – in other words, retconAttr is just attr):

use mmikkel\retcon\Retcon;
echo Retcon::$plugin->retcon->attr($entry->body, [ 'class' => 'image' ]);

For an actual use case example; here's how the rel="noopener noreferrer" example could look in a module (basically, the below code would add rel="noopener noreferrer" automatically to all <a target="_blank"> tags in your templates (unless they've already got a rel attribute set, of course):

use mmikkel\retcon;

public function init() {

    Event::on(
        View::class,
        View::EVENT_AFTER_RENDER_TEMPLATE,
        function (TemplateEvent $event) {
            if (!Craft::$app->getRequest()->getIsSiteRequest()) {
                return;
            }

            if ($event->output && Craft::$app->getPlugins()->getPlugin('retcon')) {
                $event->output =
                    Retcon::$plugin->retcon->attr(
                        $event->output,
                        'a[target="_blank"]:not([rel])', [
                            'rel' => 'noopener noreferrer',
                        ]);
            }

        }
    );
}

Selectors

A "selector" in Retcon is the same thing as a selector in CSS – i.e. something like 'img', '.foo' or h3 + p.

In Retcon 2.x, almost all CSS selectors will work – see the CssSelector docs for further details on selectors.

Multiple selectors

Multiple selectors can be defined as a comma-separated string (i.e. 'p, span') or as an array (i.e. ['p', 'span']).

Methods

**NOTE/TODO: The Wiki is written for the v. 1.x branch and hasn't been updated yet. No biggie, basically all methods will work the same as before, except for the lazy, transform and srcset methods which have been granted an additional parameter $selector (third parameter for transform and srcset; second parameter for lazy). **

Also, a wiki for the removeEmpty method hasn't been created yet, but it looks like this:

{{ entry.body | retconRemoveEmpty }}

to remove all empty nodes, or

{{ entry.body | retconRemoveEmpty('p') }}

to just remove empty paragraph tags.

transform Apply a named or inline image transform to all images. If installed, Retcon uses Imager to apply the transform.

srcset Apply an array of named or inline image transform to all images, for simple srcset support. If installed, Retcon uses Imager to apply the transforms.

lazy Replaces the src attribute of image tags with a transparent, base64 encoded SVG (retaining the original image's aspect ratio); putting the original src URL in a data-attribute

autoAlt Adds Asset title or filename as alternative text for images missing alt tags

attr Adds, replaces, appends to or removes a set of attributes and attribute values – e.g. class. Can be used to remove inline styles.

renameAttr Renames existing attributes for matching selectors, retaining the attribute values

wrap Wraps stuff in other stuff (e.g. put all <span> tags in <p> tags)

unwrap Removes parent node for matching elements; retaining their content

remove Removes all elements matching the given selector(s)

removeEmpty Removes all empty elements. NEW

only Removes everything but the elements matching the given selector(s)

change Changes tag type for all elements matching the given selector(s)

inject Inject strings or HTML

Disclaimer & support

Retcon is provided free of charge. The author is not responsible for data loss or any other problems resulting from the use of this plugin. Please see the Wiki page for documentation and examples. and report any bugs, feature requests or other issues here. As Retcon is a hobby project, no promises are made regarding response time, feature implementations or bug amendments.