DrupalのWYSIWYG環境

Drupalの本文への文書入力や画像挿入に関するモジュールについて記載します。

WYSIWYGエディタを使用しているならば、編集画面と表示画面が同じでなければあまり意味がありません。
この点についてはまだ完全にチューニングできていませんが、以前よりはよくなったと思っています。

主となるモジュールはWYSIWYG ImageFieldです。
このモジュールはCCK+FileField+ImageField+ImageCacheを必須としています。
CCKをサポートしているモジュールがまだ少なかったころはIMCEを使って画像アップロードと挿入を行っていました。
しかし、D7ではCCKがコアに吸収されるなど当たり前となった今ではIMCEを使うこともなくなりました。

利用モジュール解説

このサイトで使用しているWYSIWYG関連のモジュールは以下の通りです。
1~4番は、今やDrupalサイト構築では必須といってもよいモジュールです。これらがDrupal7でコアに吸収されていることからも容易に推測できます。

1.CCK

Content Construction Kitは、CCKフィールドのベースとなるモジュールです。
カスタムのコンテンツタイプの作成が可能になり、さらにフィールドの追加が可能になります。
CCKフィールドはViews2(D6)、Views3(D7)でもサポートされていますので、CCKフィールドを使用すれば再利用が容易になります。

2.FileField

ファイルアップロード用のフィールドタイプを追加します。D6のFileFiledにはファイルフィールドからメタ情報を収集するFileFiled Metaの機能がありますが、D7ではコアに吸収されているのか不明です。なお、メタ情報を収集するにはGetID3モジュール(とGetID3ライブラリ)が必要です。さらにDrupal6ではMIMEタイプ情報を取得するMimedetectモジュールとの併用が強く推奨されています。
画像、音楽、ビデオなどメディアファイルのアップロードには欠かせません。
FileField XXXXXのモジュールを追加することで機能追加できますが、Drupal7ではサポートしていないケースが多々ありますので注意が必要です。
FileField自体はDrupal7コアに吸収されているようですが、どの機能までかは把握できていません。

3.ImageField

ファイルフィールドの画像専用フィールドタイプを追加します。このモジュールはImageCacheとの連携をしていますので、このImageフィールドを使ってアップロードしたファイルはImageCacheでオリジナル画像を加工して表示することが可能になります。

4.ImageCache

オリジナル画像を加工して表示できるようになります。
加工にはGD2またはImageMagickライブラリを使用します。デフォルトでいくつかのアクション(加工方法)が使えますが、ImageCache Actionsモジュールを追加することでさらに加工方法が追加できます。ただし、Drupal7では未サポートのモジュールなので今後、Drupal7への以降を計画しているのであれば導入を躊躇うことになりそうです。

5.WYSIWYG API

WYSIWYGモジュールは、多くのWYSIWYGエディタをサポートしています。
DrupalでWYSIWYGエディタ機能を導入する場合は、各エディタの本体プログラム以外にエディタ専用のDrupal APIが必要でした。
たとえば、FCKEditorモジュール+FCKEditor本体プログラムが必要になります。
この場合は、Drupal管理画面からエディタ固有の設定ができたり、カスタマイズは直接本体プログラムを編集すれば反映されました。
しかし、エディタと連携するような寄贈モジュールを開発する場合にはエディタ専用のDrupal APIにあわせた個別対応が必要になります。
その影響でサポートするエディタが限定されかねません。
そのような問題を解決するのがWYSIWYGモジュールだと思います。
このモジュールを導入すればあとは利用したいエディタ本体のプログラムを配置するだけです。
また、入力書式ごとに使用するエディタを選択できます。
ただし、エディタ専用のDrupal APIを利用する場合に比べるとカスタマイズがしにくくなっているため不便を感じるかもしれません。
またエディタ専用のDrupalモジュールとの併用はできません。

WYSIWYG ライブラリ

多くのエディタがサポートされています。WYSIWYGプロファイル設定画面に入手先へのリンクがありますのでそちらで確認してください。
 

