WordPressでメディアを追加した時のimgタグを任意に編集する方法

WordPressは色々なフックがあって楽しい。

いつもは私がプログラマーなこともあって、アクションフックばっかり触っていたが、今回は珍しくフィルターフックを使ったのだ。

うむ、フィルターフックも便利だ。

では、備忘録として事の顛末を記していこう。

メディアアップローダーから追加した画像に任意のクラスをつけたい

いつものようにはてなのホッテントリをしらみつぶしに読んでいると、デザイナーさんから相談を受けた。

WordPressのオリジナルテーマを作り終え、細かい調整を行っていた時にその悲劇は起こったらしい。

メディアアップローダーから画像を記事に追加した時に、画像の配置やキャプションを入力することができる。

画像の配置を「左」や「右」として指定すると、記事本文にimgタグが生成される。

その時にWordPressはimgタグのclass属性に「alignleft」や「alignright」というCSSクラスを自動で追加するのだが、今回は「alignleft」や「alignright」というCSSクラスを既に他の用途で定義していたようだ。

つまり、WordPressに「alignleft」や「alignright」といったCSSクラスをimgタグに付与されると、デザインが崩れてしまうとのことだった。

WordPressが自動で付与する「alignleft」や「alignright」といったクラス名を、任意のクラスに変更できれば、対処できるだろう。

既に使っているという「alignleft」や「alignright」を別名で定義しなおせば済む話だが、作業の手戻りが発生するために、なるべく避けたいという。

うーん、ならばメディアアップローダーが画像を本文に挿入している箇所をフックして、クラスを書き換えることにしよう。

メディアアップローダー関係のフック

日本語版のCodexにはメディア関係のフィルターフックについては記述がないので、英語版のCodexの以下のページを参照してほしい。

Plugin API/Filter Reference の Media Filters

英語版のCodexも記事が作成されていないが、どうやら今回使用出来そうなのは、以下のフックのようだ。

  • get_image_tag_class
  • get_image_tag

get_image_tagをフックすると、imgタグ自体を書き換えることができるので、乱暴な話、文字列の置換で自分の好きなように置換してしまえば、classの書き換えを実現できる。

/**
 * メディアライブラリから画像を追加したときに、任意のCSSクラスを追加するためのフック 
 */
function set_image_tag_class($html){
	$html = str_replace('alignleft', 'alignleft-mu', $html);
	$html = str_replace('alignright', 'alignright-mu', $html);
	$html = str_replace('aligncenter', 'aligncenter-mu', $html);
	return $html;
}
add_filter('get_image_tag','set_image_tag_class');

上記コードでは、「alignleft」というCSSクラスを「alignleft-mu」というCSSクラスに書き換えている。

デフォルトのクラス名に、サフィックスとして「-mu」を追加した形だ。

実は、デザイナーさんに相談された件はこれで解決としたのだが、先にも述べたとおり、あまりに乱暴なのでget_image_tag_classの使い方を探ろう。

get_image_tag_class フィルター

get_image_tag_class については、英語版Codexのget_image_tag関数のページで、若干だが説明されている。

フィルターの引数は以下の通りだ。

  • $class imgタグに設定されるCSSクラス。今回の書き換え対象
  • $id 添付ファイルのID
  • $align 画像の配置場所。メディアアップローダーからは'none'、'left'、'center'、'right'が渡される。
  • $size メディアアップローダーで指定された画像サイズ。'medium'や'full'など。

では、先の例と同じように、「aligncleft」というCSSクラスを「alignleft-mu」という名前のCSSクラスに変更してみよう。

function original_img_class($class, $id, $align, $size)
{
	$class = 'align' . esc_attr($align) .'-mu size-' . esc_attr($size) . ' wp-image-' . $id;
	return $class;
}

add_filter('get_image_tag_class', 'original_img_class');

「align」関係以外のCSSクラスは、デフォルトをそのまま引き継ぐように書いている。

CSSクラスをWordPressとは全く関係ないオリジナルで定義したいなら、単純に次のように書けば良い。

function original_img_class($class, $id, $align, $size)
{
	$class = 'original_image';
	return $class;
}

