おまじないみたいなもので、今更ほぼ意味がない気もしますが、メールアドレスを入力したあと、それを antispambot にかけたいというニッチ案件。本来はメールフォーム置いて、そこにリンクしとけという話。

サンプルは記事の最後にあります。

Gutenberg の registerFormatType() を使えばすぐ出来そうな感じだったのですが、実は既にフォーマットが設定されているタグ名は使えない=リンクに変換できない、というかエラーが出てしまうので、ひとまずカスタム要素を作成して対応するサンプル。

Edge で動くのかわからないですけども、既存の要素を継承するタイプでなければ動きそうなのと、レンダリングエンジンが Chromium になったのと in Development らしいのでいつかは動く。

管理側の問題なので、個人サイトでは全く問題ないということにしよう。

ブロックエディタ用のスクリプトを書く

imade-antispam-email.js

新しいカスタムHTML要素を定義

class ImadeAntispamEmail extends HTMLElement {
    constructor() {
        super();
        
        // シャドウルート: Edge未対応
        var shadow = this.attachShadow({mode:'open'});
        var address = this.innerText;
        var content = this.innerHTML;

        // このあたりでメールアドレスの妥当性チェック

        var mailto = 'mailto:' + address;
        var wrapper = document.createElement('a');
        wrapper.setAttribute('href', mailto);
        wrapper.innerHTML = content;

        this.addEventListener('click', function(e){
            e.preventDefault();
        });

        shadow.appendChild(wrapper);
    }
}

customElements.define('imade-antispam-email', ImadeAntispamEmail);

これで、見た目 mailto リンクに見える <imade-antispam-email> 要素が定義されるようになります。不思議。

本当はブロックのコメントの wp:paragraph みたいに imade:antispam-email みたいな感じで名前空間持ちの要素を作ってみたかったのだけど、その場合は当然名前空間の定義が必要になるので、シンプルにカスタム要素追加したほうがよさそうということになりました。

トグルボタンを作ってフォーマットタイプを登録

これは Handbook のコードのまんまです。regiserFormatTypetagName に上で定義した imade-antispam-email 要素を指定します。

(function(wp){

    var antiSpamEmailButton = function(props) {
        return wp.element.createElement(
            wp.editor.RichTextToolbarButton,
            {
                icon: 'email',
                title: 'Email',
                onClick: function() {
                    props.onChange( wp.richText.toggleFormat(
                        props.value,
                        { type: 'imade/antispam-email' }
                    ) );
                },
                isActive: props.isActive
            }
        );
    };

    wp.richText.registerFormatType(
        'imade/antispam-email',
        {
            'title': 'Email',
            'tagName': 'imade-antispam-email',
            'className': null,
            'edit': antiSpamEmailButton
        }
    );

})(window.wp);

これでツールバーの「よりリッチなテキスト制御」にボタンが追加されます。

WordPress プラグインを書く

サイト上での表示は、流石にエディタと同様にカスタム要素を定義して、というわけにはいかないので、ショートコードAPIを使います。カスタム要素からショートコードへの変換は the_content フィルタで <> を [] に書き換えるだけなので非常に簡単です。

<?php
/*
Plugin Name: Imade Antispambot Email Format for Gutenberg
*/

// Silence is golden.
if ( !defined( 'ABSPATH' ) ) exit;

class Imade_AntispambotEmailFormat {
    public static $instance = null;
    public $customElement = 'imade-antispam-email';

    public static function hasInstance() {
        $obj = static::$instance;
        return ( ( $obj !== null ) && is_object( $obj ) && is_a( $obj, static::class ) );
    }

    public static function createInstance() {
        if ( !static::hasInstance() ) {
            static::$instance = new static();
        }
    }

    public function __construct() {
        add_action( 'plugins_loaded', [ $this, 'plugins_loaded' ] );
    }

    public function plugins_loaded() {
        add_action( 'init', [ $this, 'action_init' ] );
        add_action( 'enqueue_block_editor_assets', [ $this, 'action_enqueue_block_editor_assets' ] );
        add_filter( 'the_content', [ $this, 'filter_the_content' ] );
    }

    public function action_init() {
        add_shortcode( $this->customElement, [ $this, 'shortcode_imade_antispam_email' ] );
    }

    public function action_enqueue_block_editor_assets() {
        wp_enqueue_scripts(...);
    }

    // 設定した imade-antispam-email 要素をショートコードタグに変換
    public function filter_the_content( $content ) {
        return preg_replace( '/<(' . $this->customElement . ')([^>]+)?>(.+)<\/\1>/is', '[$1$2]$3[/$1]', $content );
    }

    // imade-antispambot-email ショートコード
    public function shortcode_imade_antispam_email( $atts, $content ) {
        //... shortcode_atts() など

        // $mailto = 'mailto:' . strip_tags( $content ); など
        // $content = antispambot( $content ); など
        // $mailto = antispambot( $mailto ); など
 
       return '<a href="' . $mailto . '">' . $content . '</a>';
    }
}

if ( !Imade_AntispambotEmailFormat::hasInstance() ) {
    Imade_AntispambotEmailFormat::createInstance();
}

これのよいところは、カスタム要素に対応していないブラウザでの編集の場合はもう、インラインショートコードをそのまま使えばいいってところです。ifCondition が使えるのではと思います。

[imade-antispambot-email]you@example.com[/imade-antispambot-email]

冗長なので、別途 email みたいなシンプルな名前のショートコードを作成して、そのショートコードに、或いはそのショートコードから、もう一方へ do_shortcode でパイプするような感じがいいと思います。

ブロックエディタのショートコードブロックはブロックなので、インライン(文中)でのショートコードは使われなくなる実装なんですよね。インラインでのショートコードも、今回の例のようなメールアドレスや何かしらの置換など、ちょっとしたところで使うのに便利だったんですけど。

実際の表示サンプル

ページ上の見栄え

メールは you@example.com までお願いします。

HTMLソース

antispambot() は、アクセスごとに文字参照に変換する場所が変わる。

エディタ上の見栄え(Chrome)

Emailのトグルボタン

適用後のエディタ上での見た目

このサイトでは、プラグインではなく、テーマにビルトインしています。いずれ分離しようと思います。