フォーラム投稿のダイジェストメールに関わる小さな改良と小さな問題

フォーラム投稿のダイジェストメールに関わる小さな改良と小さな問題

- Tatsuya Shirai の投稿
返信数: 15

 フォーラム投稿のダイジェストメールを1日に1度受け取る設定で利用しています.

 自分の管理するMoodleサイトもそうですが,moodle.orgも同様です.このダイジェストメールで以前から気になっていた点が一つあります.複数のコースに属している場合,ダイジェストメールの中身の並びに一貫性がありません.正確に言うとディスカッショントピックスの並び,です.たとえば,送られてきたmoodle.orgからのダイジェストメールのディスカッショントピックスの並びですが,

Using Moodle -> フォーラム -> Forum module -> New Posts Aren't Always...
Using Moodle -> フォーラム -> General developer forum -> Overloading...
Using Moodle -> フォーラム -> General developer forum -> Ideal way to...
Using Moodle -> フォーラム -> Messaging -> Instructor not getting email...
Japanese -> フォーラム -> Moodle開発者フォーラム ->コース省略名でURI指定...
Using Moodle -> フォーラム -> Installation problems -> Strict Standards...
Using Moodle -> フォーラム -> General developer forum -> Linux zip/unzip...
Using Moodle -> フォーラム -> Forum module -> Instructor Not Receiving...
Using Moodle -> フォーラム -> Backup and Restore -> Whole Site Database...
Using Moodle -> フォーラム -> General developer forum -> Problems with...
Using Moodle -> フォーラム -> Blogs -> Blog Privacy and Permissions...
Using Moodle -> フォーラム -> General developer forum -> Plan for the...
Japanese -> フォーラム -> Moodle開発者フォーラム ->...
Using Moodle -> フォーラム -> General developer forum -> Conditional...
Using Moodle -> フォーラム -> Backup and Restore -> Auto back up not...
Using Moodle -> フォーラム -> General developer forum -> new moodledoc...
Japanese -> フォーラム -> 日本語文字化け対応フォーラム ->課題のファイル...
Using Moodle -> フォーラム -> General developer forum -> Critical:...
Using Moodle -> フォーラム -> Messaging -> Email not sent when a new user...
Using Moodle -> フォーラム -> General developer forum ->Surprise,Category...
Using Moodle -> フォーラム -> Blocks -> Contact Form block updated for...
Using Moodle -> フォーラム -> General developer forum -> how to find from...

こんな感じですね.コースも入り乱れていますし,フォーラムの順でもない.尚,ディスカッショントピックスに対する返信はきちんとpostidでソーティングされています.

 これが第一の問題点(正確に言うと問題点ではなく,不満な点).

 もう一つは添付されてくるHTMLファイルに改行が全く無いこと.HTMLファイルとしては問題が無いのかも知れませんが,実際にはemail_to_user()で添付ファイルとして送信される前に強制的に改行が行われているようです.

 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $replyto='', $replytoname='', $wordwrapwidth=79)

の宣言に対して,

email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, '', '', $CFG->forum_replytouser)

このように添付ファイルではなく,HTMLメールの本文として送っています.実際には,以下のようなヘッダーでテキストの本文と区切られています.

  --b1_72526fc534a7c0d7853208a956d0649f
  Content-Type: text/html; charset = "ISO-2022-JP"
  Content-Transfer-Encoding: quoted-printable

したがって1行の文字数の制限に合わせて分割されます.fs_moodleではオリジナルのワードラップを改善して単語単位ではなく文字単位で改行を入れられるように改善したのが仇となって,HTMLの途中で寸断されます...その結果,ごく稀にHTMLとしてエラーになることがあるようです.

Tatsuya Shirai への返信

