CMSのポテンシャルを引き出す─MODxで作る商用サイト

第18回MODxとコメント投稿機能

お知らせ

気温の差が激しく、衣服にも気を使う季節になりましたが、皆さん体調管理は万全でしょうか?

今回は、最初になかみつ園生姜紅茶を宣伝させてください。

IT系の皆さんにとってお茶とは馴染みの薄いものかもしれませんが、特に寒くなる季節に女性を中心に人気なのが生姜紅茶です。奥さんやご両親へのプレゼントなど、ぜひ検討してみてください。注文時に「gihyo.jpを見た」とコメントいただければオマケがつくようになっていますので、この機会に是非。

MODxにおけるコメント投稿機能

今回は、MODx上でコメントの投稿、表示機能を行うためのQuipというスニペットを紹介します(とはいっても、なかみつ園では執筆時点で導入していないのですが…⁠⁠。

QuipとはMODx Revolutionの中では比較的メジャーなスニペットであり、⁠システム⁠⁠→⁠パッケージマネージャー」よりパッケージのダウンロード画面に進むと、ダウンロードランキングの上位に位置しているスニペットです。

動作イメージとしては、皆さんがご覧いただいているこのページの下部を見ていただくのが一番です。以前紹介したように、gihyo.jpではMODx-1.0系が使用されているため、Quipとは異なるスニペットが使用されているはずですが、Quipを使用することで下記のような機能を実現できます。

  • サイト全体や任意のページにコメント機能を付加できる
  • モデレート機能
  • 日本語や任意の表示
  • コメントの削除などはMODxの管理画面から操作可能

それではさっそく設定を行ってみましょう。

Quipのインストールと基本的な設置

Quipは従来のように、パッケージマネージャーより導入しますが、インストール画面では、これまでのスニペットと異なり、簡単な初期設定が要求されますので、モデレーションなどに使用されるメール関連の設定をさくっと行なっておきましょう。これらの値は、後々「システム設定」より変更することが可能です。

図1 メールアドレスの設定
図1 メールアドレスの設定

導入テストを行う前に、気になることがありませんか?このスニペットは、これまで紹介してきた一連のスニペットと異なり、ユーザからのデータを取得し、HTMLとして表示します。実際のコメントはデータベース内でどのように管理されているのでしょうか?

  • 各リソースに埋め込まれる?
  • それとも専用のテーブルに保存される?

答えは、スニペットインストール後のMySQL内の様子を見ると一目瞭然です。投稿されたコメントなどはmodx_quip_commentsといった専用のテーブルに格納されるようになります。これがわかれば、後々コメントをphpMyAdminのようなツールで編集することも簡単ですね。

MySQLデータベース内の様子
mysql> show tables; 
-- 略 --
| modx_quip_comment_notify           | 
| modx_quip_comments                 | 
| modx_quip_comments_closure         | 
| modx_quip_threads                  | 
-- 略 --

それでは、例のごとくスニペットのドキュメントを見ながら、まずは最低限のパラメータを使ってスニペットの設置を行ってみましょう。今回は、⁠生姜紅茶】という商品ページリソースのフッターに近い部分に次の2行を追加してみました。1行目は既存コメントの表示に使われるスニペット、2行目はコメントの投稿フォームとして使用されるスニペットです。

<p>[[!Quip? &thread=`shogakocha` &threading=`0`]]</p>
<p>[[!QuipReply? &thread=`shogakocha`]]</p>

実際のページは次のようになります。

図2 コメント機能が追加された様子
図2 コメント機能が追加された様子

このように、コメント機能が数分で設置できるのは素晴らしいのですが、現状では投稿制限がないため、spam投稿の格好の餌食となってしまいますし、日本語サイトなのにコメント部分は英語表示、という微妙な状態になっていますので、まずは実用的なレベルになるまでカスタマイズしていきます。

スレッド名をエイリアスにする

コメント受付可能なページが複数存在する場合、全てのページにユニークなスレッド名を与えるのは大変ですので、MODx変数を使って楽をしてみましょう。具体的には、スニペットのコールを次のようにします。

リスト1 MODx変数を利用してスニペットのコール
[[!Quip? &thread=`[[*alias]]` &threading=`0` ]] 
[[!QuipReply? &thread=`[[*alias]]`]]

この形式であれば(エイリアスの重複がない限り)どのページにもコピー&ペーストが可能なので、ずいぶん構築の手間を省くことができると思います。また、常に最新の情報を表示する必要があるコメント表示部分と異なり、コメント投稿部分ではキャッシュを使用しても問題がないことが多いため、必要に応じて

[[QuipReply? &thread=`[[*alias]]`]]

という書式でキャッシュを有効にしておきましょう。

英語表示を日本語表示にする

Quipスニペットには残念ながら日本語対応のLexicon、つまり語彙ファイル(馴染みが薄いので、以後「翻訳ファイル」とします)が含まれていません。MODxの一ユーザである筆者としても、この辺りの国際化対応にはぜひ貢献したいところではあるのですが、継続的なメンテナンスを考えると、なかなかコミュニティに貢献できないのが現状です。

今回は、翻訳ファイル全体を日本語化するのは大変ですので、⁠ユーザの目に見える部分」のみを日本語表示にしてみます。翻訳ファイルは/var/www/html/core/components/quip/lexicon以下に保存されていますので、enディレクトリをjaという名前にコピーして、各ファイルの修正を行っていきましょう。

英語の翻訳ファイルをベースに編集を行う
% cd /var/www/html/core/components/quip/lexicon
% cp -r en ja
% cd ja

ここで、翻訳したいキーワードを検索し、文字列を置き換えていきます。たとえば、現在ブラウザで表示されている「Add a new comment」という文字列を次のようにgrepしてみると、default.inc.phpというファイルを修正すれば良いことがわかります。

英語の文字列をgrepして翻訳箇所を探していく
% grep 'Add a new comment' * 
default.inc.php:$_lang['quip.comment_add_new'] = 'Add a new comment:';

結果的に、筆者の環境では次のような修正を行ってみました。ファイルを修正した後は、管理画面からキャッシュをクリアすることを忘れないでください。

リスト2 default.inc.php内での変更箇所
$_lang['quip.allowed_tags'] = '使用可能なタグ: [[+tags]]'; 
$_lang['quip.comment_add_new'] = '新規コメントを追加:'; 
$_lang['quip.comments'] = 'コメント'; 
$_lang['quip.email'] = 'メールアドレス'; 
$_lang['quip.email_err_ns'] = 'メールアドレスを入力してください。'; 
$_lang['quip.message_err_ns'] = 'メッセージを入力してください。'; 
$_lang['quip.name'] = 'お名前'; 
$_lang['quip.name_err_ns'] = '名前を入力してください。'; 
$_lang['quip.notify_me'] = '新規リプライを通知する'; 
$_lang['quip.post'] = '投稿'; 
$_lang['quip.preview'] = 'プレビュー'; 
$_lang['quip.website'] = 'ウェブサイト';

以上の修正により、コメント画面がずいぶん自然な感じになるのですが、これだけだと日付の表示が

  • 「Oct 12, 2011 at 06:30 PM」

のように、日本人には不自然なフォーマットのままですので、見慣れたYYYY/MM/DD hh:mm:ssのようなフォーマットに修正してみます。こちらはスニペットコール時に指定することが可能です。

リスト3 日時のフォーマットを変更
[[!Quip? &thread=`[[*alias]]` &threading=`0` &dateFormat=`%Y/%m/%d %H:%M:%S`]] 
[[!QuipReply? &thread=`[[*alias]]`]]

日本語表示が可能になったところで、それ以外の設定を見ていきましょう。

モデレート機能

モデレート、またはモデレーション機能とは、コメント投稿者がコメントをポストした後、管理者が内容をチェックして投稿の可否を決定できる機能です。当然コメントの表示はリアルタイムではなくなりますが、いたずらなど不必要なコメントをフィルタするのに役立ちます。

Quipスニペットでモデレート機能を有効にするためには、投稿用である QuipReplyスニペット側で次のようにパラメータを設定します。

[[!QuipReply? &thread=`[[*alias]]` &moderate=`1`]]

この設定により、投稿後の画面は次のようになり、管理者には投稿内容がメールで通知され、コメントは「コンポーネント⁠⁠→⁠Quip」より管理することができるようになります。

図3 モデレート機能が有効な場合の投稿
図3 モデレート機能が有効な場合の投稿
図4 管理画面でコメントを管理
図4 管理画面でコメントを管理

おっと、機能を有効にしたことで、新たな英語メッセージが表示されるようになってしまいました。さらには、モデレート機能を用いることで、管理者や投稿者にメール送信が行われるようになるのですが、当然それらの内容も英語の状態になってしまいますので、必要に応じて以後紹介する翻訳ファイルを用いてください。

スレッド表示

スレッド機能を有効にすれば、特定のコメントに対してコメントができるようになります。商用サイトでの用途としては、顧客からの質問に対して個別に回答を付与できるのがメリットでしょうか。

この機能を有効にするためには、これまでと異なり「スレッドに返信する」ための新規リソースが必要になります。

表1 スレッド返信用リソースの作成
タイトルスレッドに返信
公開するチェック
エイリアスreply-to-thread
メニューに表示しないチェック
コンテンツ<h2>スレッドに返信する</h2>
[[!Quip? &dateFormat=`%Y/%m/%d %H:%M:%S`]]
<br />
[[!QuipReply]]

次に、各商品ページなどに設置するスニペットの定義は次のようにします。&replyResourceIdというパラメータでは、先ほどの返信用リソースIDを指定してください。

<p>[[!Quip? &thread=`[[*alias]]` &threading=`0` &dateFormat=`%Y/%m/%d %H:%M:%S` &replyResourceId=`18`]] </p>
<p>[[!QuipReply? &thread=`[[*alias]]` &moderate=`1`]]</p>

なお、スレッド機能を有効にすることで、⁠Reply」という新たな英語の表示が行われてしまいますので、以下略…(笑⁠⁠。

翻訳ファイルの修正内容

中途半端ではありますが、今回用いた翻訳ファイルの内容を紹介しておきます。他力本願で勝手ですが、英語の内容自体はそれほど難しくないため、もし、やる気のある方がいらっしゃれば、ぜひスニペットの作者に連絡をとって、日本語の翻訳ファイルをメンテナンスしていただけるとありがたいです⁠。

 最近あまりオープンソース界に貢献できていない筆者が言うのも変ですが、OSS的な活動を行う中で非常に重荷になるのが、継続的なメンテナンスなのです……。
リスト4 default.inc.phpの内容
<?php 
/** 
 * Quip 
 * 
 * Copyright 2010-11 by Shaun McCormick <shaun@modx.com> 
 * 
 * This file is part of Quip, a simple commenting component for MODx Revolution. 
 * 
 * Quip is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU General Public License as published by the Free Software 
 * Foundation; either version 2 of the License, or (at your option) any later 
 * version. 
 * 
 * Quip is distributed in the hope that it will be useful, but WITHOUT ANY 
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License along with 
 * Quip; if not, write to the Free Software Foundation, Inc., 59 Temple Place, 
 * Suite 330, Boston, MA 02111-1307 USA 
 * 
 * @package quip 
 */ 
/** 
 * Quip English language file 
 * 
 * @package quip 
 * @subpackage lexicon 
 */ 
$_lang['quip'] = 'Quip'; 
$_lang['quip_desc'] = 'A simple commenting component.'; 
$_lang['quip.ago'] = ' ago'; 
$_lang['quip.ago_days'] = '[[+days]] days'; 
$_lang['quip.ago_hours'] = '[[+hours]] hrs'; 
$_lang['quip.ago_minutes'] = '[[+minutes]] min'; 
$_lang['quip.ago_seconds'] = '[[+seconds]] sec'; 
$_lang['quip.approve_selected'] = 'Approve Selected'; 
$_lang['quip.approved'] = 'Approved'; 
$_lang['quip.allowed_tags'] = '使用可能なタグ: [[+tags]]'; 
$_lang['quip.anonymous'] = 'Anonymous'; 
$_lang['quip.author'] = 'Author'; 
$_lang['quip.back_to_threads'] = 'Back to Thread Listing'; 
$_lang['quip.body'] = 'Body'; 
$_lang['quip.bulk_actions'] = 'Bulk Actions'; 
$_lang['quip.close'] = 'Close'; 
$_lang['quip.comment'] = 'Comment'; 
$_lang['quip.comment_add_new'] = '新規コメントを追加:'; 
$_lang['quip.comment_approve'] = 'Approve Comment'; 
$_lang['quip.comment_approve_selected'] = 'Approve Selected Comments'; 
$_lang['quip.comment_approved'] = 'Comment Approved'; 
$_lang['quip.comment_approved_msg'] = 'The comment by [[+name]] on [[+createdon]] has been successfully approved.'; 
$_lang['quip.comment_delete'] = 'Delete Comment'; 
$_lang['quip.comment_delete_selected'] = 'Delete Selected Comments'; 
$_lang['quip.comment_deleted'] = 'Comment Deleted'; 
$_lang['quip.comment_deleted_msg'] = 'The comment by [[+name]] on [[+createdon]] has been successfully deleted.'; 
$_lang['quip.comment_delete_confirm'] = 'Are you sure you want to delete this comment?'; 
$_lang['quip.comment_err_nf'] = 'Comment not found.'; 
$_lang['quip.comment_err_ns'] = 'Comment not specified.'; 
$_lang['quip.comment_err_remove'] = 'An error occurred while trying to remove the comment.'; 
$_lang['quip.comment_err_save'] = 'An error occurred while trying to save the comment.'; 
$_lang['quip.comment_remove'] = 'Permanently Remove Comment'; 
$_lang['quip.comment_remove_confirm'] = 'Are you sure you want to permanently remove this comment? This cannot be undone.'; 
$_lang['quip.comment_remove_selected'] = 'Remove Selected Comments'; 
$_lang['quip.comment_unapprove'] = 'Unapprove Comment'; 
$_lang['quip.comment_unapprove_selected'] = 'Unapprove Selected Comments'; 
$_lang['quip.comment_undelete'] = 'Undelete Comment'; 
$_lang['quip.comment_undelete_selected'] = 'Undelete Selected Comments'; 
$_lang['quip.comment_update'] = 'Update Comment'; 
$_lang['quip.comment_view'] = 'View Comment'; 
$_lang['quip.comment_will_be_moderated'] = 'コメントが投稿されましたが、管理者のモデレート待ちです。コメントが承認されるとメール通知されます。'; 
$_lang['quip.comments'] = 'コメント'; 
$_lang['quip.delete'] = 'Delete'; 
$_lang['quip.delete_selected'] = 'Delete Selected'; 
$_lang['quip.email'] = 'メールアドレス'; 
$_lang['quip.email_err_ns'] = 'メールアドレスを入力してください。'; 
$_lang['quip.err_fraud_attempt'] = 'Error: IDs of users do not match. Fraud attempt detected.'; 
$_lang['quip.err_not_logged_in'] = 'You are not logged in and therefore are not authorized to post comments.'; 
$_lang['quip.hide_deleted'] = 'Hide Deleted'; 
$_lang['quip.intro_msg'] = 'Manage your Quip comments and threads here.'; 
$_lang['quip.ip'] = 'IP'; 
$_lang['quip.latest_comments'] = 'Latest Comments'; 
$_lang['quip.latest_comments_for'] = 'Latest Comments for'; 
$_lang['quip.latest_comments_msg'] = 'View the latest posted comments here.'; 
$_lang['quip.login_to_comment'] = 'Please login to comment.'; 
$_lang['quip.message_err_ns'] = 'メッセージを入力してください。'; 
$_lang['quip.moderated'] = 'Moderated'; 
$_lang['quip.moderated_desc'] = 'New posts to this thread will be moderated.'; 
$_lang['quip.moderators'] = 'Moderators'; 
$_lang['quip.moderators_desc'] = 'A comma-separated list of usernames who can moderate this thread.'; 
$_lang['quip.moderator_group'] = 'Moderator Group'; 
$_lang['quip.moderator_group_desc'] = 'A User Group whose members can moderate this thread.'; 
$_lang['quip.name'] = 'お名前'; 
$_lang['quip.name_err_ns'] = '名前を入力してください。'; 
$_lang['quip.no_email_to_specified'] = 'Quip cannot send a spam report email because no admin email was specified.'; 
$_lang['quip.notify_me'] = '新規リプライを通知する'; 
$_lang['quip.post'] = '投稿'; 
$_lang['quip.posted'] = 'Posted'; 
$_lang['quip.postedon'] = 'Posted On'; 
$_lang['quip.preview'] = 'プレビュー'; 
$_lang['quip.recaptcha_err_load'] = 'Could not load reCaptcha service class.'; 
$_lang['quip.reply'] = '返信'; 
$_lang['quip.remove'] = 'Remove'; 
$_lang['quip.remove_selected'] = 'Remove Selected'; 
$_lang['quip.report_as_spam'] = 'Report as Spam'; 
$_lang['quip.reported_as_spam'] = 'Reported as Spam'; 
$_lang['quip.sfs_err_load'] = 'Could not load StopForumSpam class.'; 
$_lang['quip.show_deleted'] = 'Show Deleted'; 
$_lang['quip.spam_blocked'] = 'Your submission was blocked by a spam filter: [[+fields]]'; 
$_lang['quip.spam_email'] = '<p>Hello,</p> 

<p>A comment by [[+username]] has been reported as spam at:</p> 

<p><a href="[[+url]]">[[+url]]</a></p> 

<p>This is an automated email. Please do not reply directly. The 
comment\'s ID is: <strong>[[+id]]</strong></p> 

<p> 
Thanks,<br /> 
<em>Quip</em> 
</p>'; 
$_lang['quip.spam_email_subject'] = '[Quip] Comment Spam Reported'; 
$_lang['quip.spam_marked'] = ' - marked as spam.'; 
$_lang['quip.thread'] = 'Thread'; 
$_lang['quip.thread_autoclosed'] = 'This thread has been closed from taking new comments.'; 
$_lang['quip.thread_err_ns'] = 'No thread specified.'; 
$_lang['quip.thread_err_remove'] = 'An error occurred while trying to remove the thread.'; 
$_lang['quip.thread_err_save'] = 'An error occurred while trying to save the thread.'; 
$_lang['quip.thread_manage'] = 'Manage Thread'; 
$_lang['quip.thread_msg'] = 'Here you can manage all the comments for this particular thread.'; 
$_lang['quip.thread_remove'] = 'Remove Thread'; 
$_lang['quip.thread_remove_confirm'] = 'Are you sure you want to completely remove this thread, and all of its comments? This is irreversible.'; 
$_lang['quip.thread_remove_selected'] = 'Remove Selected Threads'; 
$_lang['quip.thread_remove_selected_confirm'] = 'Are you sure you want to remove these threads entirely? This is irreversible.'; 
$_lang['quip.thread_truncate'] = 'Truncate Thread'; 
$_lang['quip.thread_truncate_confirm'] = 'Are you sure you want to remove all comments in this thread?'; 
$_lang['quip.thread_truncate_selected'] = 'Truncate Selected Threads'; 
$_lang['quip.thread_truncate_selected_confirm'] = 'Are you sure you want to remove all comments in these threads?'; 
$_lang['quip.threads'] = 'Threads'; 
$_lang['quip.unapproved'] = 'Unapproved'; 
$_lang['quip.unapproved_comments'] = 'Unapproved Comments'; 
$_lang['quip.unapproved_comments_msg'] = 'Moderate any unapproved comments here.'; 
$_lang['quip.unsubscribe_me'] = 'Unsubscribe Me from this Thread'; 
$_lang['quip.unsubscribed'] = 'You have been unsubscribed from this Thread. Thanks!'; 
$_lang['quip.username_said'] = '<strong>[[+username]]</strong> said:'; 
$_lang['quip.view'] = 'View'; 
$_lang['quip.website'] = 'ウェブサイト'; 

$_lang['area_tags'] = 'Tags'; 
$_lang['setting_quip.allowed_tags'] = 'Allowed Tags'; 
$_lang['setting_quip.allowed_tags_desc'] = 'The tags allowed for users in comment posting. See <a target="_blank" href="http://php.net/strip_tags">php.net/strip_tags</a> for a list of acceptable values.'; 
$_lang['setting_quip.emailsFrom'] = 'From Email'; 
$_lang['setting_quip.emailsFrom_desc'] = 'The email address to send system emails from.'; 
$_lang['setting_quip.emailsTo'] = 'To Email'; 
$_lang['setting_quip.emailsTo_desc'] = 'The email address to send system emails to.'; 
$_lang['setting_quip.emailsReplyTo'] = 'Reply-To Email'; 
$_lang['setting_quip.emailsReplyTo_desc'] = 'The email address to set the reply-to to. Defaults to emailFrom.'; 
$_lang['setting_quip.emails_from_name'] = 'From Name for Emails'; 
$_lang['setting_quip.emails_from_name_desc'] = 'The name that will be used for the From: address in emails.';
リスト5 emails.inc.phpの内容
<?php 
/** 
 * Quip 
 * 
 * Copyright 2010-11 by Shaun McCormick <shaun@modx.com> 
 * 
 * This file is part of Quip, a simple commenting component for MODx Revolution. 
 * 
 * Quip is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU General Public License as published by the Free Software 
 * Foundation; either version 2 of the License, or (at your option) any later 
 * version. 
 * 
 * Quip is distributed in the hope that it will be useful, but WITHOUT ANY 
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License along with 
 * Quip; if not, write to the Free Software Foundation, Inc., 59 Temple Place, 
 * Suite 330, Boston, MA 02111-1307 USA 
 * 
 * @package quip 
 */ 
/** 
 * @package quip 
 * @subpackage lexicon 
 */ 
$_lang['quip.email_comment_approved'] = '<p>[[+name]] 様、</p> 

<p>下記のコメントが承認されました。</p> 

<p><a href="[[+url]]">[[+url]]</a></p> 

'; 
$_lang['quip.email_comment_approved_subject'] = 'コメントが承認されました'; 
$_lang['quip.email_notify'] = '<p>Hello,</p> 

<p>[[+name]] 様によるコメントが下記に投稿されました。</p> 

<p><a href="[[+url]]">[[+url]]</a></p> 

<p>----------------------------------------------------</p> 

<p>[[+body]]</p> 

<p>----------------------------------------------------</p> 

<p>このメールは自動送信です。直接返信しないでください。 
コメントIDは [[+thread]] スレッドの [[+id]] です。</p> 
'; 
$_lang['quip.email_notify_subject'] = '新規コメントが投稿されました'; 
$_lang['quip.email_moderate'] = '<p>こんにちは、</p> 

<p>[[+name]] 様による新しいコメントが下記に投稿され、モデレート待ちです。</p> 

<p><a href="[[+url]]">[[+url]]</a></p> 

<p>----------------------------------------------------</p> 

<p>[[+body]]</p> 

<p>----------------------------------------------------</p> 

<p><a href="[[+approveUrl]]">コメントを承認</a> | <a href="[[+rejectUrl]]">コメントを削除</a> | <a href="[[+unapprovedUrl]]">コメントを非承認</a></p> 

<p>このメールは自動送信です。直接返信しないでください。 
コメントIDは [[+thread]] スレッドの [[+id]] です。</p> 
'; 
$_lang['quip.email_moderate_subject'] = '新規投稿がモデレーション待ちです';

おわりに

Quipスニペットには今回紹介した以外にもreCAPTCHAなど、さまざまな機能が含まれています。ぜひスニペットを活用していただき、インタラクティブなページを作ってみてください。

おすすめ記事

記事・ニュース一覧