OXY NOTES

PukiWikiをビジュアルエディタ(TinyMCE)で編集できるプラグインを作成

GUIエディタのTinyMCEを利用できます

PukiWikiを編集するには独自の記述方法を覚える必要があります。
覚えると言っても簡単なものですが、それでもWikiの管理者以外はわからないというのが普通です。

編集に参加したいけど記述方法がわからない」という利用者も多数存在します。
Wikiの場合、そうした層を取り込めるかどうかが、サイトの質に直結します。
そんなときに求められるのがリッチエディタ(WYSIWYGエディタ)です。

かつてはPukiWiki Plus!用にguieditというプラグインがありました。
ただし本体であるPukiWiki Plus!は開発を終了し、プラグイン配布サイトも閉鎖されています。入手できたとしても、脆弱性のある古いタイプのCKEditorを利用しているため、そのまま利用を続けるのは非常に危険です。

そこで今回、前出のguieditを元に、最新のTinyMCEを利用したtinymceeditというプラグインを作成しました。
なぜCKEditorではなく、TinyMCEを採用したかですが、WordPress等で採用されるなど脚光を浴びている点もありますが、どうせ作り直すなら独自色を出したいという色気ですw


更新履歴

0.1.1 プラグインを書き出す際のEntity処理を追加(&noteが正しく出力できない問題の対策)
0.1.2 マルチラインプラグインの改行が勝手に削除される問題に対処
0.1.3 注釈ボタンを追加
   改行時にP要素が追加される仕様から<br />へ変更
   改行の扱いを厳密なものからエディタの見た目を反映するものに変更
   インライン要素内での改行ボタンを削除
   preg_replaceのe修飾子の廃止(PHP7対策)
   unexpected ‘new’ (T_NEW)対策(PHP7対策)
0.1.4 wiki2xhtml.phpのunexpected ‘new’ (T_NEW)対策(PHP7対策)

現在公開停止中


tinymceeditを導入する方法

このプラグインはPukiWikiのページをXHTML形式に変換したり、Wiki形式に戻したりという、大掛かりなものです。
また、ビジュアルエディタはバージョンやOS・ブラウザによっても動作が異なります。

全ての環境で検証を行うのは不可能なので安定動作する環境を以下の通りに限定させていただきます。

前提条件

1.動作するのは公式のPukiWikiのみ

このプラグインはPukiWikiでのみ動作します。PukiWiki Plus!はサイトが閉鎖されており、動作確認ができないためサポートしません。

1.ブラウザはWindows 10のChromeのみ

ブラウザはWindows 10のChromeのみ検証。ブラウザの差異まで吸収するとなると膨大な時間がかかるため。(有志の方による、より良いコードの提案は受け付けています)

1.マルチラインプラグインが有効

pukiwiki.ini.phpにある「PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK」の項目は「0」で、マルチラインプラグインを有効にしていること。

1.改行の扱いは$line_breakで有効

上と同じくpukiwiki.ini.phpにある「$line_break」の項目が「1」で有効になっていること。
改行が全て無視されるというオリジナルの仕様は明らかにおかしいため。また、ほとんどのサイトで有効になっているため。

1.以下の変更を自分のサイトに合わせて応用できること

このプラグインはPukiWiki本体のファイルを編集する必要があります。skinなど、構成はサイトごとに異なります。そのため、この解説はあくまで一例です。自分のサイト合わせて応用してください。

ファイルのダウンロード

以下のリンクからファイルをダウンロード、解凍してください。
「公開停止中」

pluginフォルダの以下の3点をPukiWikiのpluginフォルダへ移動

skinフォルダのtinymceフォルダをskinフォルダへフォルダごと移動

PukiWikiの以下のファイルを編集

lib/html.php

62行目あたり

$_LINK['edit']     = "$script?cmd=edit&amp;page=$r_page";

以下のように追加。

$_LINK['edit']     = "$script?cmd=edit&amp;page=$r_page";
$_LINK['tinymceedit']     = "$script?cmd=tinymceedit&amp;page=$r_page";