第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

 まだデバッグ中です.デバッグが1日単位なのでなかなか進みません(取り掛かり始めたのは昨日).
 (fs_moodleでは,1日に1回しか送信されないダイジェストメールの中身をいつでも閲覧できる機能を装備してあるので,実際には1日に何度でも確認できますが,本来のコードをいくつもバイパスしているので,完全に同一の処理ではありません)

 結構簡単だ,と思って昨晩仕掛けた改良の成果を今朝確認したのですがダメでした.ポイントはmod/forum/lib.phpのfunction forum_cron(),後半部です(前半部ではダイジェストメールではなく,都度送信の処理を行っています).

 

            rs_close($digestposts_rs); /// Finished iteration, let's close the resultset

            // Data collected, start sending out emails to each user
            foreach ($userdiscussions as $userid => $thesediscussions) {

                @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes

                $USER = $cronuser;
                course_setup(SITEID); // reset cron user language, theme and timezone settings

                mtrace(get_string('processingdigest', 'forum', $userid), '... ');

                // First of all delete all the queue entries for this user
                delete_records_select('forum_queue', "userid = $userid AND timemodified < $digesttime");
                $userto = $users[$userid];

(中略)

// (Shirai170): フォーラム投稿のダイジェストメール内のディスカッショントピックスの並び順を整列させる改良 (2009/10/01)
// (Shirai170): ここから追加
                if (count($thesediscussions) > 1) {
                    $discussions_work = array();
                    foreach ($thesediscussions as $discussionid) {
                        $discussion = $discussions[$discussionid];
                        $forum      = $forums[$discussion->forum];
                        $course     = $courses[$forum->course];
                        $idnumber  = str_pad($course->id,   10, '0', STR_PAD_LEFT);
                        $idnumber .= str_pad($forum->id,    10, '0', STR_PAD_LEFT);
                        $idnumber .= str_pad($discussionid, 10, '0', STR_PAD_LEFT);
                        $discussions_work[$idnumber] = $discussionid;
                    }
                    sort($discussions_work);
                    $thesediscussions = $discussions_work;
                }
// (Shirai170): ここまで追加
                foreach ($thesediscussions as $discussionid) {

                    @set_time_limit(120);   // to be reset for each post

                    $discussion = $discussions[$discussionid];
                    $forum      = $forums[$discussion->forum];
                    $course     = $courses[$forum->course];
                    $cm         = $coursemodules[$forum->id];

                    //override language
                    course_setup($course);

(中略)

                    $postsarray = $discussionposts[$discussionid];
                    sort($postsarray);

                    foreach ($postsarray as $postid) {
                        $post = $posts[$postid];

 青で示したループは,一番外側がユーザごとの処理です.各ユーザの未読投稿をまず収集します.次はそのユーザの未読投稿の属するディスカッショントピックスごとに処理を行います.問題はここで,特にディスカッショントピックスの順番には頓着しないでデータベースから拾ってきた未読投稿の順番か何かで早い者勝ちとしています.そして最後のループがそのディスカッショントピックスに属する未読投稿を抽出して並べなおします.これはsort($pstarray)で行っていますね.多分,postidの順番でしょう(=投稿日時順).

 赤い部分が追加したコードです.$thesediscussionsは配列で,キーなしでディスカッショントピックスのIDが格納されています.ディスカッショントピックスのIDはディスカッショントピックスが投稿されるたびにシーケンシャルに生成される独立した整数ですので,コースやフォーラムに無関係です.ですので,このディスカッションIDから辿ってフォーラムID,コースIDを取得しています.それを元にして10文字×3の30文字のユニークなID(コースID10文字+フォーラムID10文字+ディスカッショントピックスID10文字)を生成します.それをキーとした配列に$thesediscussionsの中に格納されている$discussionidを格納し,ユニークなIDでソートします.ユニークなIDの上位の桁がコースID,その次がフォーラムIDですので,まずコースごと,その次にフォーラムごとに並ぶはずです.ソートし終えたディスカッショントピックスIDの配列を$thesediscussionsにコピーして戻せばきちんと整列された順番でメール本文が作成されるはず,と睨んだわけです.

 昨日の日中にデバッグした時はOKだったのに,今朝届いたメールでは期待した順番に並んでいませんでした.まだ何か見落としがあったのかどうか.デバッグのためにはあちこちのコースに沢山の投稿をしないといけないので大変です^^;

 でも,成功したら効果は大きいですね.

 

Tatsuya Shirai への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

 分かりました...

 sort()関数は配列の要素の値でソートするのですね...キー(ユニークなID)でソートしなければいけないので,正しくはksort()でした.昨晩,デバッグした際に,並び順が変わり,しかもコース,フォーラムごとに分かれたのでOKだと思ったのは,偶然だったのでしょう.

 今晩,sort()をksort()に変えて試してみます.

Tatsuya Shirai への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

 Trackerに提案するにはユニークなIDを生成するあたりが(効率は良いが)トリッキーな気がしたので,多次元配列に変えてみました.

                if (count($thesediscussions) >= 3) {
                    // ディスカッショントピックス3つ以上の場合のみソートの意味がある.
                    $cfd = array();
                    foreach ($thesediscussions as $discussionid) {
                        $discussion = $discussions[$discussionid];
                        $forum      = $forums[$discussion->forum];
                        $course     = $courses[$forum->course];
                        $cfd[$course->id][$forum->id][$discussionid] = $discussionid;
                    }
                    // キーでソート
                    $thesediscussions = array();
                    ksort($cfd);
                    foreach ($cfd as $cfd_forum) {
                        ksort($cfd_forum);
                        foreach ($cfd_forum as $cfd_discussions) {
                            ksort($cfd_discussions);
                            foreach ($cfd_discussions as $cfd_discussion) {
                                $thesediscussions[$cfd_discussion] = $cfd_discussion;
                            }
                        }
                    }
                }

これで数日,様子を見てみます.うまく整列するようならばTrackerに投稿しましょう.

 正直なところ,ksort()による整列三箇所は,個人的には不要だと思うのですが...大した負荷ではないので機能として残しておきます.日によってコースやフォーラムの並び順が異なるとイライラする几帳面な人向けです.


 この並べなおした$thesediscussionsを使って新ためてforeachするよりも,この三重のforeachの内側に,その後の処理を組み込んだ方が効率良いですし,付加価値の高い処理になるのですけれども.具体的には,ダイジェストメールの行区切りが現状ではディスカッショントピックスごとになっているのを,コース,フォーラム単位でまとめることができます.そこまで改良したバージョンも試して見ましょうか.

Tatsuya Shirai への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

 Moodle Trackerに投稿しました.(MDL-20542)

Tatsuya Shirai への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Mitsuhiro Yoshida の投稿
画像 Developers 画像 Particularly helpful Moodlers 画像 Translators
今回は、白井先生のMoodle Trackerへの投稿 (MDL-20542) に対して、Martinさんの対応が凄く早いですね。笑顔
Mitsuhiro Yoshida への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

 正確に言いますと,指導(diffファイルをzip書庫の中に入れていた)をされた訳ですが^^;

 特にMartinさんのように多くのフォーラムをウォッチしなくてはならない人にとって重要な改善であると気付いて頂けると良いですね.近頃,moodle.orgから送られてくるダイジェストメールをほとんど目を通していません.なにせ読みにくいですからね.今回の改善が取り込まれ,HTMLメール形式で受け取れば,もっとダイジェストメールが有意義なものになるのは間違いないですね.Using Moodleのようにフォーラムが大量にある(しかもそのほとんどが未読の山)と,フォーラムを開く元気が萎えて行きます...


 おっと,コメントが付いていましたね.でも不調(?)のようですね.

 あれ,今までほとんどダイジェストメールは利用していなくて,今回少し試してみた.もっと購読するフォーラムの数を増やして様子を見る,との発言ですね^^;

Tatsuya Shirai への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

今朝,moodle.orgから届いたダイジェストメール,


=====================================================================
 
Using Moodle -> フォーラム -> Installation problems -> Error: Database
connection failed. It is possible that the database is overloaded or otherwise
not running properly. The site administrator should also check that the
database details have been correctly specified in config.php


このタイミングでエラーが出ると,もしかして悪影響が出たのではないかと不安になりますね.まだ整列されていないようですので無関係だとは思うのですが...

Tatsuya Shirai への返信

Re: 第1の問題点:コース,フォーラムの順で並べたい

- Tatsuya Shirai の投稿

 本日届いたメールで,ほぼコース,フォーラム,ディスカッショントピックスによる整列がmoodle.orgのダイジェストメール送信機能に組み込まれたという確証が得られたのですが,やはりメールが尻切れトンボですね...


=====================================================================
 
Using Moodle -> フォーラム -> Assignment module -> Can you share
questions with other teachers?

Can you share questions with other teachers? 2009年 10月
21日(水曜日) 10:22 - jeff ciaccio の投稿
---------------------------------------------------------------------
 
=====================================================================
 
Using Moodle -> フォーラム -> Ass


前回と異なり,変なところで切れています.どうすればこういうことが起きるのか.PHPmailer側の処理か,ワードラップ絡みか.

Tatsuya Shirai への返信

第2の問題点:HTMLメールに改行がない

- Tatsuya Shirai の投稿
 この問題は,今朝届いたメールを詳細に調べて初めて気付きました.意外と一筋縄ではいかないですね.いっそのこと,添付ファイルにしてしまってはいけないものなのでしょうか?
Tatsuya Shirai への返信

Re: 第2の問題点:HTMLメールに改行がない

- Tatsuya Shirai の投稿

 そうか,HTMLメール部の整形(ワードラップ)にはオリジナルのMoodleが持つワードラップ機能を使うようにすれば良いのですね.なんとかなりそうです.

 完全には理解していなかったようですね.$messagetextだけを日本語を考慮してワードラップ処理を行い,$messagehtmlには一切,手を入れない.そもそも$messagetextは,

    if ($messagehtml && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it
        $mail->IsHTML(true);
        $mail->Encoding = 'quoted-printable';           // Encoding to use
        $mail->Body    =  $messagehtml;
        $mail->AltBody =  "\n$messagetext\n";
    } else {
        $mail->IsHTML(false);
        $mail->Body =  "\n$messagetext\n";
    }

ここでしか使われていないのに,$messagehtmlなどを付加した$mail->Bodyまでautocut998()という自前の関数で整形していたところに問題がある.HTMLメールならば変なところで改行されてもあまり問題になりません.ただし,完全にそうとは言えません.998byteに近い日本語の文章(半角空白による切れ目なし)の後にHTMLタグが半角空白による切れ目なしに続くと,HTMLタグの途中で強制改行される危険はありますね.完璧なものを求めるのは難しいですね.

Tatsuya Shirai への返信

Re: 第2の問題点:HTMLメールに改行がない

- Haruhiko Okumura の投稿
エンコーディングの問題(UTF-8文字の中にはISO-2022-JPに変換できないものも多い)もあるので,いっそのことUTF-8のままBase64にしてしまえばどうでしょう?

Base64にすることによって33%ほどサイズが増えますが特に問題にならないと思います。

問題は,古生代のメールソフトを使っている人ですが……。
Haruhiko Okumura への返信

Re: 第2の問題点:HTMLメールに改行がない

- Tatsuya Shirai の投稿

 確かに.わざわざISO-2022-JPに変換しないでも,ブラウザはUTF-8に対応しているのですから,そのまま送ることができれば良いですよね.

 ただ,HTMLメールとはなんぞや,が,まだ分かりません.単にHTMLファイルを1つ添付しただけではダメですよね.”HTMLメール”で検索をかけても「HTML反対派」のページばかりです^^; 手元に届く他のHTMLメールを見てみても,BASE64などでエンコードされているものはありません.

 私の使っている(学校で推奨されている)メールクライアント,AL-MAILもHTMLメールには対応していません.プラグインを追加して”外部ビューワで開く”を選択しないといけない.それに対して携帯電話のメールクライアントはHTMLメールに対応しています.それで「ああ,HTMLメールも結構悪いものじゃないのだなぁ」と近頃は思い始めました.

 とりあえず以下のページしか情報源がありません.

http://cast-a-spell.at.webry.info/200707/article_9.html

何か良い情報がありましたら教えていただけないでしょうか...

Tatsuya Shirai への返信

Re: 第2の問題点:HTMLメールに改行がない

- Haruhiko Okumura の投稿
すみません,私のさきほどの書き込みは特にHTMLメールの問題じゃなくて,UTF-8+Base64にすれば文字コードと行の長さの問題が解決できるというだけのことでした。

HTMLメールについては,text/htmlパートだけのメールと,text/plainパートとtext/htmlパートが両方含まれているメールがあります。ここ(moodle.org)からのHTMLメールは後者(マルチパート)ですね。さらにUTF-8にするとBase64ではなくquoted-printableになるみたいです。quoted-printableでも改行の問題は避けられますが,日本語の場合,ファイルサイズが大きくなるので,Base64のほうがいいのですけれど。

AL-Mailでもマルチパートならtext/plainだけ表示するのではないかと思います(UTF-8やBase64に対応していればの話ですが(もし対応していなければtext/plainパートだけISO-2022-JPにすればいいですね))。

もっとも,プロファイルのところでHTMLメール/テキストメール,ISO-2022-JP/UTF-8が選べるという前提であれば,すべての場合を尽くしたコードを書かなければいけないので,やっぱりややこしいですね。うーん。
Haruhiko Okumura への返信

Re: 第2の問題点:HTMLメールに改行がない

- Tatsuya Shirai の投稿

 ありがとうございます! かなりクリアになってきました.ポイントはquoted-printableですね.それを元にして調べてみたら頭がスッキリしてきました.まず,Moodleから送られてくるダイジェストメールですが,HTML形式を指定している場合は,

代替テキスト(AltBodyかな)
Content-Type: text/plain; charset = "ISO-2022-JP"
Content-Transfer-Encoding: quoted-printable

に続いて,
HTMLメール本文
Content-Type: text/html; charset = "ISO-2022-JP"
Content-Transfer-Encoding: quoted-printable

で送信されてくるのに対し,テキスト形式のみを指定している場合は,

テキスト形式のメール本文のみ
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="ISO-2022-JP"

である.そして一行の文字列の長さに制限があるのは後者のみで,quoted-printableでエンコードされている場合は実際に送信される際にエンコードされた状態で整形のための改行が入るが,デコードされた際にはその改行は消える,ということですね.なるほど.では一行の長さを考慮に入れてワードラップ処理を行う必要があるのは,テキスト形式のダイジェストメール送付を希望している場合のみである,と.私の方では,HTMLメールの場合であってもHTMLメールの代替テキストに対してワードラップ処理を行っていました.これ,不要ですね.さらに不安に感じていた,HTMLメール本文がHTMLタグなどの途中で改行されてしまうようなことは,(Moodleが正しく実装されているのであれば)発生し得ないわけですね.とても安心しました.


quoted-printableに関する情報

http://ja.wikipedia.org/wiki/Quoted-printable
http://e-words.jp/w/quoted-printable.html

Tatsuya Shirai への返信

Re: 第2の問題点:HTMLメールに改行がない

- Tatsuya Shirai の投稿

 確認しました.文字列のワードラップを行わないでも,HTMLメールを受け取る設定にしている場合は,代替テキストもHTMLメール部も全角1200文字の文章を改行ナシで受け取ることができました.
(メッセージ機能ではテキストメールしか送れませんので,quickmailブロックで送信テストを行いました)