2010年12月25日
New DynamicMTML(PHPによるMovable Typeの拡張)-2
引き続き新しいDynamicMTMLの拡張方法についてご紹介します。
PHPによるコールバックプラグイン
コールバックプラグインはDynamicMTMLの処理の各ポイントで呼び出されるコールバックイベントに対応するPHPコードです。コンテキストの内容を設定したりコンテンツやテンプレートに対して何らかの処理を割り込ませることが出来ます。
PHPによってプラグインを書くことができるため、Wordpressやその他のCMS用のプラグインを移植したりSmartyのプラグインをほぼそのまま利用することが可能です。
コールバックプラグインは/mt/plugins/PluginName/php/callbacks/以下にpluginname_callbackname.phpのような名前でファイルを設置し、function pluginname_callbackname($mt,&$ctx,&$args,&$content)という関数を定義することで呼び出されます(実行順はプラグイン名の昇順です)。
ビルド処理の後で呼ばれたコールバックでは第4引数$contentにビルドされた結果が含まれているため、&$contentとして値を受け取ることでコンテンツの内容を変更することが可能です。
DynamicMTMLの初期化直後には第3引数$argsには標準では下記の値が含まれています。これらの値は$app->stash()で取得することもできます(プラグイン内で再定義することも可能です)。
$argsに含まれる値
blog_id | ブログID |
---|---|
conditional | 条件付き取得が有効かどうか |
use_cache | キャッシュを利用する設定であるかどうか |
cache_dir | キャッシュディレクトリへのパス |
file | 現在のリクエストに対するサーバー上のファイルパス |
base | $app->base()と同様 |
path | $app->path()と同様 |
script | $app->script()と同様 |
request | 現在のURLからクエリー文字列を削除したURL情報 |
param | $app->query_string()と同様 |
is_secure | $app->is_secure()と同様 |
url | 現在のURL |
contenttype | リクエストに対するmime_type |
extension | リクエストされているファイルの拡張子 |
build_type | 'dynamic_mtml(DynamicMTML)','static_text(MTタグを含まないテキストファイル)','binary_data(バイナリファイル)','mt_dynamic(MTのダイナミックパブリッシング)' |
コールバック(DynamicMTMLの処理におけるデフォルト・コールバック)
- init_request()
アプリケーションの実行前に呼び出されます。
- pre_run($mt,$ctx,$args)
MT::get_instanceを呼び出す直前に呼び出されます。この段階では$mt,$ctxは未定義です。$argsに初期値が格納されています。
- post_init($mt,$ctx,$args)
MT::get_instanceの直後(DBへのアクセスが確立されMTが初期化された後)に$mt,$ctx,$argsパラメタを付けて呼び出されます。
- mt_init_exception($mt,$ctx,$args,$error)
MT::get_instanceに失敗した時に呼び出されます。この段階では$mt,$ctxは未定義です。$errorにはMTInitExceptionによってthrowされたエラーメッセージが格納されています。独自に定義したエラーを返したり他のデータベースへ接続をリトライして処理を続行する等の処理の実装が可能です。
- pre_resolve_url($mt,$ctx,$args)
DynamicMTML処理が行われる際に(存在するファイルをビルドするケース)resolve_urlをコールする直前に呼び出されます。この時 $args['build_type'] には'dynamic_mtml'が格納されています。
- post_resolve_url($mt,$ctx,$args)
DynamicMTML処理が行われる際に(存在するファイルをビルドするケース)resolve_urlの直後に呼び出されます。この時 $args['build_type'] には'dynamic_mtml'が、$args['fileinfo'] にはresolve_urlの実行結果(MT::FileInfoオブジェクト)が格納されます。
- pre_build_page($mt,$ctx,$args)
ページを読み込む直前(build_typeが'binary_data'の場合は値が返される直前、MTタグをビルド実行する場合はその直前)に呼び出されます。
build_typeが'dynamic_mtml'の場合、$app->stash('text')にビルドされるファイルのデータが含まれています。この内容を書き換えることでビルドされる前にテンプレートに対して処理を行うことができます。
- build_page($mt,$ctx,$args,$content)
build_typeが'dynamic_mtml(DynamicMTML)','static_text(MTタグを含まないテキストファイル)','mt_dynamic(MTのダイナミックパブリッシング)'の時にページがビルドされた直後に実行されます。第4引数$contentにビルドされた結果が格納されています。&$contentとして値を受け取ることで、ビルド結果に対して処理を行うことが出来ます。
build_typeが'binary_data'以外の場合、このコールバックの直後にクライアントに対してデータが返されます。
この時、引数$mtはSmartyを拡張したクラスMTが、$contentにはビルドされたコンテンツが入っていますので、既存のSmartyプラグイン(outputfilterやmodifier)をそのまま利用することが出来ます。
例:ホワイトスペースをトリミングする <?php function pluginname_build_page ( $mt, &$ctx, &$args, &$content ) { require_once( 'outputfilter.trimwhitespace.php' ); $content = smarty_outputfilter_trimwhitespace( $content, $mt ); ?>
- post_return($mt,$ctx,$args,$content)
クライアントへデータをリプライした直後に呼び出されます。build_typeが'binary_data(サイズの大きなバイナリファイル)'の時、$contentには何も含まれていません。
- pre_save_cache($mt,$ctx,$args,$content)
- キャッシュが保存される設定になっている時、キャッシュを保存する直前に呼び出されます。$app->stash('cache')でキャッシュのパス、$contentにキャッシュされる内容が取得できます。
- take_down($mt,$ctx,$args,$content)
すべての処理が成功したケースで処理の一番最後に呼び出されます。
- take_down_error()
MT::get_instanceに失敗した状態で最後まで処理が実行された場合に呼び出されます。
- # init_request(Basic認証をかける)
-
# /plugins/PluginName/php/callbacks/pluginname_init_request.php <?php function pluginname_init_request () { if ( isset( $_SERVER[ 'PHP_AUTH_USER' ] ) && ( $_SERVER[ 'PHP_AUTH_USER' ] === 'username' && $_SERVER[ 'PHP_AUTH_PW' ] === 'password' ) ) { } else { header( 'WWW-Authenticate: Basic realm=""' ); header( 'HTTP/1.0 401 Unauthorized' ); exit(); } } ?>
- # init_request(別のmt-config.cgiを使って初期化する)
# /plugins/PluginName/php/callbacks/pluginname_init_request.php <?php function pluginname_init_request () { global $mt_config; global $mt_dir; $new_config = $mt_dir . DIRECTORY_SEPARATOR . 'mt-alt-config.cgi'; if ( file_exists ( $new_config ) ) { $mt_config = $new_config; } } ?>
- # init_request(Perlによる動的ビルドを有効にする)
-
# /plugins/PluginName/php/callbacks/pluginname_init_request.php <?php function pluginname_init_request () { global $app; $app->stash( 'perlbuild', 1 ); ?>
- # mt_init_exception(デバッグモード指定時にエラー出力して終了する)
# /plugins/PluginName/php/callbacks/pluginname_mt_init_exception.php <?php function pluginname_mt_init_exception ( &$mt, &$ctx, &$args, $error ) { global $app; if ( $app->config( 'DebugMode' ) ) { echo htmlspecialchars( $error ); exit(); } } ?>
- # mt_init_exception(mt-alt-config.cgiの設定を使ってインスタンス生成をリトライする)
# /plugins/PluginName/php/callbacks/pluginname_mt_init_exception.php <?php function pluginname_mt_init_exception ( &$mt, &$ctx, &$args, $error ) { global $app; global $mt_dir; $config = $mt_dir . DIRECTORY_SEPARATOR . 'mt-alt-config.cgi'; if ( file_exists ( $config ) ) { global $mt_config; global $blog_id; $mt_config = $config; try { $mt = MT::get_instance( $blog_id, $mt_config ); } catch ( MTInitException $e ) { $mt = NULL; } if ( isset ( $mt ) ) { $app->configure( $mt_config ); $app->init_callback_dir(); } } ?>
- # post_init(ログイン(username,passwordパラメタによるログインと$app->userのセット)/ログアウト)
# /plugins/PluginName/php/callbacks/pluginname_post_init.php <?php function pluginname_post_init ( $mt, &$ctx, &$args ) { if ( $app->mode() == 'login' ) { $app->login(); } elsif ( $app->mode() == 'logout' ) { $app->logout(); } ?>
- # post_init(MTのユーザー名+パスワードを利用してBasic認証をかける)
# /plugins/PluginName/php/callbacks/pluginname_post_init.php <?php function pluginname_post_init ( $mt, &$ctx, &$args ) { $app = $ctx->stash( 'bootstrapper' ); if ( isset( $_SERVER[ 'PHP_AUTH_USER' ] ) && ( $app->is_valid_author( $ctx, $_SERVER[ 'PHP_AUTH_USER' ], $_SERVER[ 'PHP_AUTH_PW' ] ) ) ) { } else { header( 'WWW-Authenticate: Basic realm=""' ); header( 'HTTP/1.0 401 Unauthorized' ); exit(); } } ?>
- # post_init(MTのログイン機能を利用した認証)
# /plugins/PluginName/php/callbacks/pluginname_post_init.php <?php function pluginname_post_init ( $mt, &$ctx, &$args ) { $app = $ctx->stash( 'bootstrapper' ); if (! $timeout = $mt->config( 'UserSessionTimeout' ) ) { $timeout = 14400; } $client_author = $app->get_author( $ctx, $timeout, 'comment' ); if (! $client_author ) { $url = $args[ 'url' ]; $return_url = $mt->config( 'CGIPath' ); $return_url .= $mt->config( 'CommentScript' ); $return_url .= '?__mode=login&blog_id=' . $ctx->stash( 'blog_id' ); $return_url .= '&return_url=' . rawurlencode( $url ); $app->redirect( $return_url ); exit(); } ?>
- # post_init(http://example.com/entry_1/パス名/ へのリクエストをhttp://example.com/entry_1/index.htmlへのリクエストとして処理する)
- # 例:ブログ記事のアーカイブマップを「entry_<$mt:EntryID$>/%i」カテゴリのアーカイブマップを「category_<$mt:CategoryID$>/%i」として、テンプレート内の「<$mt:EntryPermalink$>」を「<$mt:EntryPermalink$><$mt:EntryTitle make_seo_basename="50"$>/」に<$mt:CategoryArchiveLink$>を「<$mt:CategoryArchiveLink$><$mt:CategoryLabel make_seo_basename="50"$>/」に変更することで、日本語URLによるページへのアクセスが可能になります。
# /plugins/PluginName/php/callbacks/pluginname_post_init.php <?php function pluginname_post_init ( $mt, &$ctx, &$args ) { $app = $ctx->stash( 'bootstrapper' ); $file = $app->stash( 'file' ); $url = $app->stash( 'url' ); $request = $app->stash( 'request' ); if (! file_exists ( $file ) ) { $file = $app->path2index( $file, 'index.html' ); if ( file_exists ( $file ) ) { $request = $app->path2index( $request ); $url = $app->path2index( $url ); $app->stash( 'file', $file ); $app->stash( 'request', $request ); $app->stash( 'url', $url ); $app->stash( 'contenttype', 'text/html' ); $app->stash( 'extension', 'html' ); $cache = $app->cache_filename( $ctx->stash( 'blog_id' ), $file, $app->query_string ); $app->stash( 'cache', $cache ); } } ?>
- # pre_build_page(http://example.com/entry_1/ へのリクエストをエントリー(又はカテゴリ)の名前を用いて生成したURL http://example.com/entry_1/日本語を含む_パス名(<$mt:entrypermalink make_seo_basename="50"$>)/へ恒久的リダイレクトする)
# /plugins/PluginName/php/callbacks/pluginname_pre_build_page.php <?php function pluginname_pre_build_page ( $mt, &$ctx, &$args ) { $app = $ctx->stash( 'bootstrapper' ); $request = $app->stash( 'request' ); if ( preg_match( '!/$!', $request ) ) { $file = $app->stash( 'file' ); $blog_id = $app->blog_id; if ( file_exists( $file ) && preg_match( '!/index\.html$!', $file ) ) { $fileinfo = $app->stash( 'fileinfo' ); require_once( 'MTUtil.php' ); if (! isset( $fileinfo ) ) { $fileinfo = $mt->db()->resolve_url( $mt->db()->escape( urldecode( $request ) ), $blog_id, array( 1, 2, 4 ) ); } if ( isset( $fileinfo ) ) { $app->stash( 'fileinfo', $fileinfo ); $entry_id = $fileinfo->entry_id; $category_id = $fileinfo->category_id; if ( $entry_id || $category_id ) { $obj = NULL; if ( $entry_id ) { if ( $fileinfo->archive_type == 'Page' ) { $obj = $mt->db()->fetch_page( $entry_id ); } else { $obj = $mt->db()->fetch_entry( $entry_id ); } } elseif ( $category_id ) { $obj = $mt->db()->fetch_category( $category_id ); } if ( isset( $obj ) ) { $title = NULL; if ( $entry_id ) { $title = $obj->title; } elseif ( $category_id ) { $title = $obj->label; } $title = strip_tags( $title ); if ( $title ) { require_once ( 'dynamicmtml.util.php' ); $title = make_seo_basename( $title, 50 ); $url = $request . $title . '/'; $app->moved_permanently( $url ); exit(); } } } } } } } ?>
- # build_page(携帯キャリアからのアクセス時にソースに含まれる画像をサムネイルに自動変換する)
# /plugins/PluginName/php/callbacks/pluginname_build_page.php <?php function pluginname_build_page ( $mt, &$ctx, &$args, &$content ) { $app = $ctx->stash( 'bootstrapper' ); require_once ( 'dynamicmtml.util.php' ); if ( $app->get_agent( 'keitai' ) ) { $type = 'auto'; $scope = 'width'; // $agent = $app->get_agent(); // if ( $agent == 'DoCoMo' ) { // $type = 'gif'; // } else { // $type = 'png'; // } $content = convert2thumbnail( $content, $type, 100, 480, $scope ); } } ?>
- # build_page(携帯キャリアからのアクセス時にShift_JIS変換する)
# /plugins/PluginName/php/callbacks/pluginname_build_page.php <?php function pluginname_build_page ( $mt, &$ctx, &$args, &$content ) { if ( $app->get_agent( 'keitai' ) ) { $charset = strtolower( $ctx->mt->config( 'PublishCharset' ) ); $charset = preg_replace( '/\-/', '_', $charset ); if ( $charset != 'shift_jis' ) { $pattern = '/<\?xml\s*version\s*=\s*"1.0"\s*encoding\s*=\s*"UTF-8"\s*\?>/s'; $replace = '<?xml version="1.0" encoding="Shift_JIS"?>'; $content = preg_replace( $pattern, $replace, $content ); $pattern = '/<meta\s*http\-equiv\s*=\s*"Content\-Type"\s*content\s*=\s*"text\/html;\s*charset=UTF\-8"\s*\/>/s'; $replace = '<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />'; $content = preg_replace( $pattern, $replace, $content ); $content = mb_convert_encoding( $content, 'SJIS-WIN', 'UTF-8' ); } } ?>
- # build_page(検索エンジン,サイト内検索から流入したユーザーの検索キーワードをハイライト表示する)
# /plugins/PluginName/php/callbacks/pluginname_build_page.php <?php function pluginname_build_page ( $mt, &$ctx, &$args, &$content ) { if ( preg_match( '/(^.*?<body.*?>)(.*?$)/si', $content, $html ) ) { $head = $html[1]; $text = $html[2]; require_once ( 'dynamicmtml.util.php' ); $tag_start = '<strong class="search-word">'; $tag_end = '</strong>'; $qtag_start = preg_quote( $tag_start, '/' ); $qtag_end = preg_quote( $tag_end, '/' ); $keywords = array(); $phrase = referral_search_keyword( $ctx, $keywords ); foreach ( $keywords as $keyword ) { $keyword = htmlspecialchars( $keyword ); $keyword = trim( $keyword ); $keyword = preg_quote( $keyword, '/' ); $pattern1 = "/(<[^>]*>[^<]*?)($keyword)/i"; $replace1 = '$1' . $tag_start. '$2' . $tag_end; $pattern2 = "/($qtag_start)$qtag_start($keyword)$qtag_end($qtag_end)/i"; $replace2 = '$2$3$4'; $i = 0; while (! $end ) { $original = $text; $text = preg_replace( $pattern1, $replace1, $text ); //Nest tag $text = preg_replace( $pattern2, $replace2, $text ); if ( $text == $original ) { $end = 1; } $i++; //Infinite loop if ( $i > 20 ) $end = 1; } unset( $end ); } $content = $head . $text; } ?>
- # build_page(電話番号をリンクに置換する)
# /plugins/PluginName/php/callbacks/pluginname_build_page.php <?php function pluginname_build_page ( $mt, &$ctx, &$args, &$content ) { if ( $app->get_agent( 'keitai' ) || $app->get_agent( 'smartphone' ) ) { if ( preg_match( '/(^.*?<body.*?>)(.*?$)/si', $content, $html ) ) { $head = $html[1]; $text = $html[2]; require_once ( 'dynamicmtml.util.php' ); $tag_1 = '<a href ="tel:'; $tag_2 = '">'; $tag_3 = '</a>'; $pattern1 = '/(<[^>]*>[^<]*?)(0\d{1,4}-\d{1,4}-\d{3,4})/'; $replace1 = '$1' . $tag_1 . '$2' . $tag_2 . '$2' . $tag_3; $pattern2 = '/(<a.*?>\/*)<a.*?>(0\d{1,4}-\d{1,4}-\d{3,4})<\/a>([^<]*?<\/a>)/'; $replace2 = '$2$3$4'; $i = 0; while (! $end ) { $original = $text; $text = preg_replace( $pattern1, $replace1, $text ); //Nest tag $text = preg_replace( $pattern2, $replace2, $text ); if ( $text == $original ) { $end = 1; } $i++; //Infinite loop if ( $i > 20 ) $end = 1; } unset( $end ); $content = $head . $text; } } ?>
- # build_page(ホワイトスペースを除去する)
# /plugins/PluginName/php/callbacks/pluginname_build_page.php <?php function pluginname_build_page ( $mt, &$ctx, &$args, &$content ) { require_once( 'outputfilter.trimwhitespace.php' ); $content = smarty_outputfilter_trimwhitespace( $content, $mt ); ?>
- # post_return(アクセスログを記録する)
# /plugins/PluginName/php/pluginname_post_return.php <?php function pluginname_post_return ( $mt, &$ctx, &$args, &$content ) { $app = $ctx->stash( 'bootstrapper' ); $url = $app->stash( 'url' ); if ( $url ) { $app->log( $url ); } } ?>
- # post_return(検索経由のアクセスの場合にキーワードをアクセスログに記録する)
# /plugins/PluginName/php/callbacks/pluginname_post_return.php <?php function pluginname_post_return ( $mt, &$ctx, &$args, &$content ) { $keyword = referral_search_keyword( $ctx ); if ( $keyword ) { $keyword = trim( $keyword ); $url = $app->stash( 'url' ); $referral_site = referral_site(); $app->log( "url : $url\nreferral_site : $referral_site\nkeyword : $keyword" ); } } ?>
コメントを投稿する