286行目あたり以下の部分を。

$s_postdata  = htmlsc($refer . $postdata);

以下のように編集。

// TinyMCEにより自動でEntity化してしまうため振り分ける
if ( $vars['cmd'] == "edit" ){
	$s_postdata  = htmlsc($refer . $postdata);
} elseif ( $vars['cmd'] == "tinymceedit" ){
	$s_postdata  = $refer . $postdata;
}

308行目あたりの上に

// 'margin-bottom', 'float:left', and 'margin-top'

以下のように追加。

// 通常のeditとtinymceeditによって振り分けを行う。
if ( $vars['cmd'] == "edit" ){
	$cmd = "edit";
} elseif ( $vars['cmd'] == "tinymceedit" ){
	$cmd = "tinymceedit";
}

// 'margin-bottom', 'float:left', and 'margin-top'

256行目あたりのヘッダー部分を編集

	$body = <<<EOD
<div class="edit_form">
 <form action="$script" method="post" style="margin-bottom:0px;">
$template
  $addtag
  <input type="hidden" name="cmd"    value="edit" />
  <input type="hidden" name="page"   value="$s_page" />
  <input type="hidden" name="digest" value="$s_digest" />
  <textarea name="msg" rows="$rows" cols="$cols">$s_postdata</textarea>
  <br />
  <div style="float:left;">
   <input type="submit" name="preview" value="$btn_preview" accesskey="p" />
   <input type="submit" name="write"   value="$_btn_update" accesskey="s" />
   $add_top
   $add_notimestamp
  </div>
  <textarea name="original" rows="1" cols="1" style="display:none">$s_original</textarea>
 </form>
 <form action="$script" method="post" style="margin-top:0px;">
  <input type="hidden" name="cmd"    value="edit" />
  <input type="hidden" name="page"   value="$s_page" />
  <input type="submit" name="cancel" value="$_btn_cancel" accesskey="c" />
 </form>
</div>
EOD;

以下のように編集。

$body = <<<EOD
<div class="edit_form"><form style="margin-bottom: 0px;" action="$script" method="post">$template
$addtag
<input name="cmd" type="hidden" value="$cmd" />
<input name="page" type="hidden" value="$s_page" />
<input name="digest" type="hidden" value="$s_digest" />
<textarea id="tinymce" cols="$cols" name="msg" rows="$rows">$s_postdata</textarea>

<textarea id="tinymce" style="display: none;" cols="1" name="original" rows="1">$s_original</textarea>
<div style="float: left;"><input accesskey="p" name="preview" type="submit" value="$btn_preview" />
<input accesskey="s" name="write" type="submit" value="$_btn_update" />
$add_top
$add_notimestamp</div>
</form><form style="margin-top: 0px;" action="$script" method="post"><input name="cmd" type="hidden" value="$cmd" />
<input name="page" type="hidden" value="$s_page" />
<input accesskey="c" name="cancel" type="submit" value="$_btn_cancel" /></form></div>
EOD;

ja.lng.phpの編集(必要であればen.lng.phpも編集)

メニューの表示ですがGUIエディタを利用するユーザー層を想像すると、「WYSIWYGエディタ」や「リッチテキストエディタ」と書いても何のことか理解しにくいのではないでしょうか。
同じ理由で「GUIエディタ」というのも向いていないと思います。
ということで、人気のCMS WordPressで採用されている「ビジュアルエディタ」という文言を採用しました。このへんはサイトの利用者層によって、自由に設定してください。

109行目あたり

$_LANG['skin']['edit']      = 'ページの編集';

以下を追加。

$_LANG['skin']['edit']      = 'ページの編集';
$_LANG['skin']['tinymceedit']      = 'ビジュアルエディタで編集';

skin/pukiwiki.skin.phpを編集

まずはheadの最後あたりにjQueryを追加します。(既に読み込んでいる場合は不要。jqueryは2系でも動作するはず)

</head>

以下のように追加

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>

例として、GUIエディタを追加したい場所に追加。(アイコンも変化をつけたい場合は用意した画像に差し替えてください)