6.JQuery UI

javascriptによるユーザインターフェースを提供してくれるライブラリを使用可能にするモジュールですので、別途ライブラリが必要です。

JQuery UI API

RedadMeには明言されていませんが、LibrariesAPIをサポートしているようですので「sites/all/libraries」以下にディレクトリ名「jquery.ui」で配置すれば読み込んでくれます。
※「ハイフン」や「アンダーバー」でなく「ドット(点)」です。ダウンロードしたトップディレクトリ名は「ハイフン」が使われているので注意してください。
※正常によみこめているかはサイトの状態「admin/reports/status」のJQuery関連の項目で確認できます。
JQuery UI APIバージョン選定にはJQueryのバージョンが関係しますので、JQueryのバージョンとセットで考えれください。
JQuery Updateモジュールを導入している場合は、そのモジュールによってコアのJQueryのバージョンがアップデートされていますので、JQuery UpdateモジュールでのJQueryのバージョンで考える必要があります。

7.Insert

本文への画像挿入に使用するモジュールです。
画像貼り付けコードはタイトルやALTタグに対応していますので、SEOや自動読み上げソフトにも対応できます。
カスタマイズが必要な場合はテーマディレクトリ以下に「imagecache-insert-image.tpl.php」を作成し以下のように記載すれば、タイトルやALTの自動付与とThickBoxの使用ができました。

<a class="thickbox" rel="group1"  href="<?php print base_path().$item['filepath'] ?>"<?php print $class ? ' class="' . $class . '"' : '' ?> title="__description__">
<img src="<?php print $url ?>" alt="<?php print substr($item['filename'], 0, strpos($item['filename'], '.')); ?>" title="<?php print substr($item['filename'], 0, strpos($item['filename'], '.')); ?>" class="imagecache-<?php print $preset_name ?><?php print $class ? ' ' . $class : '' ?>" /></a>

※Aタグ内のtitle="__description__”の箇所では、"__description__”が空(NULL)なのでタイトルは設定できていません。この箇所にきちんと文字列を代入すればThickBoxの画面内にもタイトル表示できると思います。

ColorBoxはInsertに対応していますので、Thickboxのときのカスタマイズは不要です。

8.WYSIWYG ImageField

WYSIWYG Presetから入力書式ごとに使用可能なWYSIWYGを選択し、ボタン設定でWYSIWYG ImageFiledボタンを有効にすれば、WYSIWYGエディタのツールバーに追加したボタンが表示されます。この追加されたボタンを使用して画像の挿入を行います。画像のソースの場所がローカルPC上ならアップロードしてから挿入が可能です。
この挿入にはInsertモジュールの機能が使われています。

9.Libraries API

モジュールの要件で別途ライブラリなど外部プログラムが必要なケースでは、これまではモジュール以下の所定の場所に配置していましたが、このLibrariesモジュールを導入すれば「sites/all/libraries」以下に配置することで各モジュールが読み込んでくれるようになります。ただし、Libraries APIをサポートしているモジュールに限定されます。
JQuery UIのように明言されていないモジュールもありますが、D7ではサポートしているモジュールがD6のころよりは増えていますので導入しても損はないと思います。
JQuery UIモジュールのようにディレクトリ名を所定の名前にしておけば自動で読み込んでくれるケースと「sites/all/libraries」以下も指定可能にはなっているけれども別途、管理画面からパスを入力するタイプの2通りがあります。後者の場合はディレクトリ名はユニークでもよいようです。どちらのケースでもUNIXでいうところのリンク(lnコマンド)を使えば複数バージョンを管理できそうです。

10.Color Box

このモジュールはLitghtBox2相当の機能があるようです。
D6ではThickBoxを使用していましたが、D7ではサポートしていないのでColorBoxにしました。
Insertモジュールをサポートしているので、モジュールを導入するだけで済みます。
ThickBoxのときのようにテーマディレクトリ以下にテンプレートファイルを配置する必要はありません。

