知人のサイトをWordPressで作成しました

知人から依頼を受けて、WordPressでサイトを作成しました。WordPressはインストール済み、デザインもすでに作ってあるとのことなので、行なうことは主にCMSとしての組み込み作業とレスポンシブ対応。継続的にメンテしてほしいという話でもないので手離れよくまとめる必要があります。寿司職人に中華料理を作らせるみたいな畑違いの依頼ですが、どうなるか。

ログイン画面。これが気に入らないと始まらない。

ダッシュボード。MODXの管理画面のようなデザインにカスタマイズしました。

フォントも見やすくしてます。WordPressの管理画面は最新のv4.7でもMSゴシックなのでちょっと・・・

投稿画面。シンプルに構成しました。

技術資料を管理画面内に設置。私以外の技術者さんがサイトをスムーズに管理できるように、要点をまとめました。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>[*pagetitle*] - [(site_name)]</title>
  <meta name="description" content="[*description*]">
  <link href="[(theme_url)]style.css" rel="stylesheet">
  [(wp_head)]
</head>
<body class="[+body_class+]">

テンプレートはMODXみたいな感じ。PHPコードを全く使わず、HTMLだけで構成してます。WordPressが苦手なデザイナーさんでも、これならなんとなく管理できると思います。テンプレートは全て管理画面内で編集できます。

http://forum.modx.jp/viewtopic.php?f=34&t=1695
自作のプラグイン。これを使うと、MODXみたいなテンプレートワークができます。上記フォーラムで配布しているのは参考出品程度の機能しかありませんが、時間があれば改善していきたいです。

<?php
/**
 * The template for displaying all pages.
 */

if(!defined('WPINC')) exit('error');

$result = '';
if(is_front_page())     $result = $ex->parse('tpl/page_top.tpl');
elseif(is_page())       $result = $ex->parse('tpl/page_other.tpl');
elseif(is_attachment()) $result = $ex->parse('tpl/page_pict.tpl');
elseif(is_404())        $result = $ex->parse('tpl/page_404.tpl');
else                    wp_safe_redirect(get_option('siteurl'));

if(is_404()) header('HTTP/1.0 404 Not Found');
if($result) echo $result;
return;
?>

テーマファイルの中身はたったこれだけ。PHPファイルがごちゃごちゃ混じってる感じがないので、管理しやすいはず。WordPressではあまり使われないindex.phpをエントリーポイントとして使っています。single.php や 404.php などの、他のテンプレートファイルは全く使いません。

ヘッダやフッタなどのパーツはWordPressでテーマを作る際はほぼ必須だと思いますが、細かくパーツ分けすると分かりにくくなるので、それも使ってません。でもちゃんとwp_head()などは処理してます。

URLとテンプレートファイルの紐付けをテーマが行なって、テンプレートの中身の展開はプラグインが行なうという役割分担です。

テーマの構成ファイルが少なくて、コードの行数も少なく、プラグイン数も最小限。このサイトは基本的に一年に一度しか更新しないのですが、極端ですが、一年ぶりにログインしても迷わないようにしました。

「これだけで動くの?」と思うくらいスカスカかも。こういう特殊な作り方をすると「設計した本人しか理解できない」ということになりがちなんですが、そうならないように構成できたと思います。

他、セキュリティ面にいろいろ配慮してます。Acecpt-Languageヘッダの有無をチェックして、ログイン画面はボットに見せないようにとか。また、WordPressはmod_rewriteがコア領域にも及ぶのですが、手当たり次第に探りを入れてくるボットがいちいちインスタンスを起動して、それ自体の負荷でDoS攻撃が成立してしまうことがあるくらいなので、そのへんの対策もやっておきました。

RewriteEngine On
RewriteCond %{HTTP:Accept-Language} !^ja [NC]
RewriteRule ^(wp-admin|wp-includes)/ - [R=404,L]
RewriteRule ^(wp-admin|wp-includes|wp-content)/.*$ - [L]

たったこれだけ。これがあるとないとでは大違い。

本家版MODX Evolution 1.0.12リリース

http://modx.com/blog/2013/09/13/evolution-1.0.12-released/
本家版MODX Evolution 1.0.12がリリースされました。日本語版は基本的に同日リリースを守ってきましたが、今回は検討事項がいくつかあるため、数日遅れます。開発が再開して以来、状況が大きく変わりました。

以下、本家版1.0.12の改善内容について日本語版との比較も踏まえて説明します。