$_IMAGE['skin']['edit']     = 'edit.png';

以下のように追記。

$_IMAGE['skin']['edit']     = 'edit.png';
$_IMAGE['skin']['tinymceedit']     = 'edit.png';

あとは表示したい部分に「_navigator(‘tinymceedit’)」を追加するだけです。例としてヘッダーに追加するには、129行目あたり

<?php _navigator('edit') ?> |

以下のように追記。

<?php _navigator('edit') ?> |
<?php _navigator('tinymceedit') ?> |

メニュー(toolbar)に表示するには244行目あたり、

<?php _toolbar('edit') ?>

以下のように追記。

<?php _toolbar('edit') ?>
<?php _toolbar('tinymceedit') ?>

以上で動作するはずです。以下はPukiWiki1.5.1をインストールした直後に上の変更を加えた表示サンプル。


サポートしている書式

PukiWikiで採用されている基本的な記法はサポートしています。
具体的には以下のとおりです。

段落

~ インライン要素

引用文

> インライン要素

リスト構造

- インライン要素

リスト構造

+ インライン要素

表組み

| インライン要素 | インライン要素 |

CSV形式の表組み

,データ,データ,…

見出し

*、**、*** の3段階

左寄せ・センタリング・右寄せ

LEFT:インライン要素
CENTER:インライン要素
RIGHT:インライン要素

水平線

----

スマイルマーク

現状使う人がいるかも疑問ですが、一応オリジナルと同じスマイルマークが選べるようにボタンを実装しました。

&smile; など

行間開け

#br

改行の仕様は非常に迷いました。

前提としてTinyMCEの仕様上、改行すると段落が作られ、pタグで囲まれます。

反面、PukiWikiの仕様は段落内の改行を無視します。例えば以下のような文章を打つと、

hoge
hoge
hoge

hoge
hoge

以下のように表示されます。

hoge hoge hoge

hoge hoge

改行が2つ連続した場合だけ、別の段落として扱われていることがわかると思います。

この仕様はさすがに現実離れしたものなので、ほとんどのサイトが「pukiwiki.ini.php」の「$line_break」の設定を「1」に変更しています。
この状態だと上の編集画面の文章が以下のように改行されます。

hoge
hoge
hoge

hoge
hoge

このプラグインでも「$line_break」が「1」の場合を想定しています。

このような想定でプラグインを作ったのですが、実際にTinyMCEエディタで編集をしていると、様々な問題があることがわかりました。

エディタの見た目と、PukiWikiの表示が異なる

TinyMCE上で新しい要素を追加すると、全て段落(P要素)として書き出されます。
プラグインやインライン要素も段落として書き出されるため、通常の改行とは異なり、行間が空きます。

しかしこの状態ではP要素が2つだけなので、文章上は以下のように改行はありません。

<p>テストテストテスト</p>
<p>てすとてすとてすと</p>

これをPukiWikiの文章に直すと以下のようにります。

テストテストテスト
てすとてすとてすと

これだとPukiWiki上の見た目は段落内の改行と解釈されるため、実際の表示は以下のようになります。

さらにこれをTinyMCEのエディタで表示すると以下のように、同じ段落内で改行した文章になります(行間のスペースが無くなる)。

以上のようにHTMLの構文的に正しく解釈しようとすると、GUIエディタ上の見た目と、実際の表示が一致しません。
それだけでなく、GUIエディタで再度開いた時にも表示が一致しません。

このような問題の対策として、以下の仕様に決定しました。

TinyMCE上でEnterを押すと新しい段落として書き出されないようにする

TinyMCEのforce_br_newlinesやforce_p_newlinesを無効にして、Enterが段落内でのbr要素として書き出されるように変更しました。

こうすることで改行が1つの時は段落内でのbr要素、改行が2つの場合は別の段落として振り分けることができます。(TinyMCEエディタ上はただの改行1つと2つの違い)

段落が連続した場合は、改行が連続したのと同じ扱いにする

こうすることで、エディタとPukiWikiの表示が異なるという問題が解決できます。