ColorBoxライブラリ

別途、CorBox本体が必要になります。「sites/all/liblaries」以下に配置すればOKです。
ColorBoxの使用可能バージョンは、JQueryのバージョンに依存します。
Drupal6.xの場合はJQuery1.4を必要とするColorbox 3.19はそのままではうまく動作しません。

11.JQuery Update

DrupalコアのJQueryライブラリを最新の状態に維持するためのモジュールです。
JQueryライブラリはコア(Drupalルートディレクトリ/misc以下)にありますが、このモジュールを導入すると最新のバージョンが利用可能になります。Drupalシステムファイルやモジュール以下のファイルは直接編集するのはあまりよくないのでこういうモジュールの需要が高まったのだと思います。

※このJQuery Updateについては以前記事にしました。誤解してしまう表現になっていますのでここで改めて記載しておきます。
Drupalコア(miscディレクトリ以下)にJQueryライブラリ(ver1.2.6)が含まれており、JQueryUpdateモジュール(sites/all/modules/jquery_update/replace以下)でそのJQueryライブラリのバージョンを最新に更新する。ただし、Drupalコアのバージョン(マイナーバージョン含む)が異なればJQueryのバージョンが異なる。またJQueryUpdateモジュールには6.x-1.xと6.x-2.xがあり、それぞれJQueryライブラリのバージョンが異なるので注意が必要である。(表参照)
そして、JQuery UIはモジュールのみでAPIは別途JQuery UIサイトから入手する必要がある。
JQuery UI API(sites/all/libaries/jquery.ui/以下)はシステムのJQueryのバージョンに依存して動作するので、JQueryのバージョンにあったJQuery UI APIをダウンロードしなければならない。JQuery関連が正常な状態であるかは「サイトの状態」画面で確認できる。
※Drupal7ではサイトの状態には表示されていません。

以上はDrupal6での話しです。

Drupalコアおよび寄贈モジュールとJQueryライブラリのバージョン関係

  JQueryライブラリ JQuery UI API
Drupalコア JQuery Updateモジュール Durplaコア JQuery UIモジュール
6.x-1.x 6.x-2.x 6.x-1.x
Drupal 6.x ver.1.2.6
※Drupal 6.22の場合
ver.1.2.6 ver.1.3.2 なし 別途入手
※JQueryに依存
Drupal 7.x ver.1.4.4
※Drupal 7.8の場合
なし なし ver.1.8.7(min)
※Drupal 7.8の場合
なし

JQueryライブラリとJQueryUIライブラリのバージョン関係

  JQueryライブラリバージョン
ver.1.2.6 ver.1.3.2 ver.1.4.4
JQueryUI API ver.1.6.x ver.1.7.x ver.1.8.x

JQueryはテーマの動作にも影響してきますので、システムでどのバージョンを使用しているかを把握しておくことは重要です。
 

カスタマイズ

WYSIWYGということで編集画面と表示画面が同じでなければ意味がありません。
WYSIWYGエディタ単体で使用する場合にはWYSIWYGエディタの設定をそのまま使用すれば同じになるはずですが、Drupalシステム上ではDrupalテーマで使用しているスタイルシートなどの影響でそうはなってくれません。
また、DrupalにWYSIWYGエディタ機能を導入する場合は、個別のモジュールを導入する方法もあります。この場合はインターネット上で公開されているような一般的な各エディタの設定ファイルを
編集すればカスタマイズが行えますが、WYSIWYG ImageField+WYSIWYGモジュール+個別ライブラリの方法ですと一般的なカスタマイズ方法が使えません。
WYSIWYGモジュール+CKEditorライブラリを例にカスタマイズの方法について少し紹介しておきます。

カスタマイズにはhookモジュールを使用しますので、ユニークなモジュール名で作成してください。
※カスタマイズ用のhookモジュールを作成しておくといろいろな用途に使えるので便利だと思います。
※以下の情報はDrupal公式サイトで紹介されていますが、URLを失念してしまいました。

