Category Archives: Web技術

本番環境でWordPress Themeを安全に開発できる環境を作りました

テスト環境なしの開発はさすがにない

WordPressに移行したこのブログも大分システム・データが安定してきたので、そろそろフロントを調整していくフェーズに入ろうと思います。

ところでWordPressって「外観 > テーマ編集」から直接テーマのphpファイルエディットで来たりするんですが、さすがに怖いっすww

バグったら画面真っ白ですし、下手なセキュリティホール仕込んでしまうかもしれない。当然開発は別の環境でやって、テスト済みのモジュールを本番に反映するっていうのがソフトウェア工学上の基本セオリーです。

そのために自前のMacにもWordPressインストールして、開発環境作ってもいいんですが、それって古いっす!今どきFTPとか情けない。今日パソコンってのはシンクライアントであるべきです。明日このMacが壊れてWindowsに買い替えたとしても、すぐに開発が始められるのが理想です。

概念図

ということで、下記のような環境を作りました。

my-wp-env

データ保全上は完璧ではありませんが、僕がミスっても大丈夫にするためなんで費用対効果からこんな感じにしました。

簡単な解説

Ephemeral DiskとPermenet Disk

Google Compute Engineでサーバを作ると標準で割り当てられるEphemeral Diskは揮発性が高いディスクで、インスタンスがダウンすると消えてしまいます。そこで、データなどは、データ保全性が高いPermanent Diskを追加しこちらに格納することがセオリーです。それぞれ
/
/mnt/pd0
にマウントし、Ephemeral Diskにnginx, hhvm, maria-dbなどのシステムを入れ、pd0にwordpressやmysqlのデータを配置しました。

あとの、細かい説明は割愛w

テーマの編集手順

新しいテーマを作成しようとした場合はこのようにする想定です。

まず、/mnt/pd0/themes/に新しいテーマディレクトリを作成します。おそらく元となるテーマがあるのでコピーして作成します。

$ cp -r twentytwelve susumuis-1

次に、/mnt/pd0/wp-content/themesにシンボリックリンクを作成します

$ ln -s /mnt/pd0/themes/susumuis-1 /mnt/pd0/wp-content/themes/testing

これで準備完了です。ちなみに、/mnt/pd0/wp-content/themes/には実際にテーマディレクトリは配置せず、必ず/mnt/pd0/themes/に実ファイルがあり、/mnt/pd0/wp-content/themes/は常に次のシンボリックリンクを配置します。

production
testing

あとは、productionやtestingの向き先を変えることで、現在のバージョンのテーマを切り替えていくこととします。

テスト環境へのアクセス

標準状態のWordPressでは、テーマは一度に一つしか選択できないので、このままでは本番をproductionにしつつ、testingテーマへアクセスすることができません。

そこで、不本意ですが、プラグインの力を借ります。

Theme Test Drive

というプラグインを使います。このプラグインをインストールすれば、ログイン中の管理者だけ、あるいはURLに?theme=xxx(xxxはテーマ名)を付与したときだけ、別のテーマで表示することができます。

ここで、theme=xxxという指定はプラグインのオリジナルのままでは、一般ユーザーも指定できてしまいます。これでは、開発中の画面を一般ユーザーに晒してしまい、運悪くバグがあると、データを壊すなどの危険があります。

そこで、プラグインを一箇所書き換えました。

theme-test-drive/themedrive.php