ただしデメリットもあります。それは改行の数が元のPukiWikiの文章と異なることです。
TinyMCEとPukiWiki、どちらに厳密にしても不具合が出るため、折衷案といった感じです。

見出し要素やプラグイン要素、リスト要素の改行の入れ具合などは、以上の点を考慮しつつ個人的な好みで決めました。細かくテストして煮詰めることも可能ですが、利用させていただいた元となったプラグインのコードが難解で、はっきり言って解析するのに疲れましたwおそらく同じ問題に対応するため付け焼刃的に追記をしたのだろうなと苦慮した様がうかがい知れます。もっと良いコードがあるよという方がいたら教えてください。

添付ファイル・画像の貼り付け(プラグイン用の記述)

通常、画像の貼り付けにはrefプラグインを使うと思います。
今回、プラグインは独自のボタンを設置しました。
以下のプラグインボタンをクリックすると追加することができます。

添付ファイルを追加するrefプラグインの場合。

1.プラグインボタンをクリック

2.「タイプ」で「インライン型」もしくは「ブロック型」を選びます。
ちなみにインライン要素は「&」ではじまり「;」で終わるもの(&size(12){ほげ};など)。ブロック要素は「#」で始まるもの。(#brなど)

3.「プラグイン名」はそのままプラグインの名前。refプラグインなら「ref」といった具合。

4.「オプション」はプラグインのオプションを設定します。いわゆる、プラグインに渡す引数の部分です。refプラグインなら「sample.jpg,around,nolink,400×300」など

5.「インライン要素」は少し特殊で、インライン要素を示す「{}」の部分から記述する必要があります。「&size(12){ほげ};」の場合は「{ほげ}」といった具合に記述します。ちなみに最後の「;」は自動でつくので必要ありません。

追加したプラグインの項目を編集したい場合はプラグイン要素をダブルクリックしてください。再び編集用のウィンドウが開きます。またはプラグインを選択した状態でプラグインボタンをクリックすれば編集用のウィンドウが表示されます。

他にもエディタの機能で画像を表示したり、refプラグインの形式に変換して書き換えることも可能ですが、画像をページにアップロードするというPukiWikiの記述方法を考えると蛇足な気がするので実装しませんでした。
(プレビューを押せば確認できます)

強調・斜体

''インライン要素''、'''インライン要素'''

文字サイズ

&size(サイズ){インライン要素};

PukiWikiではpx指定が一般的なので、慣習にならっています。
実装しておいてなんですが、レスポンシブ時代に逆行するため、例外的な仕様に留めて、基本はCSSでの指定がおすすめです。ちなみに一番右端にあるボタン「書式をクリア」ボタンで指定を解除できます。

文字色

&color(文字色,背景色){インライン要素};

テキストの色」というプルダウンメニューから色を指定することができます。

指定できる色はTinyMCEで設定されている色だけです。

"000000", "Black",
"993300", "Burnt orange",
"333300", "Dark olive",
"003300", "Dark green",
"003366", "Dark azure",
"000080", "Navy Blue",
"333399", "Indigo",
"333333", "Very dark gray",
"800000", "Maroon",
"FF6600", "Orange",
"808000", "Olive",
"008000", "Green",
"008080", "Teal",
"0000FF", "Blue",
"666699", "Grayish blue",
"808080", "Gray",
"FF0000", "Red",
"FF9900", "Amber",
"99CC00", "Yellow green",
"339966", "Sea green",
"33CCCC", "Turquoise",
"3366FF", "Royal blue",
"800080", "Purple",
"999999", "Medium gray",
"FF00FF", "Magenta",
"FFCC00", "Gold",
"FFFF00", "Yellow",
"00FF00", "Lime",
"00FFFF", "Aqua",
"00CCFF", "Sky blue",
"993366", "Red violet",
"FFFFFF", "White",
"FF99CC", "Pink",
"FFCC99", "Peach",
"FFFF99", "Light yellow",
"CCFFCC", "Pale green",
"CCFFFF", "Pale cyan",
"99CCFF", "Light sky blue",
"CC99FF", "Plum"