add_filter('get_image_tag_class', 'original_img_class');

これでimgタグに「original_image」というCSSクラスのみが設定されることになる。

どうでもいいけど……

日本語版Codexの編集アカウントを発行してもらったので、今回の様な記事は、日本語版Codexに反映させれば良いのだが……。

日本語版Codexは英語版Codexの翻訳版という扱いなのだろうか。だとしたら、面倒くさいなあ。英語苦手だから翻訳作業はできないのよね。

あくまでWordPressの知識を集約する場所という扱いなら、自由に編集するんだけどなあ。

生年月日と現在日付を使って年齢を計算する方法

上司に教えてもらったんですが、以下の計算式で年齢を計算できるんだって。

年齢 = (現在日付_yyyymmdd - 生年月日_yyyymmdd) / 10000

現在日付が2013年7月3日、生年月日が1984年12月12日とした場合に、以下の計算式を用いて年齢が計算できる。

28.9491 = (20130703 - 19841212) / 10000

小数点は切り捨ててください。

f:id:omiya6048:20130703121135p:plain

上図は誕生日近辺の値ですね。

当日で29才、前日で28才と計算されます。
関わる業務において、この辺の年齢の扱いは微妙に変わってきますので、よく確認してから使う必要がありますね。

理屈が全く分からないんですが、そこらへん解説してるページないかなあ。

入門Git買ってきた

入門Git

入門Git


↑これ。

最近、Git入門、再入門! って記事が結構出てたから、にわかにGitに興味を持っていた。

でも、使うだけなら簡単なんだよなあ。Windowsだしー、GUIって最高ね!!
今のまま、何となくで使ってても良いんだけど、何となくで使ってる内は、仕事で使ってみましょうよ!! とかは口が裂けても言えないので、もう少し体系的に学んでみようと、入門しようと思ったのです。

あと、Webサービスやら何やらの構想があって(レコメンドエンジン作ってみたいっていうのも、その内の一つ)ソースコードをちゃんと管理したいと思ったのですよ。

というわけで、しばらくこの本を読んでみて、知識が深まったらGitの記事でも書いてみたいと思うのでした。

よーし読むぞー。

レコメンドエンジンを作りたい part2

というわけで、計算ロジックをPHPにて実装してみた。

/**
 * 座標格納クラス
 */
class Coordinate
{
    /** X座標 */
    public $x;
    /** Y座標 */
    public $y;

    function __construct()
    {
        $this->x = 0.0;
        $this->y = 0.0;
    }
}

/**
 * 座標を計算する
 */
class CalcCoordinate
{
    /** カテゴリマスタ */
    private $category;

    /** 重みづけテーブル */
    private $weight_table;

    /**
     * 座標計算クラスを初期化する。
     * 引数は以下の様な配列を渡すこと。
     * $category = array("book", "food", "suply", "adults", "kids");
     */
    function __construct($category)
    {
        $this->category = $category;
        $this->weight_table = array();

        $degress = 360 / count($this->category);
        $weight = 0;

        foreach($this->category as $name)
        {
            $this->weight_table[$name] = $weight;
            $weight = $weight + $degress;
        }
    }

    /**
     * カテゴリ別のデータを渡すことで、ユーザー座標を計算して返す。
     * 座標の範囲は -100 ~ 100。
     * 引数は以下の様な配列を渡すこと。なお、コンストラクタに渡したカテゴリと項目名が違う値は無視される。
     * $data = array (
     *       "book"=>92,
     *       "food"=>38,
     *       "suply"=>67,
     *       "adults"=>55,
     *       "kids"=>44,
     * );
     */
    public function get_coordinate($data)
    {
        $total = 0.0;
        foreach($data as $ammount)
        {
            $total += $ammount;
        }

        $coordinate = new Coordinate();
        foreach($data as $key=>$value)
        {
            if (array_key_exists($key, $this->weight_table) === TRUE)
            {
                $distance = ($value / $total) * 100.0;
                $coordinate = $this->advance($coordinate, $this->weight_table[$key], $distance);
            }
        }
        return $coordinate;
    }

