2007年12月アーカイブ

今年最後のプラグインは、メールからポストできるプラグインMooberっす。Mooberってのは、Boomerひっくり返したんですけど、ネーミングのセンスねぇって!? 放っておいてくだせぇ。

携帯から添付ファイル付き投稿できるMTOS用プラグインをGPLで公開しますです。

投稿用にひとつメールアドレス設定してください。run-periodic-tasks実行時にメールチェックしにいって、そのアドレス宛に投稿されてたらエントリーに反映して再構築します。添付ファイルはちゃんとエントリーに貼付けてMT::Assetに登録もしますぜ。

Net::POP3とMIME::Parserが必要です。

送信元アドレス例えば投稿する携帯電話のメールアドレス
送信先アカウント投稿用メールのアカウント
パスワード投稿用メールアカウントに対するパスワード
メールサーバ投稿用メールのメールサーバ
プロトコルPOP3 or APOP
データベースの文字コード例:utf8
サムネイル幅添付画像の幅がこの数値を超えていた場合自動的にサムネイルを作成
サムネイル代替テキストデフォルトは「Click for larger view」つまりサムネイルクリックでオリジナルを表示になるわけですが、その画像のalt属性
テンポラリファイルの保存場所作業用ディレクトリ(このプラグイン専用に作成してください)
添付ファイルの保存先添付ファイル(画像)及びサムネイルの保存場所
ブログID投稿先のブログのID
投稿者ID投稿者のID
公開状態「2」であればポストしたエントリーは公開状態で保存
再構築チェックを入れた場合、メール受信→エントリー保存後に再構築を行う
画像の配置「テキストの前に画像を配置する」か「テキストの後に画像を配置する」か

公開がこの時間になったのは「通信各社」に配慮したためです(嘘)。来年もひとつよろしくお願いします。

後継のプラグインはこちら

MT3.xではMT::Entryにステータス1:HOLD「未公開(下書き)」ステータス2:RELEASE「公開」、ステータス4:FUTURE「指定日」という3つのステータスがあって、MT4ではそれぞれ「下書き」「公開」「日時指定」にラベルが変更されている。で、MTOS/MT4.1βでは「未公開(原稿)」「公開」「日時指定」というようにまたまた微妙に変更になっている。

実はMT3.0の時点で既にもうひとつのステータス3:REVIEWってのがあって、MTOS/MT4.1βでは「未公開(原稿)」に対して「未公開(承認待ち)」というラベルがついている。

つまり、その名の通り「原稿」→「承認待ち」→「公開」という流れでコンテンツを管理出来るようになっているのだけど、これが最終的なMTOS/MT4.1βで有効になるのか、はたまたEnterprise 向けのものなのかは分からない(よく考えたら先日Enterpriseを購入したのだった。ちゃんと活用しないといけませんね)。

エントリーのステータス

まぁ、何に使うかはなんだけど、せっかくだから使えるようにしてみた。

実際のワークフローに組み込むとなると権限の細かな制御は必要かと思うけど、運用のルール上「原稿(ドラフト) 」と「承認待ち」が分けて管理できるだけでも、それはそれで使えるシーンがありそうなものだし。

年末は再構築を考えるってことになってるんですけど(嘘付け!) っていうか、去年の12月31日に公開したんですよね。年末なのに何やってんだろ。

で、今度はRebuildAt1stViewの拡張としてバックグランドで再構築を走らせるようにしてみた。

/RebuildAt1stView/BackgroundRebuilder.cgiのPerlのパスを環境にあわせて、実行パーミッションを与える。あとは管理画面で設定してください。ドキュメントはいずれ近いうちにまとめます。

RebuildAt1stView設定

エントリー保存の時

  • エントリーを保存→再構築(関連するファイルを削除)
  • 保存完了画面へ戻る→バックグラウンドタスクを走らせるCGIへリクエストを投げる
  • (この間にアクセスがあったらRebuildAt1stView.cgiが物理再構築処理)
  • エントリーと関連するファイルをバックグラウンドで物理再構築