取消線

%%インライン要素%%

アンダーライン

%%%インライン要素%%%

注釈

これもプラグインと同様に、専用のボタンを追加しました。
追加する際にはエディタのボタンをクリックし、編集する場合はエディタに表示された注釈画像をクリックしてください。

((インライン要素))

コメント

こちらも上の項目と同じです。コメント用のボタンと画像を用意しました。

//インライン要素

ルビ構造

&ruby(ルビ){インライン要素};

プラグイン対応の一部。

リンク

[[リンク名:URL]]、[[リンク先URL>リンク元テキスト]]

リンクにしたい文字列を選択した状態で「リンクボタン」をクリックするとリンク先URLが指定できます。

以上の書式に対応しています。
オリジナルのプラグインは上付き文字 下付き文字・添え字にも対応していましたが、PukiWikiではサポートされていないため削除しました。


TinyMCEエディタのテスト用文章

以下の文章をPukiWikiの通常のエディタで書き込んだ後、TinyMCEエディタで開き保存し直してください。
細かな改行などを除けば、しっかりと反映されるはずです。

この文章はPukiWikiの文章が正しくTinyMCEエディターに反映されるかをテストするもの。

*これは見出し(H2)

**これは見出し(H3)

***これは見出し(H4)

これは段落(P要素)
P要素内での改行

改行が2つ。これは別のP要素として書き出す。


改行が3つ。この場合は上の要素と同じでP要素として書き出される。(改行一つ文無視される)

~ チルダでの段落
段落内の改行

~ チルダでの段落
~ PukiWikiの文章上は改行一つでチルダを再指定(改行は一つだが、上の行とは別の段落として書き出される)

> 引用文のテスト

> 複数行の引用文のテスト
複数行の引用文のテスト

> 複数行の引用文のテスト
> 連続した引用文のテスト(上の行とは別の引用文として書き出される)

- マイナスによるリスト構造
- マイナスによるリスト構造

- マイナスによるリスト構造
-- リスト子供
--- リスト子供の子供

+ プラスによるリスト構造
++ リスト子供
+++ リスト子供の子供

| 2段表組み | 表組み |
| 表組み | 表組み |

| 3段表組み | 表組み | 表組み |
| 表組み | 表組み中の改行&br;表組み中の改行 | 表組み |

,CSV形式の表組み,表組み,

LEFT:左寄せ
CENTER:センタリング
RIGHT:右寄せ

水平線
----

スマイルマーク
&smile;
&bigsmile;
&huh;
&oh;
&wink;
&sad;
&heart;
&worried;

行間開け
#br
行間開け(↑この行にbr要素を入れている)

''強調''

'''斜体'''

&size(48px){文字サイズの変更};

&color(#ff0000){文字色のテスト(金赤:#ff0000)};

&color(,#0000ff){背景色のテスト(青:#0000ff)};

%%取り消し線のテスト%%

%%%アンダーラインのテスト%%%

((注釈のテスト))

文章の最後に注釈を入れる。((注釈のテスト2))

// コメントの追加(GUIエディタでは見えるが、実際には表示されない)

ルビ&ruby(ようそ){要素};のテスト

[[リンク名:URL]]
[[リンク先URL>リンク元テキスト]]

作成後記

冒頭で紹介した通り、このプラグインはPukiWiki Plus!のguieditというプラグインを元に作成しました。
この場を借りてお礼申し上げます。

PukiWiki用に移植するにあたり、一通りの書式は網羅したつもりです。
ただし、自分で宣言するのもなんですが、十分検証したとは言えません。

この段階で公開するのは早計かとも考えましたが、ひとまずベータ版として公開し、様々な環境でテストしていただいたほうが結果として、より早く完成度が高まるのではないかと考えました。
そこで「テストに付き合ってあげてもいいよ」「もっと良いコードの書き方があるよ」といった有志のご意見を募集しています。

よかったらこのページのコメントや、当ブログのフォームからご提案いただけると幸いです。