    /**
     * 任意の方向に前進させる
     */
    private function advance($coordinate, $degress, $value)
    {
        $coordinate->x = $coordinate->x + cos($degress * M_PI / 180) * $value;
        $coordinate->y = $coordinate->y + sin($degress * M_PI / 180) * $value;
        return $coordinate;
    }
}

前回の記事では、カテゴリを4つに固定していたが、それでは何ともいやはや。ということで、実装では複数カテゴリに対応している。

その為に、任意の方向(角度)に座標を進める必要があった。それが上記ソースコードのadvance関数である。

これがなかなか良い感じに動くので、デモプログラムを作って動作を確認してみた。

デモプログラムはSkyDriveの共有フォルダに置いておきました。

座標が分かると、近くにいるユーザーが分かります。そして近くにいるユーザーほど、関連度が高いと判断するわけです。

f:id:omiya6048:20130613221158p:plain

上表は、「井上 運吉」に近いユーザーの検索結果です。この結果の上位三名をレーダーチャートにして、視覚的に本当に似たユーザーが取れているのか確認したいと思います。

(あんまり人数が多いとレーダーチャートが見づらいので、上位三名のみにします。)

f:id:omiya6048:20130613222329p:plain

結果はこのような感じ。黒い線が井上 ウンキチ(サダヨシ?)さんです。

井上さんは、小説と絵本が好きな空想好きと言ったところでしょうか。ノンフィクションや実用書はあまり読まないタイプのようです。

一方、関連度一位の藤井 彩さんは、哲学と論文が好きな学者肌の人のようですね。井上さんとは全然違うタイプのようです。

二位の谷さんはどうでしょうか。エッセイ、生活、絵本などをよく読んでいるようなので、恐らく幼いお子さんがいるお母さんではないでしょうか。

三位の松崎さんはカルチャー、参考書、思想と読む本が大分偏っています。おそらく女子大学生(サブカル系女子)ですね。

といったように、レーダーチャートで比較すると、あんまり井上さんに似ていない気がします。

まぁダミーデータなので、他に似た人がいないという可能性もありますが、1000人分のダミーデータなので、そんなことは無いと思うんですよね。

あとはお気づきかもしれませんが、カテゴリによって進む方向が決まるので、CalcCoordinateクラスにカテゴリを渡す時に、カテゴリ順が重要になってきます。

似たようなカテゴリを隣り合わせにしておけば、座標はそっちの方向に進むので、より関連の高いユーザーを抽出できるというわけです。

二次元座標を用いた関連度はこんなところですね。

次は、先ほどレーダーチャートで分析したように購買カテゴリのジャンルの比率から関連度を計算したいと思います。

レコメンドエンジンを作りたい part1

レコメンドエンジンに興味がある。

オープンソースのレコメンドエンジンはあるものの、自分の欲しい機能だけを持ったミニマルなレコメンドエンジンが欲しい。

あと、自分でそういうのを考えるのも好きだし。

というわけで、自分でレコメンドエンジンを作ってみようかな。と思い立ったのだ。

そうは言っても、頭がわるいのであんまり高度なレコメンドは出来ないだろうが。

購入履歴から似たユーザーを探す

そういうわけで、まずは購入履歴から、似ているユーザーを探す機能を考えた。

下調べした結果、ベクトルがどーのこーのとかって難しい話が出てきたので、まずモデルを単純化して考えることに。

f:id:omiya6048:20130613003903p:plain

上図が、購入履歴だとする。ユーザーのカテゴリ別、購入件数の集計結果だ。

全体の購入件数とカテゴリ別の購入件数から、どのカテゴリの購入割合が高いかを計算する。
(カテゴリ別購入件数 / 全体の購入件数)

そして、それを元に、ユーザー座標を計算する。

今回は、考えやすいようにカテゴリを4つとしたので、次の条件でユーザー座標を計算した。

  • 文庫の割合分だけ、X座標を加算する。(90度方向に進む)
  • 新書の割合分だけ、X座標を減算する。(270度方向に進む)
  • コミックの割合分だけ、Y座標を加算する。(180度方向に進む)
  • 単行本の割合分だけ、Y座標を減算する。(0度方向に進む)