全再構築処理の時

  • 全再構築処理→ファイルを削除
  • 保存完了画面へ戻る→バックグラウンドタスクを走らせるCGIへリクエストを投げる
  • (この間にアクセスがあったらRebuildAt1stView.cgiが物理再構築処理)
  • 物理再構築タスクと同等の処理(FileInfoデータを元に再構築タスクをバックグラウンドで走らせる→ファイル構築と構築の間には一定の待ち時間を設定出来るので、負荷をさげたい場合は待ち時間を設定してゆっくりと再構築)

ダウンロード

残りタスクとしては、複数のプロセスを走らそうとしたときの処理とか、そのあたり。

テンプレート一覧への状態表示

ブログ一覧への状態表示

タスク実行状況表示

今回はインターフェイス周りのみです(後、再構築のリトライをするようにしましたけど)。最終的にはDBの再構築後にバックグランドプロセスで物理再構築を走らせるところまでやりたいと思っていて、多分やるんでしょうね!

RebuildAt1stView(PHP版)。

| コメント(0) | トラックバック(0)

別にPHP版つくる必要性感じてないんですけど(MTには既にダイナミックパブリッシング用のキャッシュの仕組みが組み込まれていますし)、現状の使用だと.htaccessがぶつかるのでダイナミックとRebuildAt1stViewの共存が出来ない(ブログを分けるとかやりようはあるけど)ので何となく書いてみました。

何だか流行りみたいなので(<<お前だろ!)

当然ながらレンダリング結果はMTのPHPアーキテクチャを使ったものになります。

mtview.phpを上書きするテンプレート


<?php
    include('<$MTCGIServerPath$>/php/mt.php');
    $mt = new MT(<$MTBlogID$>, '<$MTConfigFile$>');
    ob_start();
        $mt->view();
        $output = ob_get_contents();
    ob_end_clean();
    echo $output;
    $url = $mt->request;
    $pattern = '/¥/$/';
    if ( preg_match($pattern, $url) ) {
        $blog = $blog = $mt->db->fetch_blog(<$MTBlogID$>);
        $url .= $mt->config('IndexBasename');
        $url .= '.';
        $url .= $blog['blog_file_extension'];
    }
    $data = $mt->resolve_url($url);
    $info = $data['fileinfo'];
    $file_path = $info['fileinfo_file_path'];
    if ( ! file_exists($file_path) ) {
        if (!($fh = fopen($file_path, 'w'))) {
            return;
        }
        fwrite($fh, $output, 128000);
        fclose($fh);
    }
?>

おまけ

MT4で「真の」Perl版ダイナミック・パブリッシングを実現するよ(ただしM専)

このエントリーで私が示したかったのは、MT::WeblogPublisherに依存することなく、アーカイブのレンダリングとファイルの生成を「分離できること」である。改造版のMT::App::Viewerは、blog_idとURIだけを入力として受け取り、対象のアーカイブをオンメモリでレンダリングし、その結果をas isでブラウザに対して出力する。

「FileInfoを食べてレンダリング結果を返すメソッド」ってのがあれば確かに色んな実装が楽になりますね。Viewer.pmを使って書き換えてもいいけど、まぁ今回のはファイルを生成出来ればいいし「ステージングサーバーでのファイル生成用途」ってのが作ったきっかけなのですけど(どっちにしても「もっさり」してますが)。

でも、僕はむしろ「プレビュー」に活用したいですね。一時ファイルを作らないプレビュー、且つエントリー以外の例えばインデックスアーカイブもファイルを生成すること無くプレビューできるっての。

年末の宿題にしようかなぁ。既に宿題いっぱいだしなぁ...

車輪の再発明ってことは分かってるんだけど、受託の仕事してると目の前の顧客案件の課題をクリアしないといけないんだよ。先のエントリーの書き方は何だか申し訳なかったのでごめんなさい。MT4は再構築やスピードアップに関して様々な手段を提供しています。