Ability to change folder MANAGER ( rename the folder and define the path in assets/cache/siteManager.php)

managerディレクトリのディレクトリ名を変更できるようになりました。セキュリティ面において重要な改善のため日本語版でも対応する予定ですが、いくつか気になる点があります。

まず、ディレクトリ名をデファインするためだけにsiteManager.phpという名前のファイルを専用に作る仕様がすっきりしません。管理したい情報はディレクトリ名以外にもあるので、汎用的な「define.inc.php」などのファイル名がよいと思います。

日本語版ではsitePublishing.idx.phpがこれに近い役割を持っていますが、このファイル名もよくありません。とりあえず見切り発車で、本家版はこのファイル名のままでいきます。

気になる点としてもうひとつ、書き換え可能とするために分離すべきパス情報はmanagerだけではなく、少なくともセキュリティ面だけを見ても、コアは無視できないと思います。コア処理を行なうファイル群は、できればブラウザからはアクセスできないディレクトリ階層に置きたいです。

  • MODX_CORE_PATH
  • MODX_MANAGER_PATH

両方が必要です。

この考え方を進めると、管理画面ファイルに関係するディレクトリとコア処理に関係するディレクトリを自由に定義できるので、ひとつのコアで複数のMODXを管理するような実装も可能になります。Revolutionではコンテキストという仕組みで実現されていますが、他サイト・他ユーザの情報が混じってしまう危険性があるため、慣れていないと扱いづらいです。

Evolutionの場合は、もしマルチサイト管理を実装する場合はログインレベル以前から完全に分離する方向で検討することになります。コアと管理画面まわりのファイルのみを共有し、データベースと拡張機能まわりはサイト単位で別物とします。

もっと論理的な手法はあると思いますが、熟練のプログラマーじゃないとピンとこないような実装は避けたいし、かといって設定チョイチョイで済ませるようなブラックボックス実装もメンテナーとしては扱いにくいし、そうすると選択肢があまり残りません。致命的な欠陥を抱えるような仕様でなければ、残された選択肢で十分と思ってます。いいアイデアがあれば、教えていただければ参考にしたいです。

Replacement MCPUK on KCFinder

コア内蔵のファイルブラウザー(通称mcpuk、正式名はfbxp?)KCFinderに変更しました。便利なツールなので歓迎するユーザが多いと思いますが、処理が少し複雑なため脆弱性のチェックが全く追いついてません。実際にはmcpukのほうが複雑ですが、こちらはシンプルに整理していける見通しがついています。

KCFinderはiPadなどのタッチデバイス操作に対応してないので、この点をいずれ改善する必要があります。たとえばファイル選択を長押し対応できればよいですが、このへんどう書いていいのか正直よく分かりません。

こういった機能はユーザの好みの問題も大きいですし、プラグイン化できるように仕組みを工夫したいです。

Added the ability to send messages via SMTP

なんとか実装しましたが、まだ若干の不具合があります。SMTPsの考え方がよく分かってなかったので、そのへんの実装が原因でうまく送信できないことがあるみたいです。また、SMTPはもともと送信主詐称がしにくいようになっているので、PHPのmail関数ほど自由には扱えないことも周知する必要があります。

Added a snippet Phpthumb

phpThumb自体が古く構造も複雑、処理も実際重いので、日本語版では採用しないと思います。この種のライブラリを採用するとしたらTimThumbです。WordPressの一件で脆弱なイメージが定着しているみたいですが、ちゃんと理解して実装すれば安全です。

Added the ability to save cache with the $_GET (optional)

日本語版ではすでに標準仕様として対応しています。

Added ability SEO Strict URL in the nucleus (optional)

nucleusというのはコアのことですね。翻訳ソフトの翻訳間違いのようです。このオプションを有効にすると、カノニカルなURLに正規化することができます。たとえばID指定でリソースを開こうとしても、必ずエイリアスに変換してURLを展開し、301ヘッダを出力してからリダイレクトします。SEO Strict URLsプラグイン同等の機能です。プラグインを使う場合と比べると、負荷の大きさが全く違ってくるので、コアの機能として内蔵する意味は大きいです。

まだ実装が甘いため、条件によっては機能しません。日本語版では、修正してから機能を取り込みます。

Added and set a new default theme MODxRE