作成したカスタムモジュールのhogehoge.modulesファイルに記述します。
※hogehogeがカスタムモジュールのモジュール名(プロジェクト名)になります。
※カスタムモジュールの作成については以前の記事にありますので詳細はそちらを参考にしてください。

1.スペルチェックの無効化

CKEditor個別の機能だと思いますが、文字入力のたびにSCAYTスペルチェックサイトへアクセスするため文字入力に邪魔です。
この機能はデフォルトで有効になっているため以下の設定で無効にします。一般的にはCKEditorライブラリ内のConfig.jsで無効にできるのだと思います。

<?php
function hogehoge_wysiwyg_editor_settings_alter(&$settings, $context) {
  if($context['profile']->editor == 'ckeditor'){
    $settings['scayt_autoStartup'] = FALSE;
  }
}

_wysiwyg_editor_settings_alterをhookしています。
WYSIWYGモジュールは多種のエディタをサポートしていますので、エディタがckeditorの場合という条件をつけておきます。
各エディタのカスタム設定が反映されない理由は、WYSIWYGモジュールがエディタ個別のXXXXXX.incファイルで標準設定のみを読み込むためです。_wysiwyg_editor_settings_alterのhookモジュールを作成することで、incファイルで読み込んだ内容を上書きすることができます。

2.Pタグとbrタグのキー設定を入れ替える

ほとんどのエディタが<Enter>キーでPタグを挿入、<Shift>+<Enter>キーでbrタグを挿入になっていますが、これを逆の設定にしています。
今後使うかもしれないことを想定して「TinyMCE」「Fckeditor」「CKEditor」の3種類を追加しました。

function hogehoge_wysiwyg_editor_settings_alter(&$settings, $context) {
  if($context['profile']->editor == 'fckeditor') {
    $settings['EnterMode'] = 'br';
    $settings['ShiftEnterMode'] = 'p';
  }
  if($context['profile']->editor == 'ckeditor'){
    $settings['enterMode'] = 2;
    $settings['shiftEnterMode'] = 1;
    $settings['scayt_autoStartup'] = FALSE;
  }
  if($context['profile']->editor == 'tinymce'){
    $settings['forced_root_block'] = FALSE;
    $settings['force_br_newlines'] = TRUE;
    $settings['force_p_newlines'] = FALSE;
  }
}

3.ツールバーのアイコンボタンを並び替える

WYSIWYGモジュールが各エディタの標準設定をファイルから読み込むため、ボタンの配置は読み込んだ順になります。
また、WYSIWYG ImageFieldプリセット画面でボタンに関する項目は有効/無効の設定のみです。
当初あきらめていた事項ですが、アイコンボタンを並びかえる方法を寄贈モジュールとして公開されている方がおられましたので、それをそのまま使わせていただきました。

オリジナルはこちらにあります。D6版とD7版がありますがどちらもhookモジュールですので、このモジュールをダウンロードして上記1~2番を上に追加すればよいと思います。
ダウンロードしたファイルは、カスタムモジュールを作成するときの雛形としても使えます。
ちなみにD6版のまま、incファイルのコアバージョンの箇所を書き換えるだけでD7でも動作しました。