だけど情報不足だったり環境によってうまく動作しない場合もある。もっときびきび動いて欲しいというニーズは確かにあるのです。

まずは新たなバージョンのダウンロードは以下

任意のタイミングで再構築したいていう要求はやっぱり出て来たし、実装の方針は固まっていたのでやってみた。

プラグインの設定画面

Backgroundプロセスが使える環境ならタスクをそのまま実行してしまえばいいわけだが、そうでない環境(今回の案件のような場合)では何らかの手段を提供しないといけないし、そのためのインターフェイスを実装してみた。

メニュー:静的再構築

静的再構築の実行中

fileinfoテーブルと実際にファイルが吐き出されているかどうかを比較して、ファイルが存在しなかったらファイル生成。

設定はなんだか想像つくと思いますが、今はゆとりが無いのでコードとキャプチャのみ晒しておきます。

MT4重い!

(注)ある案件(環境)の話ですからね。あくまでも。

いや、重いですよ本当に。例えばこのサイトの検索、同じプログラムをMT3/MT4で動かしてもはっきりその差がわかる。っていうか、500エントリー程度の某サイトでエントリー保存時にWindowsサーバーのCPUが100%って...どうすりゃいいんだというこの2日だったので対策を施してみた。

施策1.FastCGI化

結論からいうと駄目だった。管理画面の操作はきびきびと動くようになったがやはりCPU100%でエラー頻発。あと一定時間経過すると何故か文字化け。何故だろう複数の環境で遭遇したよ。

施策2.Memcached

これも駄目だった。とにかくCPU100%になってしまうとどうしようもない。

施策3.ダイナミックパブリッシング化

これは実は最初から無理だった。CMSとしてステージングサーバー上で使っていて、出来上がったファイルを公開サーバーにコピーする必要があるっていう要件だったのだ。だからファイルが出来ないのは無理。

施策4.バックグラウンドのキューを使って再構築を行う

使っている人どんだけいるんだろうか? リアルタイムに確認出来ないってことでこれも要件満たせないから今回は駄目。エントリー保存後、バックグラウンドキューを発行するしくみを作っていたのだがこれもうまく動かない。このあたりでWindowsサーバーが嫌になる。サーバー変えようよってな気分に。

施策5.テンプレートの最適化

一定の効果はあったものの根本的な解決にならず。そもそも要求に応え続けた結果、ブログをまたがって再構築するわエントリーのコピーを別ブログに自動生成するわ、1エントリーの保存と同時に10も20もファイル吐いてたらそりゃ重いわな。

施策6.Greg Packer's Publishingの導入

OK! これでクリアできたよ。軽い! 再構築せずにファイルを削除するだけだからそりゃ軽い。ただし施策3と同じくファイルが出来ないと要件クリア出来ない。ということでファイルの再構築をまとめて出来るようにしたよ。

Greg Packer's Publishing については以下のエントリーを参照ください。

ということでRebuildAt1stViewプラグインバージョンアップ!

導入/設定をより簡単に。

ブログ単位で使用/不使用が選択出来るわけだが、プラグインアクションで一括設定可能にしたよ!

ブログ毎の設定

テンプレート毎にリアルタイムで再構築するか最初のアクセス時に再構築するかを選択可能なのだが、これもプラグインアクションで一括設定可能にした。なのでcssとかJavaScriptとかは普通に吐き出すようにできる。

テンプレート毎の設定

タスク実行によってファイルをまとめて生成可能に

これが目玉? ということで、run-periodic-tasks実行で存在しないファイルはまとめて再構築できるようにした。再構築は1ファイル単位、再構築と次の再構築の間の待ち時間も設定できるようにした。これで、夜中に一回、昼休み時間に一回再構築を行って、その後本番サーバーへ反映させる、ってなことができるようになった。もちろんGreg Packer's Publishingだから更新した結果はブラウザでちゃんと確認できる。