管理画面テーマのデザインが新しくなりました。日本語版ではRevoStyleというテーマ名で、だいぶ前から採用しています。MODxCarbonのデザインを新調した時は好意的な感想がほとんどでしたが、今回は不評が時々見られるのが少し気になります。控えめなデザインのほうがよいかもしれません。

Added the ability to exclude a parent document from a URL

これはどういう機能かというと・・・特定のリソースのエイリアスを、エイリアスパスに含めない設定です。URL構成にこだわりたい時には便利な機能です。でもこの機能を使うと、同じエイリアスパスを持つ別のリソースが複数発生する可能性が出てきますね。日本語版での採用は当面保留になると思います。需要があるのは分かるので、何らかの形で実現できるようにしたいです。Revoでは、リソース編集画面に新設されたFix URIというフィールドが使えますね。

Removed all queries mysql now all over DBAPI ( next step MySqli and PDO to choose from)

生のmysql_関数をDBAPI関数に置き換えました。日本語版では1.0.5Jの頃に置き換えを完了しています。PHP5.5が普及してくる頃にはmysql_関数を撤去する必要がありますが、DBAPI関数への置き換えを先に済ませているので、少ない工数で対応できます。

Notification about errors on the mail run a network

エラーが発生した時にメールで通知する機能。日本語版では実装済みです。

$modx->getIdFromAlias()

エイリアスパスからリソースIDを取得する新規関数です。日本語版では実装済みです。Evoの負荷軽減を今後進める上で、重要な役割を担う関数となります。

$modx->db->optimize()

任意のテーブルの最適化を行なう新規関数です。日本語版では1.0.12Jで採用します。

$modx->sendmail()

メール送信機能を手軽に実装するための新規関数です。デフォルト値を工夫しているので、極端ですが$modx->sendmail($_POST)と書くだけでも届きます。与えられた引数が文字列か配列か、文字列ならアットマークを含むかどうかなどで柔軟に挙動が変わります。

New events for plug-ins :
OnLoadDocumentObject
OnManagerMainFrameHeaderHTMLBlock
OnManagerPreFrameLoader
OnManagerFrameLoader
OnManagerTreeInit
OnManagerTreePrerender
OnManagerTreeRender
OnManagerNodePrerender
OnManagerNodeRender

OnManagerNodePrerenderとOnManagerNodeRender以外は日本語版でも対応済み、または1.0.12Jで対応します。

https://raw.github.com/modxcms/evolution/v1.0.12/install/changelog.txt
リリース記事に書かれたもの以外では、上記のような改善が積み込まれています。かいつまんで、さらに説明します。日本語版では解決されているものも多いです。