1~3の内容を追加すると以下のようになります。
<?php
// $Id: hogehoge.module,v 1.00.00 2011/04/14 Custom $
function hogehoge_wysiwyg_editor_settings_alter(&$settings, $context) {
  if($context['profile']->editor == 'fckeditor') {
    $settings['EnterMode'] = 'br';
    $settings['ShiftEnterMode'] = 'p';
  }
  if($context['profile']->editor == 'ckeditor'){
    $settings['enterMode'] = 2;
    $settings['shiftEnterMode'] = 1;
    $settings['scayt_autoStartup'] = FALSE;
  }
  if($context['profile']->editor == 'tinymce'){
    $settings['forced_root_block'] = FALSE;
    $settings['force_br_newlines'] = TRUE;
    $settings['force_p_newlines'] = FALSE;
  }
  if ($context['editor']['name'] != 'ckeditor' || !isset($settings['toolbar'][0])) {
    return;
  }

  $tools = array_flip($settings['toolbar'][0]);
  $tools2 = $tools;

  $tools['-'] = 42;
  $map = _wysiwyg_ckeditor_nice_plugin_map();
  $items = 0;

  foreach ($map as $key => $toolgroup) {
    if ($toolgroup == '/') {
      // When there have been no items in this row
      // remove the seperator
      if ($items == 0) {
        unset($map[$key]);
      }

      $items = 0;
      continue;
    }

    if (!is_array($toolgroup)) {
      continue;
    }

    foreach ($toolgroup as $k => $tool) {
      if (!isset($tools[$tool])) {
        unset($map[$key][$k]);
      }
      else {
        unset($tools2[$tool]);
        $items++;
      }
    }

    if (count($map[$key]) == 0) {
      unset($map[$key]);
    }
    else {
      $map[$key] = array_values($map[$key]);
    }
  }
  // Renumber
  $map[] = array_values(array_flip($tools2));
  $map = array_values($map);

  $settings['toolbar'] = $map;

  // Set language
  global $language;
  $settings['language'] = $language->language;
}

function _wysiwyg_ckeditor_nice_plugin_map() {
  $map = array(
    array('Source', '-', 'Save', 'NewPage', 'Preview', '-', 'Templates'),
    array('Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Print', 'SpellChecker', 'Scayt'),
    array('Undo', 'Redo', '-', 'Find', 'Replace', '-', 'SelectAll', 'RemoveFormat'),
    array('Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'),
    '/',
    array('Bold', 'Italic', 'Underline', 'Strike', '-', 'Subscript', 'Superscript'),
    array('NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote', 'CreateDiv'),
    array('JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'),
    array('Link', 'Unlink', 'Anchor'),
    array('Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak'),
    '/',
    array('Styles', 'Format', 'Font', 'FontSize'),
    array('TextColor', 'BGColor'),
    array('Maximize', 'ShowBlocks', '-', 'About'),
  );

  return $map;
}

WYSIWYG ImageFieldボタンなどこれに含まれていないボタンは、上記のボタンの後ろに表示されます。
Drupal公式サイトで"_wysiwyg_editor_settings_alter"を検索キーにすればいろいろ情報が出てくると思います。

4.SyntaxHightlighter

コードをハイライト表示できればビジュアル的によいのかもしれませんが、それよりも行番号の表示ができればよいかなぁという希望はあります。
入力フィルターを追加するタイプはあるようですが、記事作成後にフィルター処理されるのがあまり好きではないのとblockquoteは引用で使うので敬遠しています。
preタグを使ってGeSHI Proプラグインのようにエディタ上で使えるものを探しています。
1~3番の方法でカスタムプラグインが読み込めそうですが、実際のところよくわかりません。
基本的にはpreタグ+スタイルシートの組み合わせで満足しているので時間があればチャレンジしようと思っています。
 

5.スタイルシートの活用

文字を入力する際は、エディタにあるボタンで個別に文字サイズ、太文字、斜線、カラーなどの装飾をしていました。
それぞれの記事個別でみればまとまっているように見えますが、複数のページを読んでいくと非常に乱雑な感じをうけてしまいます。
そこで、フォーマットで見出しを設定するなどしてDrupalテーマのスタイルシートが反映されるように変更しました。
現在、テーブルのスタイルも複数使用していますがデザインなど全くことなるため一貫性がなく見辛くなっているように思います。
このあたりは今後の課題です。

WYSIWYG ImageFieldの環境はD7でも動作することを確認できましたのでD7環境でも使用する予定ですが、まだ編集と表示が同じになるまではチューニングしきれていません。

留意点

ノードのインポートとエクスポート