再構築間隔の設定

ダウンロード

ライセンス

GPL/商用のデュアルライセンスとします! MTOSではGPL、MT4.0(4.1)では商用ライセンスとします(ウチの会社で扱っているプラグインセットPowerCMSの1.1(MT4.1リリースと同時にリリース予定)にバンドルしますので、ライセンス購入いただいている方へはサポートもいたします!(ってことでいいんですよね!? 識者の皆様)


ライセンスの件ってやっぱり結構悩ましいかな。例えばMT4iってGPLですよね。商用MTにリンクするってのはありなんだろうか。WebSigの時の小川さんのスライドを改めて見直していますが、どうなんでしょうね? MTOSの姿も見えただけに、このあたり明確になると動きやすいなぁ...

TemplateHammerプラグインというものがあって、これを使うとMT4で導入されたモジュール化されたデフォルトテンプレートをフラット化してくれる。

fujimotoさん的には↓ということですが僕の推測はちと違っていて、FastCGIやMemcachedが使えない環境で少しでも再構築の負荷を軽減するためにフラット化するってことなのかなぁと。

おそらく、SixApartに「モジュール化されたテンプレートは分かりにくい」という声が多く寄せられたのではないかと思われます。 そして、それにこたえるために、TemplateHammerプラグインが作られたのではないかと推測します。

* またこんなこと書くと「賢くキャッシュ使えよ」って怒られそうですが(^^;

えっと、話を戻してfujimotoさんの言う「分かりにくい」ってのは人によるみたいで、ウチの連中はもう慣れちゃったみたいで何だかうげぇ、こんなにIncludeが入れ子になっててこの案件のサーバーCPUがほら100%なってるのに...ってなことがあったので (かといって、フラット化しろっていうと口が尖るし...) 書いた。

* TemplateHammerプラグインがインストールされている必要があります。

利用方法について意識する必要は無い筈です。テンプレートを保存すると自動的にフラット化されますが、管理画面に表示されるソースはフラット化される前の状態(モジュール化された状態)のもので、編集もそのまま行えます。

また、テンプレートの「親子関係」を覚えておいて「子供」が更新されたら「親」テンプレートも再度フラット化し直すようになっています。

ダウンロード

ご利用は自己責任でお願いします。テンプレートのバックアップとってから利用してください。何かあっても責任持てないので。あとTemplateHammer プラグインのライセンスがGPLなので、このプラグインについても利用に制限は設けません。

MTとは何だ?

MTって(あなたにとって)何? って聞かれたなら、僕は『MTとは「MT::Object」である』と答えるかな。

loadメソッドは間違いなく最も複雑なメソッドです。

とあるけれど、これこそが MTの本質なのですよ。きっと。

MT::Entry とか MT::Blog とか MT::Category とかみんな MT::Object のサブクラスであり、オリジナルのクラスを作ることも簡単にできる。

MT::Object についてちょっと書いてみる。

MTOSによって、これがオープンソースになるのです。だからMTOSは「ただで使えるブログソフト」じゃない。そんなことをちょっと書いてみたい。ここから、MT中毒患者の告白です。

#!/usr/bin/perl -w
use strict;
use lib '/path/to/mt/lib';
use MT;
my $mt = MT->new(Config => '/path/to/mt/mt-config.cgi');
print "content-type: text/plain; charset=utf-8¥n¥n";

my @entries = MT::Entry->load();
 
foreach my $entry (@entries) {
    print $entry->title . "¥t" . $entry->permalink . "¥n";
}

すべてはここから始まるっていうか、最初はこれでPermalinkを出力してPHPで読み込んでDBを操作ってやってたんだね。1年程前は。それが今じゃすっかりヘビーユーザーですよ。ということで MT::Objectの扱い方についてちょっと掘り下げてみるよ。

このスクリプトでやっていることはすべてのエントリーのタイトルとURLをタブで区切って出力する。それだけ。MT::Entry->load();でつまりすべてのエントリーを読み込んでいる。

(この後は面倒なので先頭の6行は省略します。)

では次。BlogのIDが「1」で「公開の状態」が「公開」(下書きじゃなくて) のエントリーだけを読み込む場合。

my @entries = MT::Entry->load({ blog_id => 1,
                                status  => 2,
                                });
 
foreach my $entry (@entries) {
    print $entry->title . "¥t" . $entry->permalink . "¥n";
}

以後、最後の3行も省略します。同じなので。

status => 2 ってのは「公開」状態を指すのだけど、以下のように書くことも出来る。

MT::Entry::RELEASE()

即ちMTで定義されているステータスが「公開」なエントリー (ブログ記事) ということね。つまり以下のように書くことができる。

my @entries = MT::Entry->load({ blog_id => 1,
                                status  => MT::Entry::RELEASE(),
                                });

ついでに。blog_id => 1 って決めうちするのもなんだから、blogもMTに登録されているのを1つ読み込んでそのブログの記事一覧を取得するように変更してみる。

my $blog = MT::Blog->load( undef, { limit => 1 } );
my $blog_id = $blog->id;

my @entries = MT::Entry->load( { blog_id => $blog_id,
                                 status  => MT::Entry::RELEASE(),
                                } );

loadメソッドでオブジェクトを読む時は、ちと注意しないといけない。読み込むオブジェクトが半端でないレコードを持ってる時には、大量の値をメモリに読み込んでいまうので遅くなったりするのだ(なので limit => 1 としている)。お行儀良くひとつずつ読み込むには load_iter, 数を数えるだけなら count メソッドを使う。いずれにしても基本的に書き方は同じ。

以後、またまた面倒なので最初の2行は省略。

では次。件数やソート順を指定して読み込む。

my @entries = MT::Entry->load( { blog_id => $blog_id,
                                 status  => MT::Entry::RELEASE(),
                                }, {
                                 limit   => 10,
                                 sort    => 'authored_on',
                                 direction => 'descend',
                                } );

公開状態のエントリーを10件、公開日(authored_on)をキーにして descend(降順=新しい順)で取得。limit, sort, directionを指定してるね。

さてさて、さらに次。範囲を指定するその1。今年の10月1日以降のエントリーを取得。start_valで日付を指定。昇順(ascend)にすれば公開日が10月1日以降のエントリーが取得できる。

my @entries = MT::Entry->load( { blog_id => $blog_id,
                                 status  => MT::Entry::RELEASE(),
                                }, {
                                sort    => 'authored_on',
                                start_val => 20071010000000,
                                direction => 'ascend',
                              }
                              );

範囲指定その2。現在の月に公開されたエントリーだけを新しいものから10件取得する。「range」で範囲を指定する。

use MT::Util qw( offset_time_list start_end_month );

my @tl = offset_time_list(time, $blog);
my $ts = sprintf "%04d%02d%02d%02d%02d%02d", $tl[5]+1900, $tl[4]+1, @tl[3,2,1,0];
my ($start, $end) = start_end_month($ts);

my @entries = MT::Entry->load( { blog_id => $blog_id,
                                 status  => MT::Entry::RELEASE(),
                                 authored_on => [$start, $end],
                                }, {
                                range  => { authored_on => 1 },
                                limit   => 10,
                                sort    => 'authored_on',
                                direction => 'descend',
                               }
                               );

MT::Utilにはこういった日付関連のルーチンが色々用意されている。start_end_month ってので $start, $endには例えば今月なら 20071201000000 と 20071231235959 が返る。

range には カラム名と、常に「1」を指定。offset_time_list はブログに設定されているタイムゾーンを反映させた上で日付関連のデータを返してくれる。

さて次。テーブルの結合というか、別のテーブル (ここではエントリーとカテゴリーを紐づける) MT::Placement を結合して読み込む例。entry_id とplacement_entry_id で紐づける。

以下の例は今月に公開された category_id が 1 のエントリーを10件取得するもの。

my @entries = MT::Entry->load( { blog_id => $blog_id,
                                 status  => MT::Entry::RELEASE(),
                                 authored_on => [$start, $end],
                                }, {
                                join => [ 'MT::Placement', 'entry_id',
                                          { category_id => 1 },
                                          { unique => 1 }
                                        ],
                                range  => { authored_on => 1 },
                                limit   => 10,
                                sort    => 'authored_on',
                                direction => 'descend',
                              }
                              );

紐づけるためのキーは object名_id でないとうまくいかない、そういう仕様みたい(ちょっとハマった経験がある)。

こいつはつまり、SQLでいうと以下のようになる(実際にこういうSQL文をMTがDBに投げているのだ)。

SELECT DISTINCT entry_id, entry_authored_on
FROM mt_entry, mt_placement
WHERE (entry_status = '2') 
AND ((entry_authored_on > '2007-12-01 00:00:00')
AND (entry_authored_on < '2007-12-31 23:59:59'))
AND (entry_class = 'entry') AND (entry_blog_id = '1')
AND (placement_category_id = '1')
AND (entry_id = placement_entry_id)
ORDER BY entry_authored_on DESC
LIMIT 10

unique => 1 とすることで、返ってくるエントリーの重複がなくなる。例えば今月にコメントがついたエントリーを読み込む場合なんかでは重複の可能性があるので unique => 1 とする。

結局のところ、このようなPerlスクリプトであれプラグインであれBootstrapアプリケーションであれ、SQLを意識すること無くましてやDBの種類を意識すること無くオブジェクトを扱えるのが Movable Type ってもんだ。

これ、面白いよ。冒頭の話に戻ると「ただで使えるMT」って捉えてるとすんごく損するよな、Web屋としては。

文系プログラマはどのようにMTと付き合っているか?

おいらはプログラマじゃなくてスーツなんだ! って、もういいか。

とにかくこれを良く読む。これを見ながら書いてますね。あとはやっぱりソースを読むことでしょうか。といってもどこをどう見るってのは長いこと触ってると分かってくるわけだが、とりあえず grep! ってな感じでもいいんではないでしょうか。

cd /path/to/mt/lib
find . -name "*.pm"|xargs grep 'MT::Entry->load'

あと、mysqlのログもよく見るね(mysqlじゃなくてもいいんだけどね)。SQLを意識しない分うまくいかないときにハマることがあって、そんな時にどんなsqlを投げてるんじゃ! ってなことでログを見て、phpMyAdmin とかでエラーを確認したりってのも良くやるよ。

まぁ、とにかくMTOSがどうなっていくのか、そして自分はどうコミットしていくのか、久しぶりにワクワクする出来事じゃないか。

ブロガー、Web屋、プログラマの皆さん、MTOSめっちゃ面白いと思いますけどどうですかね!?

MTOSがリリースされましたね。Boomerって言うと阪急ブレーブスを思い出すって書くと年齢がばれるってな話はどうでも良くて、年末だしめっちゃ忙しいんだけどまぁこれまでさんざん騒いでたのに何黙ってんの...ってまぁどうでもいいか。

MT4.1もベータ。とりあえずPowerCMS for MTの4.1対応で正月もなさそうな予感。

で、MTOSのリリースを記念して!? MTOS用のプラグインをGPLで公開したよ。 GPLで公開するMTOS用プラグインの第一号ってのを狙ってみた? ってもう他にあるのかなぁ?

あと、以前KOF2007でMTOSについて話したんだけど結局その時はMTOSがまだリリースされていなかったのでMT一般の話になったんだけど思ったより沢山来ていただいたので配布資料が足りなくて、Webで公開しますって言ってたのがのびのびになってしまった。ごめんなさい。

Facebook

Twitter

このアーカイブについて

このページには、2007年12月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2007年11月です。

次のアーカイブは2008年1月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 6.2.6