[#10180] ForgotManagerPassword – Improvement reset url

ログイン画面で「パスワードを忘れた場合」をクリックした時に送信される、FMPプラグインのメールに含まれるリンク。このリンクは日付が変わるまで有効で、何度でもノーパスで管理画面にアクセスできます。外部に漏れると危険なので、一度アクセスしたら無効になるようにしました。

[#10142] database name with “-“

ハイフンを含むデータベース名の場合、カテゴリーを正しくインストールできない不具合を修正しました。

[#4305] Webuser/Manageruser records don’t have “City”

ユーザ・ウェブユーザの設定項目として「市」「町名」などを追加しました。また、「性別」の選択肢として「その他」を追加しています。

[#9888] add CodeMirror plugin

CodeMirrorプラグインを追加しました。ユーザの好みが分かれるプラグインなので、日本語版では採用しないと思います。

本家版でプラグインをいろいろとプリインストールするのはそれなりに理由があって、日本語版のようにコピー・ペーストだけでシステムイベントを登録できるようになっていないので、とりあえずインストールさせておいて、不要であれば削除するなり無効にするなりすればよい、ということになっています。

[#9706] Convert language files encoding

言語ファイルをほぼ全てUTF-8化しました。UTF-8に揃えることで、GitHubやTransifexなどのコラボレーションツールを安定して利用できるようになったので、翻訳活動に参加しやすくなりました。さっそく、ポーランド・オランダ・イタリアから最新の翻訳をいただきました。

[#9707] Translate error messages

エラーメッセージを翻訳できるようにしました。日本語版ではだいぶ前から対応しています。

全体として見ると、保守的な調整が多い日本語版と比べて、1.0.9以降の本家版の変化はアグレッシブです。まともに動作するかどうか検証しきれていない新機能もあります。差分を追うことで仕様上の問題ごと抱える可能性があり、互換性を保つこと自体に意味がなくなっているため、日本語版としては今後の追従を慎重に行なう必要があります。地味で実用的な多数の機能に関しては、どっちかというとだいたい本家版が日本語版を追ってる感じですが・・・

本家版も、いったん追加した新機能を元に戻すことがあり得ます。

開発中のMODX Evolution 1.0.10

管理画面デザインの変更を柔軟に行えるようになります。今まではフレームの高さ・横幅を変更できませんでした。ログイン画面やダッシュボードのデザインを変更したい場合はファイルを書き換える必要がありましたが、これもテーマの切り替えと連動できるようになります。分かりやすいようにひとつのディレクトリ内にまとめて収める構成にしています。

本家版MODX Evolution 1.0.9がリリースされました

MODX Evolution 1.0.9 Restarts the Little Engine that Could

リトルエンジンというのはEvoの小規模なパーサのことを指しているのかと思いましたが、そうではなくて、アメリカで有名な童話イメージになぞらえているみたいです。壊れたベテラン機関車の代わりに、大切な荷物を届ける使命を負った小さな機関車。彼は「こんなの簡単、僕ならできる!」と言い聞かせながら峠を乗り越えて使命を果たします。

というわけで、新生EvoチームによってMODX Evolution 1.0.9をリリースすることができました。「古くて複雑な構造のCMSの修正を実現した!」みたいな話になってますが、実際のところ、修正できるのに「しなくていい」と長期に渡って阻んでいたのは誰かと・・

チームを牽引する機関車役を務めているのは、ウクライナのドミトロ。東欧・ロシア圏ではEvolutionの支持が高く、本家で配布されてないような便利なスニペットなども豊富に流通しています。

今回の件は、ドミトロがライアンに「私が鍵を預かる」と伝えたことがきっかけです。LLC代表のライアンとしても、かれこれ一年近く「門番」を探していたので、やっと念願がかなったことになります。日本チームは率先して名乗り出るべきでしたが、日本はシェアが低くて難しい。ロシア語圏はEvoの市場がそこそこ成り立っています。

ドミトロを筆頭として、日本の自分、ドイツのジャコ、ロシアのAgel_Nashの4人で新しいEvoチームを構成しています。お互いに尊敬し合える最高のチームワークです。
※注・Agel_Nashは参加スタンスの違いにより現在は脱退しています。チームに属さず、個人として自由な立場からコミットしたいということのようです。

実際の開発は、トラッカーとSkypeチャットがベース。基本的にはトラッカーにチケットをガンガン並べて片っ端から処理していきますが、難しい話はSkypeに持ち込まれて、その場で修正します。スピード感があります。

MODXの近況

2013年中にMODX3をリリースすると宣言されたのが去年の5月か6月の話。その直後から、ほぼ毎月行われていたRevolutionのアップデートが停滞し、Cloudサービスの充実に注力するようになりました。

http://modx.com/
現在のサイトのイメージを見ても分かるとおり、本家MODXチームは、「開発チーム」というよりサービス提供業者としてのスタンスを強めています。

パッケージのダウンロードはいちおうできますが、控えめな表現ですし、

パッケージのダウンロードページでもCloudのアピールのほうが目立ちます。うっかりTry it Freeのほうをクリックしてしまいそうです。今からMODXを使う人はCloudのほうを試してみてくださいということでしょう。

でも月額が少し高い。数千円くらいします。しかもインスタンスを管理する画面は全部英語。日本人が使うには今のところメリットが少ないです。スケールアップやスケールアウトには対応してないし、メリットとしてはMODXサイトをいくつでも簡単に作れて複製も簡単、ということくらいです。一年ほど様子を見てましたが、ちょっと厳しいんじゃないかというのが正直な感想です。

http://modx.jp/news/20130113.html

MODX3はどうなってるのかとライアン(リーダー)に聞いたところ、

http://www.kendoui.com/
Kendo UIの採用を検討していたが、採用しないことになった。という返事が返ってきたのみです。どうも、ExtJSと大差ないみたいなんですね。MODX3のコードは、まだ一行も書かれていないみたいです。もう3月ですので、今からMODX3を出すとしたら、アルファに近い状態か、Revolutionの見た目を変えた程度のものを出すのが精一杯だと思います。去年の5月頃から言ってた話なので、かなり遅れています。

MODXといえば、世界的には今はRevolutionです。Evolutionが普及してるのは日本とロシアくらいのものです。でも日本とロシアのスキルが低いのかというと、むしろ逆ではないかと思うことのほうが多いです。

最近になってですが、Evolutionの開発を再開するから手伝ってほしいと声がかかりました。発起人はウクライナのドミトロ氏。ロシア版Evolutionの作者です。ライアンを説得したみたいです。ドミトロ氏のサポートを務めるアジェル氏の技術が高く、彼ら2人が今後のEvolutionの命運を握ると言っても過言ではないと思います。

1.0.6・1.0.7・1.0.8は、最低限の消極的な修正しか行われませんでしたが(一行しか変わってないとか・・)、現在整理中の1.0.9はアグレッシブな改修が進んでいます。

リーダーのライアンは「どんどんやってくれ!」と乗り気ですが、技術チーフのジェイソンがどう出るか。Revolutionの開発に専念していただいて、Evolutionのことは全部任せていただけたらと思うのですが。

かつて新世代型CMSとして注目を浴びたMODXですが、この数年は低迷しています。よくできたシステムなので私はずっと使うつもりですが、できれば順当に普及してほしいなと思います。あれこれ戦略的に考えるのが、かえってまずいんじゃないでしょうか?

MODXの高速化

MODX Evolutionは動作の軽さを特長とするCMSです。デフォルトでインストールされるコンテンツ構成で確認したところ、ページ出力に用いられるメモリ量は、WordPressは17.5MBに達しますが、MODX Evolutionは1.2MB程度しか使いません。

さらに調整を重ねた結果、キャッシュ処理を改善することで、わずか15KBまでメモリ消費を落とせることが分かりました。標準で備わっているキャッシュ機構と比べると多少制限がありますが、通常のサイト運用なら問題を感じることは少ないと思います。$_POSTで値が渡ってきた場合や、管理者としてログインしている場合は、自動的に標準のキャッシュ機構にスイッチしますので、eFormやQuickManagerを使うぶんには影響ありません。

今回作ったキャッシュ機構は、キャッシュの中身を生成するところまでは標準のキャッシュとほとんど同じです。違うのはキャッシュのファイル名です。ファイル名を工夫し、URIと直接紐づけたので、リクエストを受けると同時に対象キャッシュのファイル名を特定できます。やってることは単純で、URIをmd5関数でハッシュ化した文字列をファイル名にしているだけです。だから処理も軽い。

ただし必要に応じて、ある程度の動的処理ができるようには配慮しました。MODX設置ディレクトリにautoload.phpというファイル名で処理を置いておけば、これを自動的にロードして、キャッシュを読み込んだ直後に処理を実行することができます。任意のトリガー文字列をリアルタイムの時刻で置き換えるくらいは簡単にできます。autoload.phpで値をfalseで返せば、キャッシュの出力をスキップして通常のパース処理に進みます。

MODX Evolutionには、ページ数が増えると負荷が高くなるという問題もあります。これについては半年ほどかけて大幅に改善してきましたが、根本的には変わってません。5000ページくらいなら問題ありませんが、1万ページを超えると35MB近いメモリを消費します。

これを改善するために、Revolutionと同様のアプローチでキャッシュ機構を見直す予定です。具体的には、documentObjectにフィールドをひとつ追加します。SQLクエリの発行回数は少し増えるかもしれませんが、プラスマイナスで見れば、小規模サイトにおいてもマイナスを期待できるレベルで実装できると思います。うまくいけば、100万ページ規模のサイトでも数百KB程度の軽い負荷で駆動できることと思います。

https://github.com/dmi3yy/modx.evo.custom

ロシアチームもキャッシュの改善に積極的に取り組んでいて、ある程度成功しているらしいと聞いています。コードを見たところ、迂回が多い処理のように見えます。コアの変更をできるだけ避けるためと思われます。

Zen Cart日本公式サイトをMODX化しました

Zen Cart日本公式サイトはPukiWikiを使って運用されていましたが、これをMODXに載せ替えました。

HTMLを書かずに手軽にガンガン更新できるのがwikiのいいところですが、現在のコアチームで手を動かせる人は(私も含めて)PukiWiki記法に慣れておらず、逆に扱いにくいです。PukiWikiに慣れているベテランメンバーは、忙しくてほとんど動けません。

簡単にサイトを管理できればなんでもいいのですが、とりあえず私はMODXが得意なのでMODXへの載せ替えを提案しました。手を動かせる人が他にいればWordPressやMTを採用していたかもしれません。

まず普通にMODXをインストールします。次にテンプレート。PukiWikiもテンプレート制御でページを出力していますので、切り抜いてテンプレートを作るのに数十秒。

あとは、各ページで該当ブロックをコピー・ペーストするだけです。コンバートプログラムがあれば楽ですが、どのみち目視でチェックする必要はありますし、40ページ程度なのでチェックも兼ねて手作業で移します。

「使用エディター」は「なし」にしておきます。

Firebugを使って、対象領域を選択します。

コピーし、MODXの投稿画面に貼り付けます。あっという間に1ページ完了です。

投稿画面のアクションバーのモードを「次の作成」にしておくと、ひとつのページが終わるたびに新しい投稿画面を開く操作を省くことができるため、リズムを崩さず次々にコピペ作業を繰り返すことができます。

全部のページをコピーし終えたら、site_contentデータをSQLダンプとして落としてテキストエディタで開き、いろいろ調整します。正規表現なども使いますし、慣れてないと難しい作業かもしれません。いろいろありますが、個人的には、PukiWikiの出力するタグが冗長なのが気になりました。

この時、ちょっとしたコツがあります。サーバ上のassets/backup/ディレクトリにSQLデータを置き、WinSCPやFileZillaなど、テキストファイルを直接的な操作で更新できるタイプのクライアントツールを用いて、こまめに上書き保存。更新するたびに、MODX管理画面のリストア画面のスナップショットタブで「このデータに戻す」を実行し、更新を反映します。

失敗したと思ったらテキストエディタの ctrl + z で戻していけばいいだけなので楽です。

  • RSSフィードはDitto
  • サイトマップはWayfinder
  • サイト内検索はGoogle CSE。MODXにはAjaxSearchという拡張機能がありますが、今回は使いません。なかなか高機能なんですが、score判定を行なわないため実用的ではないからです。

全部を移し替えるのに、2時間か3時間くらいでした。

MODXの管理画面全体をフルカスタマイズする方法

簡単なプラグインを作ることで、MODXの管理画面を自由にカスタマイズできます。

投稿画面。上記の例では、ManagerManagerを用いて、パソコン操作に不慣れな担当者さんでも扱いやすいように、文字は大きめ、入力項目数は最小限にしています。機種依存文字を使ってますが管理画面の中だけのことなので特に気にしてません。

ページの一覧。

今回の記事は、そのまわりのヘッダ領域とサイドバーのカスタマイズについてです。

実装の考え方

OnManagerPageInitイベントにフックして任意のHTMLコードを出力する、簡易プラグインを作ります。MODXの管理画面は、全域において、ひとつのエントリーポイント(コントローラ)を通じて出力を行なう合理的な仕組みになっていて、URLを見るだけでちょっとしたヒントが得られます。

if($action!=1 || $_GET[‘f’]!==’tree’) return;

基本的には上記のように書いた後で任意のHTMLコードを出力し、最後に後続の処理をキャンセルするだけです。後続の処理をキャンセルしないと、不明なアクションが実行されたとしてエラーがログに蓄積されます。後続の処理というのは、今回のようにアクション指定を無視する場合は、実際にはこのログを蓄積する処理だけなので、きっちり作りたい場合はログ蓄積の処理を書くとよいでしょう。

$actionは$_GET[‘a’]として読み替えることもできます。どちらも同じです。

次回リリース予定のr12では、上記の処理を考慮し、空処理を行なう998番アクション・999番アクションが追加されます。999番は本当に何も出力しませんが、998番ではデフォルトのヘッダコード・フッタコードを出力しますので、MODXの管理画面デザインに合わせた画面出力が簡単にできます。
コードの出力も、echoではなく$modx->event-output()が使えるためすっきりします。

$actionへの値のセットは、

  • global $action;$action=999;
  • $_GET[‘a’] = 999;

どちらでもよいのですが、global宣言を使うのも$_GET変数に代入するのも、あまりスマートじゃないですね。

  • $modx->event->setGlobalVariable(‘action’,999);

1.0.5J-r11からは、上記のメソッドで値をセットできます。これを使いましょう。

if($_SESSION[‘mgrRole’]!==’2′) return;

実用的には、上記の判定を加えてロールごとに管理画面を切り替えるとよいでしょう。そうしないと、管理画面を作る作業自体が面倒です。

// OnManagerPageInit
if($_SESSION[‘mgrRole’]!==’2′) return;
if($action!=1 || $_GET[‘f’]!==’tree’) return;
$modx->event->setGlobalVariable(‘action’,998);
$resource = $modx->getDocument(137,’content’,0);
$modx->event->output($resource[‘content’]);

総合するとこんな感じになります。参考程度に見てください。

MODXの管理操作は、設定に関してはCUIを用いる部分も多く、画面を一度作ってしまえば、あとはコピー・ペーストで簡単に使いまわせるため便利です。

HTMLコードは、リソースを用いてMODX自体で管理するといつでも簡単に修正できるので便利です。スニペットも使えるので、なんでもできる気がします。

画面製作の際、HTMLの記述を誤ったりすると、管理画面操作ができなくなってしまうことがあります。このような場合は、r11から追加された$modx->safeModeを設定すると、プラグインのロードを全てスキップすることができます。manager/index.php内にコメントアウトの状態であらかじめ記述していますので、困った時はここを一時的に書き換えるとよいでしょう。

他のCMSでこういうことをやろうとすると、それなりにしんどい作業になると思います。


For english

// OnManagerPageInit
if($action!=1 || $_GET[‘f’]!==’tree’) return; // In the case of top navigation, $_GET[‘f’] is ‘menu’.
echo <<< EOT
<base target=”main” />
<link rel=”stylesheet” type=”text/css” href=”media/style/MODxCarbon/style.css” />
<h2>Resources</h2>
<ul>
<li><a href=”index.php?a=4″>New resource</a></li>
</ul>
<h2>Tools</h2>
<ul>
<li><a href=”index.php?a=17″>Configuration</a></li>
<li><a href=”index.php?a=93″>Backup</a></li>
</ul>
<h2>Other</h2>
<ul>
<li><a href=”index.php?a=28″>Change Password</a></li>
<li><a href=”index.php?a=8″>Logout</a></li>
</ul>
EOT;
exit; // Important


Resources list view is Japanese edition only.

MODXのログイン画面をプラグインでカスタマイズ

MODX Evolution 1.0.5J-r8以降で有効な方法です。

manager/media/style/MODxCarbon/manager/login.html

上記のファイルをテキストエディタで開き、その内容をチャンクの作成画面に貼り付けます。チャンク名は「ログイン画面」など適当につけてください。続いて、チャンク内のHTMLコードを好きなように編集します。スニペットなども使えます。

global $tpl;
$tpl = $modx->getChunk(‘ログイン画面’);

上記のような内容のプラグインを作って、OnManagerLoginFormPrerenderイベントにフックします。

できあがりです。

ログインできなくなった時は

記述ミスなどのためログインできない場合は、以下のように対応します

assets/cache/siteCache.idx.phpをテキストエディタで開きます。

プラグイン名を探します。今回はチャンク名と同じで「ログイン画面」としています。

適当にリネームします。またはこの行ごと削除またはコメントアウトしてもよいです。

デフォルトのログイン画面が表示されるので、ここからログインして修正作業を行なうことができます。

1.0.5J-r7以前のバージョンの場合

assets/templates/manager/

1.0.5J-r7以前のバージョンの場合は、上記ディレクトリ内のファイルを編集してカスタマイズできます。1.0.5J-r8以降でもファイルでカスタマイズを行ないたい場合は、login.htmlというファイル名で作ると同じようにできます。

MODX Evolutionのページ出力の処理の流れ

MODX Evolution 1.0.5J-r11で、処理の流れがほぼ確定しました。r10以前と比べて、違うのはindex.phpの処理。パスの取得やconfig.inc.php読み込みのタイミングなどに問題があり、汎用的な使い方をする上で問題があったため整理されています。

index.phpの中では、以下の処理が行なわれます。

  1. ベンチマークに必要な初期情報をセットします。
  2. 設置ディレクトリにautoload.phpというファイルがあればインクルード。MS-DOSのautoexec.batのようなイメージで、管理者が必要に応じて自由に使います。
  3. protect.inc.phpを読み込んで、PHPコードを安全に扱うための基礎的な無害化処理を行ないます。危険なデータが入ってこないように、ここでガードします。
  4. initialize.inc.phpを読み込んで、MODXのコントロールに必要なパス情報を生成します。
  5. config.inc.phpを読み込んで、データベースアクセスとセッション生成に必要な情報を取得します。この時点ではまだデータベースに接続しません。
  6. set_parser_mode()関数を実行し、パース展開のモード設定を行ないます。ページ表示を行なうのか管理画面アクセスを行なうのかをここで決定します。
  7. startCMSSession()関数を実行し、セッションを開始します。ここから、CMSとしてのシステム制御が始まります。
  8. DocumentParserオブジェクト($modx)を生成します。この時点で、MODXが備えるAPIを全て利用できるようになります。
  9. $modx->executeParser()が実行され、ページ出力の処理が始まります。ただし、MODX_API_MODEがセットされている場合は、executeParser()を実行せず、DocumentParserオブジェクトにアクセスできるようになった時点で処理が終了します。

executeParser()の処理を説明する前に、DocumentParserオブジェクトを生成する際の初期化処理について説明します。

  1. $_REQUEST[‘q’]の値をFIX。フレンドリーURLの処理に必要となります。この処理は1.0.5J-r11で加わったもので、mod_rewriteを使わずここで処理することで、URLで用いると特殊な扱いを受ける「 + 」などの文字や、日本語エイリアスを正確に扱うことができます。
  2. DBAPIオブジェクトを生成。この時点ではまだデータベースに接続しません。
  3. SystemEventオブジェクトを生成。プラグインのコントロールに必要です。
  4. $modx->dumpSQL$modx->dumpSnippets$modx->stopOnNotice をセット。デバッグ作業に関係しますが、このタイミングでは無効にします。必要に応じて、後のタイミングで有効にして使用します。
  5. ini_set(‘track_errors’, ‘1’)を実行し、PHPが出力するワーニングやエラーをCMS側で巻き取り、イベントログとして蓄積します。
  6. 管理画面にログインしていない場合は ini_set(‘display_errors’,’0′)を実行し、PHPのエラー表示を抑制します。本家版ではこの処理に関係なく、詳細なエラー情報を一般ユーザに対して出力するため注意が必要です。

$modx->executeParser()以降の処理は、下記のとおりです。大きな流れとしては、executeParser()・prepareResponse()・outputContent()・postProcess()の順に処理が実行され、スクリプトが終了します。

  1. executeParser() – ここで初めてデータベース接続が行なわれ、configの取得を行ないます。サイトステータスの判定によりエラーページの出し分けを行なった後、通常のページ出力であればdocumentMethodのFIXが行なわれ、フレンドリーURLモードが確定します。リソースIDを取得した直後に、最初のシステムイベントOnWebPageInitが実行され、さらに、config[track_visitors]が有効な場合はOnLogPageHitイベント(※非推奨)が実行されます。
  2. prepareResponse() – ページ出力に必要な情報の取得が行なわれます。まず、ページキャッシュがあればそれを取得しOnLoadWebPageCacheイベントを実行、そのままprepareResponse()の処理を終えます。OnLoadWebPageCacheイベントを実行する時点でdocumentObject(リソース変数・テンプレート変数)は取得されており、ここで自由に扱うことができます。
    ページキャッシュがない場合はデータベースにアクセスし、documentObjectを取得します。そのページの公開状態・削除状態・権限状態に応じたページ遷移判定を行ない、通常のページ出力として判定されれば、ウェブリンク判定を経てからテンプレートコードとdocumentObject[content](本文です)の取得を行ないます。その直後でOnLoadWebDocumentイベントが実行されます。OnLoadWebDocumentイベントが実行されるタイミングで、パース処理前の documentObject[content] の内容が $modx->documentContent にセットされます。直後にパース処理が実行されます。
  3. outputContent() – 最終的なページ出力処理が行なわれます。この時点では、キャッシュの有無に関係ない処理の流れに戻っています。まず、非キャッシュ書式のスニペットコールが残っていれば、これを展開します。続いて、APIを通じて登録されたCSSやJavaScriptがある場合は、これをページ内に挿入します。次に、documentObject[content_dispo]がセットされているリソースであれば、これをダウンロードダイアログに引き渡して関数を終了します。通常のページ出力である場合は、ベンチマークタグの処理を行ない、その値をセットします。最後にシステムイベントOnWebPagePrerenderを実行し、その直後にページの内容を全て出力します。
  4. postProcess() – ページ出力後の処理を行ないます。まずOnBeforeSaveWebPageCacheイベントを実行し、ブラウザに出力されたdocumentContentデータとシリアライズされたdocumentObjectを結合し、これをページキャッシュとしてファイルに書き込みます。最後にOnWebPageCompleteイベントを実行し、ここで全ての処理が終了します。