@@ -192,6 +192,9 @@

   function themedrive_determine_theme()
   {
+      if (!current_user_can(themedrive_get_level())) {
+          return false;
+      }
       if (!isset($_GET['theme'])) {
           if (!current_user_can(themedrive_get_level())) {
               // not admin

(patchコマンドでパッチできるかはテストしていません)

これで、管理者としてログイン中の時だけ、testingのテーマを試すことができ、一般ユーザーは?theme=xxxと指定しても、テーマを変更することができなくなりました。

テーマの編集をバージョン管理

cd susumuis-1        
git init
git add .
git commit -m 'first commit'

これでとりあえず、バージョン管理できるようにしました。間違えてしまった時はresetで戻ることができます。ある程度できたらcommitしてテストをすることで、「あれ、さっきまではできてたのに」というのを防ぐことができます。必要に応じてリモートリポジトリを作成したりブランチを切ったりしていきたいと思います。

公開

テーマが完成して本番に公開するときはシンボリックリンクを貼り直すだけです。

WordPressに持ってきてしまった、はてなキーワードリンクを解除しました(XMLRPC + wordpress-javaを使用)

前回 はてなダイアリーから持ってきたはてなダイアリーの記事がはてなキーワードリンクを含んでしまっているので、解除するためにスクリプト組むと宣言していました。
今日そのためのスクリプトを書いてリンク解除に成功したので報告します。

変換方法

WordPressのXMLRPC機能を使ってAPI経由でデータ変換をしました。

イメージとしては一つ一つのページを手で開いて、エディタに記事を貼り付けて正規表現置換して保存を繰り返すのと同じです。

この方法のメリットは

  • インターネットに繋がっていればどの端末からも実行できる
  • 履歴データまでは置換しない(WordPressは記事を変更した履歴を保持しています)
  • 変更履歴が残る(その気になればいつでも変換前に戻せる)

デメリットは

  • 前記事分の通信が発生する
  • APIの仕様を調べなければならない
  • APIを有効にしなければならない

当初SQL一本流して変換しようとしましたがMySQLは正規表現で文字列置換をすることができないらしく、それならPHPでスクリプトを組もうとも思ったのですが、DB直接操作するより外部から触ったほうが安全だと考え、API方式を選択しました。

WordPressのAPIはXMLRPC方式と、AtomPub方式の二種類提供されていますが、後者はデフォルトで無効になっていたり、日本語の情報が皆無に近く、XMLRPC方式の場合は、もう少し状況がマシなようで、 wordpress-java というJavaクライアントライブラリもありましたので、今回はこちらを使用しました。

変換プログラム全文

public class App {
    static String user = "******";
    static String pass = "****************";
    static String url = "*************/xmlrpc.php";

    public static void main(String[] args) throws MalformedURLException, XmlRpcFault {
        WordPress wp = new WordPress(user, pass, url);
        List<Page> recentPosts = wp.getRecentPosts(1000);
        Pattern pattern = Pattern.compile("<a class=\"keyword\" href=\"[^\"]+\">([^<]+)</a>");
        for (Page page : recentPosts) {
            String contents = page.getDescription();
            if (contents.contains("keyword")) {
                Matcher m = pattern.matcher(page.getDescription());
                System.out.println(page.getPermaLink());
                while (m.find()) {
                    System.out.println("\t\t" + m.group(1) + "\t" + m.group());
                    contents = contents.replace(m.group(), m.group(1));
                }
                page.setDescription(contents);
                System.out.println("\tsaved status = " + page.getPost_status());
                wp.editPost(page.getPostid(), page, page.getPost_status());
            }
        }
    }
}

実行結果はこんなかんじです。

http://www.susumuis.info/entry/2014/03/31/103829
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        API <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>
        hoge    <a class="keyword" href="http://d.hatena.ne.jp/keyword/hoge">hoge</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        Java5   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Java5">Java5</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        GC  <a class="keyword" href="http://d.hatena.ne.jp/keyword/GC">GC</a>
    saved status = publish
http://www.susumuis.info/entry/2014/03/30/004202
        Mayaa   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Mayaa">Mayaa</a>
続く...

キーワードの変遷を見ると当時どんなことに注目していたのか見られて面白いです。

実行後、変換された記事のリビジョンが1増加しています。差分を表示すると、たしかにキーワードの箇所のみ置換されています。

キーワードだけ置換されている

プログラム解説

解説するまでもない短いプログラムですが、正規表現によって検索をし、置換のところは正規表現を使わずに完全一致で置換しています。こうすることで、どこが変わったのかログに出力することができます。

contents = contents.replace(m.group(), m.group(1));

の部分は記事本文をまるごと置換しているのでメモリ効率の点で言えば有利ではありません。が、以前業務で作った変換ツールはこの方式でトータル数十万行のファイル群を平気で置換できてしまっていたのでこの程度気にするほどではないでしょう。ただし、件数によっては時間がかかるかもしれません。

wp.editPost(page.getPostid(), page, page.getPost_status());

の部分は第三パラメータがJavaDocで

String pubulish : publish status

となっていて、それ以上の説明がありません。WordPressのAPIの仕様書を見てもよくわからなかったので、当てずっぽうでprivateまたはpublicを返すpost.getPost_status()にしたら、うまくいきました。

newPostの場合はbooleanなので、この辺り統一性がないなと思いました。

キーワードリンクの是非について

自分がはてなブログ内にいたら、キーワード被リンクでSEO効果もあったかもしれませんが、はてな外にいますからその恩恵はありません。じゃあ、はてなブログを使用しているならどうかといえば、最近の検索エンジンは外部リンクが多すぎることに対して厳しくなってきていますから、その効果も今はどれほどなのかとも思います。

ユーザーとしてページを見ると、リンクが多すぎるとうるさい感じがしますし、キーワードリンクをクリックして便利だと思ったことはあまりありません。分からない語句があったらはてな内を探すより「ぐぐる」のが自然だと思います。

今後はてなキーワードが終了したり、使用が変わったりすると、外部のブログからのキーワードリンクは全部リンク切れになってしまいますから、引っ越してきたサイトでキーワードリンクが残ってしまっている状況は百害あって一利なしと思います。

実行にJavaが必要ではありますが、同じ状況の方がいらっしゃったら使っていただいて構いません。ただし、事前にバックアップを取るなど自己責任にてお願いします。MySQLのバックアップは

mysqldump -u ユーザー名 -p DB名 > ファイル名.dump

にて取れます (^^;

はてなブログからWordPressに移行するためにしたこと

ほっとくと忘れてしまいそうなので、忘れないうちに、記録残そうと思います。

はてなブログから、WordPressへ結構苦労して移行しました。その時やったことをざっと書きたいと思います。

記事の移行

はてなブログ用ツールで記事データの取得

定番ですが、下記のツールを利用しました。
はてなブログ用ツール

.NETで作られているのでWindowsかMonoの環境が必要です。MacもMono環境を整えればできるかもしれないと書いてましたが、Windowsマシン持ってきてやってしまいました。

はてなのAPIを使用しているらしいので、それなら、自分もAPI使ってもいいんですが、せっかくあるツールが使えるなら、やっぱ楽さには叶いません。

このツールでデータをエクスポートしたものは、完全ではありません。次の問題点があります。

  • コメントが含まれない
  • 画像などがはてなフォトライフのままである
  • シンタックスハイライトなどの装飾が一部崩れている

はてなダイアリーのエクスポート機能を使用

はてなブログにはエクスポート機能がありませんが、はてなダイアリーにはあります。はてなブログに移行する前ははてなダイアリーを使っていて、はてなダイアリー時代が大半だったので、はてなダイアリーにログインすることではてなダイアリー時代のデータを抜くことは出来ました。

方法は下記の記事と同じです。

はてなダイアリーからWordPressへ全記事を移行した件 | ツインズパパの徒然日記 Ver.2

上記記事にも書いてあるとおり、次の問題点があります。

  • はてなキーワードのリンクもそのまま持ってきてしまう
  • 一部の記事の日付が1970年になってしまう

    • →これは、時刻 00時代のAMがうまく取り込めないという問題。リンク先や、その方が参考にされたサイトでは01時に変更することで対処されていましたが、僕の場合はAM自体を取り除いたらうまくいきました。
  • シンタックスハイライトがガタガタ
  • 画像がはてなフォトラ(ry
  • なぜか一部の記事がうまく移行できないで画面が真っ白になった(すごい大昔の記事)

RSSからインポート

はてなブログはRSSも配信しています。そしてWordPressは公式でRSSからインポートすることができます。しかし、これは、.NETのツールの下位互換でした。

  • 新着7件しか取ってこれない
  • コメントデータを取ってこれない

結局どうしたか

  • はてなダイアリー時代の記事のうち、真っ白にならなかったものは、はてなダイアリーで移行
  • はてなブログ時代の記事ははてなブログで移行
  • はてなブログ時代のコメントは、3件しかなかったので手でSQLを発行して移行:
insert into
    wp_comments (
        comment_post_ID,
        comment_author,
        comment_author_url,
        comment_date,
        comment_date_gmt,
        comment_content,
        comment_karma,
        comment_approved,
        comment_parent,
        user_id

    ) values (
        614,
        'susumuis',
        'http://www.susumuis.info/',
        '2014-03-30',
        '2014-03-30',
        'id:faith_and_braveさん コメントありがとうございます。 共感いただいて本当に嬉しいです。 自分は開発(省略)',
        0, 1, 0, 1
    )

こんな感じです。コマンドラインツールでのSQLの発行はキツイし、phpMyAdminのような「クラックしてください」と言ってるようなツールは使いたくなかったので、このツールを使いました。SSHトンネルの設定もやってくれるので便利でした。

Sequel Pro

はてなキーワード持ってきてしまった件についてはSQLに正規表現書いて変換しようとしましたが、MySQLは正規表現を使った置換ができないそうなので、PHPなどの言語で外から変換するしかないようです。まだ出来ていませんので後日できたら報告します。

検索結果のindexとか「いいね」カウントとか

検索エンジンのインデックスについては、301リダイレクトで対処するのが王道ですが、これは、

はてなダイアリー → はてなブログ → 独自ドメイン(はてなブログPro)

を公式移行ツールで移行してきたのでシームレスにやってくれていました。なので、あとは、はてなブログProで使っていた独自ドメインのURLそのままで記事のURLを移行するだけです。

WordPressにはパーマリンク機能というのがあって、記事のURLを

http://www.example.jp/?p=123
のようにクエリーパラメータにするのではなく、
http://www.example.jp/2014/06/28/sample-post/
のような任意のURLにすることができます。

ここで、移行前のURLは次の形式になっていました。

はてなブログ時代

http://ドメイン/entry/yyyy/mm/dd/数字6桁
例:
http://www.susumuis.info/entry/2014/06/11/222935

はてなダイアリー時代

http://ドメイン/entry/yyyymmdd/p数字
http://ドメイン/entry/yyyymmdd/数字10桁
http://ドメイン/entry/yyyymmdd/任意の単語
例:
http://www.susumuis.info/entry/20110728/p1
http://www.susumuis.info/entry/20090822/1252377609

見事に分かれています。それなら、共通する、「/entry/2014」の部分 + 投稿名として「06/11/XXXXXX」を書くようにしようとしましたがこれはできません。

"/"という文字が投稿名に使用できなかったためです。

結局どうしたかというと、 Enhanced Custom Permalinksというプラグインのお世話になりました。これを使えば記事ごとにパーマリンクを設定できます。

さて、記事の数は90くらいありますので、全部手でやってたら大変ですから、こいつは、SQLで一括で設定してしまいました。

当時のクエリを残していなかったので、正確なものを紹介できませんが、wp_postmetaテーブルをinsertしていく形になります。

単なる301ではなく、URLを同一にしたため、はてなスター、Facebookいいねまでそのまま引き継ぐことが出来ました!

はてなブックマーク、はてブコメント

ところが、はてなブックマークについてはうまく行きませんでした。はてなブックマークもURLに紐づく情報なので、てっきりブログをそのまま移行すれば、独自ドメインでブックマークが引き継がれると思っていました。確かに移行直後はそうだったのですが、移行後数時間後、はてなブックマークカウントが0になっていました。

なんでかなあと思ってみたところ、はてなブログProの管理画面を見たら理由がわかりました。

独自ドメインは解除されている!

はてなブログは、定期的にドメインが有効かどうかクロールしているようです。ドメインがきちんとはてなのDNSに向いていることがわかった時点で、元のはてなブログのURLから独自ドメインのURLにリダイレクトしているようです。

ドメインが無効、または別のサーバーを向いていると判断されると、はてなブログのドメインから独自ドメインへの転送がストップします。それと同時に、はてなブックマークのデータも、元のはてなブログのドメインのものに変換され、独自ドメインのカウントは、0になります。はてなブログとして独自ドメインで受けたはてなブックマークカウントも、一緒にはてなブログドメインに持って行かれてしまいます。

はてなブックマークははてなのものだから仕方がないですね。

人気エントリー一覧

人気エントリーの一覧は、元のはてなブログドメインのものをはてなブックマークブログパーツを使用すれば作成できます。このままだとリンク先がはてなブログになってしまうので、リンク先をJavaScriptで強引に書き換えてしまいました。右サイドバーの「以前反響を頂いた記事」という部分です。具体的にはこんな感じです。

jQuery('div#hatena_hotentry').on('DOMNodeInserted', function() {
  $links = jQuery('div#hatena_hotentry .hatena-bookmark-entrytitle');
  $links.each(function() {
    jQuery(this).attr('href', jQuery(this).attr('href').replace('susumuis.hateblo.jp', 'www.susumuis.info'))
  });
});

こんなことして怒られたらやめます。wwもう、この段階で欲しいのは過去のはてなブックマークじゃなくて、この記事がオススメだよっていうことなので、僕の方で勝手にレコメンドしちゃえばいいんです。

ブックマークコメント

はてなブックマークコメントはどうしましょう。最近はブログのコメント機能を使ってのコメントより、はてなブックマークコメントで暑いコメントをもらったりします。

はてブコメントがないと、過去盛り上がった記事もなんか寂しい感じですね。ただ、個人的には、はてなブックマークに対しては歓迎的でない部分もあって、何かと炎上しやすいなど、攻撃的なこと書く人がたまにいます。

コメントなのか、個人のメモなのかわからないしろものですから、今後、ブログの雰囲気的を良くしていくためにはない方が良いかもしれません。

ということで、過去の記事まで追跡して、はてブコメントを画面に出すのはやめました。ただ、僕のブログのなかで、はてブ2桁頂いた記事が6件だけあったのでそれらについては記事の下に手動で旧ドメインでのブックマークリンクを記載しました。こんなかんじです。

この記事は以前多くのブックマーク・コメントを頂きました。
<a href="http://b.hatena.ne.jp/entry/http://susumuis.hateblo.jp/entry/20110728/p1" class="hatena-bookmark-button" data-hatena-bookmark-layout="simple-balloon" title="以前のURLでのブックマーク件数"></a>
これからブックマークされる方はこちら↓

Markdown記法を使えるようにする

WordPressにはMarkdownを使えるようにするプラグインが複数存在します。いろいろ試しましたが僕はこのプラグインが使いやすいと思いました。

WP Markdown Live

コードのインデントとシンタックスハイライト

はてなダイアリーやはてなブログから記事をインポートした結果、残念なこととして、ソースコードのインデントやシンタックスハイライトがガタガタになってしまったことがありました。

これについては、幸いWP Markdown LiveプラグインがGitHub風のプログラムコード記法を持っているので、テキスト化されたコードからもう一度自前でインデントしてMarkdownに貼り直すことにしました。全部だと大変だから一部だけです。

これだとシンタックスハイライトがつきませんがWP Markdown Liveで出力したコードは

<pre class=" language-css">
<code class=" language-css" data-language="css">

コード

</code>
</pre>

(*うまくコードをテキスト化してくれないので、最初のpだけ全角文字にしています)
のように出力されます。これはそのまま prism.jsが使用できます。

テーマのスタイルシートにダウンロードしてきたprism.cssとjsを配置するだけで、綺麗にシンタックスハイライトすることができました。

SEOとかOGPとかTwitter連携とか

まだまだやらなきゃいけないことはたくさんありますが、だんだん面倒くさくなってきてしまったので、これらはプラグインでお茶をにごしました。
余力があったらプラグインを使わずに自力で連携したいです。

使ったプラグイン
* Jetpack by WordPress.com
* WP Open Graph
* WP Social Bookmarking Light

Jetpack使ってるあたりが負けてる感がするので、早急に自力でなんとかしたいです。

SEOのメタタグの部分はしっくり来るプラグインが見つからなかったので自分でheader.phpにスクリプト書きました。