アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

WordPress プラグインでエディタにボタンを追加する 4

このシリーズの 3 でメディアボタンからのショートコード挿入を取り上げたのだが、その記事に寄せられたコメントによって編集モードがビジュアルの時に正しく動作しないことがわかった。今回はこの問題の調査結果と正しい実装についてまとめたい。

問題の原因

前回の記事ではショートコードを編集画面のテキストエリアに埋め込む方法として、以下の関数をページ本体に埋め込み

<script type="text/javascript">
//<![CDATA
function onMyPluginShortCode( value ) { edInsertContent( edCanvas, value ); }
//]]>
</script>

メディアボタンから表示されたダイアログ側のインクルードする editor.js 内の以下のメソッドで呼び出すようにしてた。

var WpMyPlugin = function() {
    // ...中略

    /**
     * 「投稿に挿入」ボタンが押された時に発生します。
     */
    this.onClickSubmitButton = function() {
        self.parent.onMyPluginShortCode( getShorCode() );
        self.parent.tb_remove();
    };
};

編集モードがテキストならばこれで動作する。ビジュアル モードにも edInsertContentedCanvas が定義されているが、これらを使用するとエラーが発生する。Firebug のコンソールを見ると NS_ERROR_FAILURE と出ていた。これは Firefox の内部エラーのようなので原因調査は大変そうだ。よって方法そのものの見直しを検討する。

media-upload.js の実装

WordPress 標準の画像挿入はビジュアルとテキストの両方に対応できているので、そのコードを読む。はじめからこうしておくべきだった。反省。

WordPress ディレクトリ内 wp-admin/js/media-upload.js が画像のショートコード挿入処理を担当している。挿入はこの中に定義されている send_to_editor という関数を利用している。これを読めば適切な処理を学べそうだ。ただしスクリプトは難読化されている。空白とインデントは除去され変数名も a や b になっているので読める形に直してみる。ついでにコメントも付けてみた。

/**
 * 編集画面のエディタにテキストを送信します。
 *
 * @param text テキスト。
 */
function send_to_editor( text ) {
    // ビジュアルモード
    var editor;
    if( typeof tinyMCE != "undefined" && ( editor = tinyMCE.activeEditor ) && !editor.isHidden()) {
        editor.focus();
        if( tinymce.isIE ) {
            editor.selection.moveToBookmark( tinymce.EditorManager.activeEditor.windowManager.bookmark );
        }

        if( text.indexOf( "[caption" ) === 0 ) {
            if( editor.plugins.wpeditimage ) {
                text = editor.plugins.wpeditimage._do_shcode( text );
            }
        } else {
            if( text.indexOf( "[gallery" ) === 0 ) {
                if( editor.plugins.wpgallery ) {
                    text = editor.plugins.wpgallery._do_gallery( text );
                }
            } else {
                if( text.indexOf( "[embed" ) === 0 ) {
                    if( editor.plugins.wordpress ) {
                        text = editor.plugins.wordpress._setEmbed( text );
                    }
                }
            }
        }

        editor.execCommand( "mceInsertContent", false, text );
    } else {
        // テキストモード
        if( typeof edInsertContent == "function" ) {
            edInsertContent( edCanvas,text );
        } else {
            $( edCanvas ).val( $( edCanvas ).val() + text );
        }
    }

    // ダイアログを閉じる
    tb_remove();
}

この関数をプラグインから呼び出すようにして Firebug でステップ実行してみる。するとビジュアルモードはエディタに TinyMCE を使用しており、テキスト モードは edInsertContent になることが分かる。つまりビジュアルとテキストに両対応する場合は、この関数に相当する処理が必要となる。18 ~ 44 行目は画像挿入に固有のものなので不要だが、その他の部分はそのまま使えそうだ。

サンプル プログラム

プラグインで対応するなら前述の関数を自前で実装するか send_to_editor を利用することになる。

自前で実装すると media-upload.js へ依存しなくて済むメリットがある。しかしメディア ボタンからダイアログを表示している時点で画像挿入機能に依存しているから、その一部である media-upload.js へ依存しても別によいのでは?と思う。そんなわけで send_to_editor を利用するように修正したサンプルを作成してみた。手元の環境ではビジュアル、テキストのどちらの編集モードでも正しく動いている。

WP-MyPlugin という名前の WordPress プラグインとして実装しているので、プラグイン ディレクトリにアップロードして有効化する事でメディア ボタンの動作を確認できる。

最後にこの記事を書くきっかけとなるコメントを寄せてくれた zzrgtr さんへ感謝しておきたい。ありがとうございました。