ImageFieldに関連して無制限で使用している点がネックなのかインポート/エクスポートモジュールではインポートで失敗します。
※D6 to D7、D7 to D7でダメでした。
フィールドがなければCSVで簡単にインポートできますが、ノードすべてをとなるとNodeExportやFeedsを使っても上手くいきません。
D7 Node Export⇒D7 Node Inportでもダメでした。
インポート/エクスポートができなければD7をクリーンインストールしてという方法は使えなくなるので、コアのアップグレードで対応するしかなさそうです。

Bookでの使用

Bookではカスタムフィールドを設定すると選択メニュー(入力書式、投稿オプション、投稿の情報)などが使用できなくなります。フィールドの破損による影響?
WYSIWYGエディタも表示できなくなりますので注意が必要です。Bookコンテンツで使用できるのはテキストフィールドのみのようです。
他のコンテンツタイプで作成したノードをアウトライン指定でBookに挿入する場合は大丈夫です。

コンテンツタイプの変更

NodeTypeモジュールでコンテンツの変換をするとフィールドを損失しますのでNodeConvertモジュールのほうを使用してください。
変換用のテンプレートを作成する手間がありますが問題なく変換できます。

D6とD7の比較

D7では、D6と比べるとCCKやフィールドおよびJQuery関連がコアに吸収されたので追加モジュールが少なくて済みます。
ファイルアップロード後の画面が少々ことなります。

アップロードファイルのパーミッション

※外部転送ソフトを使ったアップロードの話ではありません。
WYSIWYG ImageFieldでアップロードする際にはFileFieldが使われていますが、アップロードファイルのパーミッションが従来と変わっていることに気が付きました。
ファイルのパーミッションが変わるとウェブサーバの設定次第では動作しなくなる場合があります。
さくらのレンタルサーバを利用していますが、CGIは755また705にするように記載されています。
そして、不適切なパーミッションの場合は、Server Internal Error 500が発生します。
CGIが読み込むファイルやディレクトリのパーミッションの影響も受けるケースもあるようです。
しかし、オーナー以外に書き込み権限がなければよいらしく755や705でなくても644や604でも動作していました。
これまでは644だったのが664になってしまいました。

D6⇒D7⇒D6でD6に戻した際、カスタマイズ箇所がリセットされて発生してしまったのか、途中で仕様がかわったのか・・・
これの影響だと思うのですが、ファイルアップロード時にServer Internal Error 500が発生するようになりました。
毎回ではありませんがApacheのログには「Premature end of script headers:【Drupalルートディレクトリ】/index.php」のエラーが記録されています。
このエラーの原因は大抵、ファイルパーミッションのケースがほとんどなのでファイルパーミッションが664に変わってしまう件が非常に気がかりです。
動画サイトを構築したときに何か策を施したようなやってないような・・・
Drupalのサイトの情報をみるとImageFiledのデフォルトは664だとあり、あれ?となってしまった状態です。

以下は、WYSIWYG ImageFieldを使用時の影響についてです。
一旦、アップロードすると記事を保存するまではキャッシュ(一時保存場所)にファイルが置かれています。
仮にアップロード中にエラーが発生してしまったために再アップロードをすると名前が重複しないように「_0」といった数字が付与されます。
※フィールドの設定次第ではあります。
同時にファイルフィールドのテーブルにも同じ画像ファイルで複数の名前のデータが存在してしまいます。
さらにファイル名が同じファイルを画像データを変えて再送する場合(画像編集ミスを修正して送りなおすといった場合)には、アップロード時のファイルブラウザー上の画像、アップロード後のWYSIWYG ImageFiled画面上の画像、記事保存前と保存後のImageCache画像、オリジナル画像とが一致しないことがあります。

先に書いたようなServer Internal Error 500が発生すると、(途中記事の保存をしていなければ)それまでの編集内容は消失することになります。
そして、その修復のために再度、画像ファイルの挿入をやり直す場合に混乱が発生します。

Drupalバージョン

Drupal 6.x Drupal 7.x