括弧書きで書いたのが、本来の考え方だ。

カテゴリが4つなので、単純にX,Y座標に対する加算と減算だけで計算できるが、例えばカテゴリが6つある場合は、60度刻みに座標を進めなくてはいけない。

(ここでベクトルが登場するわけだ。)

ベクトルとか、よく分からないので今回はカテゴリを4つにしたのだ。へへーん。頭良いだろう。(悪いよ!)

f:id:omiya6048:20130613003908p:plain

ともかく、そうやって何やかんやで、ユーザーの嗜好を座標に変換すると上図のように図示できる。

ユーザー座標が得られれば、あとは単純な範囲検索で嗜好の似たようなユーザーを取得できるというわけだ。


というわけで、今回はここまで。

次回は、実装をしてみよう。(あ、実装ではカテゴリ4つ以外も考慮に入れます)

何で実装しようかな。PHPで良いかな。WordPressのプラグインに再利用できるし。

PHPでopenssl_pkcs12_exportを使って、PEMファイルからPKCS12を得る方法

JavaでSSL通信したいのに、手元にはPEMファイルしか無い時の解決方法」という記事の続きのような感じの記事です。

先の記事では、JavaSSL通信をしたいのに手元にはPEMファイルしか無いで! 困ったわー。とか、書いてたんですけど、これは何故かっていうと元のシステムがPHPで書かれていたから。

今回は、PHPのシステムをJavaに全面的に移行するのではなく、バックエンドの一部の処理だけをJavaに移行する予定なのだ。

そんでもって、PEMファイルはユーザーがアップロードするのね。

f:id:omiya6048:20130603202016p:plain

上図は移行後のイメージ。

つまり、PEMファイルをユーザーがアップロードする時に、PKCS12に変換できればOKなのだ。

JavaでPEMファイルをPKCS12にするには、OpenSSLのプロセスをRuntimeのexecで起動するか、BouncyCastleという暗号化ライブラリを使うと書いた。

しかし、そもそもOpenSSLというのはCで書かれたラッパーなのだという。

OpenSSL(Wikipedia)
http://ja.wikipedia.org/wiki/OpenSSL

PHPもOpenSSLを使ってSSL通信を行っている。

お? ということは、JavaからOpenSSLを実行したりしなくても、既にPHPでOpenSSLが使える環境が整っているのではないだろうか。

openssl_pkcs12_exportという、そのまんまな関数があることを確認。おー、いけるで!!(ババーン)

ということで、以下の様なコードを書いて動作検証。

// cert.pemファイルにCERTIFICATEとPRIVATE KEYの両方が含まれているものとする

// Private key を取得
$privatekey = openssl_pkey_get_private("file:///usr/local/xxxx/cert.pem");

if (openssl_pkcs12_export("file:///usr/local/xxxx/cert.pem", $lis, $privatekey, "p@ssw0rd")) {
    file_put_contents("/usr/local/xxxx/cert.p12", $lis);
} else {
    echo "変換に失敗したよ!";
}

とまぁ、こんな感じで、ちゃんと動いた。

あとは、cert.p12をJavaで読み込めばいいだけ。


今回は、たまたまフロントエンドがPHPで、元々PHPSSL通信を行っていたため、OpenSSLが使えて良かった。

正直、Javaから外部プロセスの実行とか、使ったことのないオープンソースのライブラリを使うのとか面倒だったんだよねー。

Poderosaで「未サポートのエスケープシーケンスを見つけました」って言われる

MinecraftのサーバーをさくらVPSで運営しているのだけれど、PoderosaでさくらVPSに繋ごうとすると「未サポートのエスケープシーケンスを見つけました」って言われて、接続後、すぐに切断されて強制終了してしまう。

何でですかー(ババーン)

解決方法はここに書いてあった。
http://d.hatena.ne.jp/pg_sugarless/20091217/1261059949

接続設定をxtermからktermに変えればOKですって。なーんだ。
実に良い情報だった。