カテゴリー別アーカイブ: パッケージ・サービス

WordPressでよく見かけるけどよくわからない関数 esc_urlについて

esc_urlについて

先日とある勉強会で
「WordPressの配布されてるテーマでは結構使われているようなんですけれど、esc_urlという関数って何のためにあるのですか?」
という質問を頂きました。
「エ、エスケープする関数なんで適当に使ったらいいですよ!」
なんていう答えにならない答えを返してしまったので反省して調べておきます。

WordPressに標準で付いてくるデフォルトテーマにはesc_urlがふんだんに使われています。
たとえばtwenty fifteenではheader.phpに使われていて

<?php echo esc_url( get_template_directory_uri() );

といったように、テンプレートディレクトリを出力する際に間に挟んで利用しています。

どうしてこのように出力をするためにつかっているのか、なぜそのまま出力するといけないのかについて調べてみました。

マニュアルを調べてみた

まずは公式の説明について調べてみました。
幸い日本語訳がありましたのでそちらを紹介します。

テキストや属性などのURLを無害化する時に用いる関数です。ホワイトリストに登録されているプロトコル (デフォルトでは、http, https, ftp, ftps, mailto, news, irc, gopher, nntp, feed, telnet) 以外のURLを拒絶し、無効なキャラクタを除外し、危険なキャラクタを削除します。 この関数はキャラクタをHTMLエンティティとしてエンコードし、 (X)HTML または XML ドキュメントを作成する時に用います。アンド記号 (&) とシングルクォート (') はそれぞれ (&#038, &#039) という形に変換します。 この関数は非推奨となった clean_url() の代わりに用いるものです。

なるほど、ホワイトリストに登録されているプロトコル以外を拒否し、また使ってはダメな文字をURLから省いたり変換したりするもの、なのですね。

実際に動かしてみる

それでは早速どのような動きになるか試してみました。

$urls = array(
  'http://www.example.com/hogehoge',
  'https://www.example.com/hogehoge',
  'tel://example.com',
  'tel://0120000000',
  'http://.$,;:&=?!*[email protected]#_()',
  'http://\"\'|*`^><)(}{][',
);
foreach($urls as $url) {
  echo $url."<br />\n";
  echo esc_url($url)."<br />";
}

出力はこうなります

元テキスト esc_url変換後
http://www.example.com/hogehoge http://www.example.com/hogehoge
www.example.com/hogehoge http://www.example.com/hogehoge
https://www.example.com/hogehoge https://www.example.com/hogehoge
tel://example.com tel://example.com
tel://0120000000 tel://0120000000
hoge://0120000000
http://.$,;:&=?!*[email protected]#_()’| http://.$,;:&=?!*[email protected]#_()’|
http://\”`^><{}[] http://
http://ほげふがぴよ http://ほげふがぴよ

なるほど、色々と変化していますね。
httpやhttpsはそのまま出力されていますね。
またtel:も出力されているようです。マニュアルにtelというのは無いのですが、どうやら出力されるようです。
ログなどを追いかけてみると(#18268, #21081, src)WordPress3.4あたりで「telやfaxといったプロトコルも追加してくれ」という要望があがったそうで、それに対応する形で追加れたようです。
他には記号系が削除されるようですね。これはURLで利用してもよい文字とそうでは無い文字がありまして、そのルールに沿って削除されるようです。

esc_urlの目的

さて、このesc_urlの目的なのですがなぜ必要なのかについて考えてみました。
まずはURLとして無効な文字を防ぐことですね。
URLに利用して良い文字というのはIETFがRFC1738として定義しています。一般の利用者が「この文字は使える、これはダメ」と調べるのは大変ですから、esc_urlを利用する事でこの違反を防ぐことができます。
またプロトコルのホワイトリストについては、インジェクション攻撃などに対応する目的があるでしょう。例えばコメントの「ウェブサイト」の部分にtelで110番を書かれてしまうと、携帯電話ユーザがクリックした際、いきなり110番が掛かってしまったりします。今ではウェブサイトやメールから通話する際には警告がでますが、そういった対処の無かったガラケー全盛期に流行った手法ですね。
telプロトコルの許可については許可されていると書きましたが、調べたところコメントでは不許可のようです。
プロトコルのホワイトリストについてはテーマ作成者がちゃんと管理できるようになっているので、例えばお店紹介サイトであれば「どこでもtelプロトコルを許可したい」、といった要望にも応えることができます。
基本的にesc_urlの目的としては、「意図しないURLの誤った誘導を防ぐ」ことだと思っています。ですのでテーマを作成する際にURLを出力する箇所でesc_urlを埋め込むことで不正なURLが吐き出されることを防いでくれます。

まとめ

esc_urlを使うと不正なURLを防いでくれます。またコメント(本文ではないところ)などURLが入ってくる箇所でesc_urlを挟む事でインジェクション攻撃を防ぐことにもなります。
ですのでURLが入る箇所では積極的にesc_urlを使っていきたいですね。

NginxでWordPressを動かす@Ubuntu 14.04 LTS

少し流行は過ぎてしまったのですが、UbuntuのnginxでWordPressを動かしたのでメモ。

1. 構成
まずは構成について。
ウェブサーバの構成について、単にnginxを使うとしてもいろいろな構成があるんだけれど、今回はリバースプロキシを使った構成にしました。

リバースプロキシはユーザからのリクエストを受ける際、いったんプロキシサーバがリクエストを受け取って、それを本当のウェブサーバに問い合わせを行う、というそんな機能を提供してくれます。
プロキシ(代理)サーバなので、リクエストを途中から代理してくれる、そんなサーバです。
リバースと付いているのは、リバースじゃ無い普通のプロキシサーバもありまして、こちらはクライアント側で利用します。リバースプロキシはリバース、逆向きなので、サーバ側において代理させるんですね。
ちなみにリバースプロキシサーバ、プロキシサーバともにキャッシュ機能が付いている物がほとんどです。

で、今回の構成では、インターネット側からのリクエストをnginxのリバースプロキシで受けて、それを内部のnginxへリクエスト、そのnginxはWordPressに必要なPHPをPHP-FPMというものを使って動作する、ということに。
このリバースプロキシを使うことでキャッシングが容易にできるのでWordPressの高速化が進む、というものですね。

ちなみにPHP-FPMはプロセスで待ち受けさせていて、リクエストがあるとすぐにPHPを実行して結果を返すという、そういう仕組みをもったPHPのサーバ、デーモンです。

一台のサーバで”リバースプロキシ”、”ウェブサーバ”、”PHPサーバ(アプリケーションサーバ)”を動かす必要があるので、そのやりとりにはいくつかのルールが必要です。
サーバへリクエストを投げるときには必ず、どのサービスへ繋ぐか、という情報が必要なのですが、これを通常ポート番号によって振り分けるのです。
ちなみに一般的なウェブサービスだと80番を利用します。またプロキシサーバでは8080版を利用します。

今回はリバースプロキシですのでこいつが80番で待ち受けて、このリバースプロキシからウェブサーバへはウェブサーバの8080番ポートへ接続、PHPが動いているデーモンへはポート番号じゃなくてsockという仕組みで繋ぐことにしました。

2. 手順
まずはインストール。ubuntuなのでaptを利用します。今回必要になるのはnginx, php-fpmです。

shell> sudo apt-install nginx php-fpm
shell> cd /etc/nginx

インストールが終わったらnginxのデフォルトコンフィグが格納されている/etc/nginxで作業します。

まずは早速、WordPressに必要なnginxのconfigを二つ書きます。
これはWordPressの公式サイトで紹介されています。二つのconfを/etc/nginx/siteconf.d/というディレクトリを作って配置しています。

more /etc/nginx/siteconf.d/global_restrictions.conf       
# this file is from http://codex.wordpress.org/Nginx                            # Global restrictions configuration file.                                       
# Designed to be included in any server {} block.</p>                           
location = /favicon.ico {                                                       
         log_not_found off;                                                     
         access_log off;
}

location = /robots.txt {
         allow all;
         log_not_found off;
         access_log off;
}

# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Sto
re (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities suc
h as fail2ban)
location ~ /\. {
         deny all;
}






shell> cat /etc/nginx/siteconf.d/wordpress_base
# this file is from http://codex.wordpress.org/Nginx

# WordPress single blog rules.
# Designed to be included in any server {} block.

# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
         try_files $uri $uri/ /index.php?$args;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
       access_log off; log_not_found off; expires max;
}

# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-wp-super-cache.conf;
#include global/wordpress-w3-total-cache.conf;

# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
         # Zero-day exploit defense.
         # http://forum.nginx.org/read.php?2,88845,page=3
         # Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi.
         # Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine.  And then cross your fingers that you won't get hacked.
         try_files $uri =404;

         fastcgi_split_path_info ^(.+\.php)(/.+)$;
         #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

         include fastcgi_params;
         fastcgi_index index.php;
         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
         fastcgi_param REMOTE_ADDR  $http_x_real_ip;
#        fastcgi_intercept_errors on;
         fastcgi_pass unix:/var/run/php5-fpm.sock;
}

最初のglobal_restrictions.confでは不正なアクセスを防ぐための設定です。nginxでは.htaccessを初めとしたファイルを利用しませんので、それらのファイルが標準では直接表示されたりします。中にはパスワードを書いてあったりすることもあるので安全のためこれらのファイルを非表示にするconfです。

続いてwordpress_base.confです。これがnginxとphp-fpmでwordpressを動かすための基本的なconfigになります。ほとんどは公式通りなのですが、注意点がいくつかあります。
conf本体の下部にある、 “fastcgi_param REMOTE_ADDR $http_x_real_ip;”については追記してあります。
これは今回リバースプロキシを使うに当たって、ウェブサーバで見えるクライアントIPアドレスが127.0.0.1のような自分自身のアドレスになってしまうことを防ぐ処置です。
自分自身をリバースプロキシにする場合に限らず何らかのリバースプロキシを挟むとREMOTE_ADDRが書き換わってしまいます。そうするとアクセス解析やコメントをつけた人のIPアドレスがすべて同じものになってしまい意味のないものになってしまいます。
それを防ぐための構文になります。ちなみにapacheであればmod_rpafというモジュールで対処したりします。
もう一点気をつける構文は”fastcgi_pass unix:/var/run/php5-fpm.sock;”です。今回はPHPとnginxを動かすサーバが同じサーバでしたのでsockを利用しました。他のホストで動かす場合やTCP/IPで動かす場合には変更が必要です。

これでwordppressの基本的なconfは揃いました。続いてはサイトのconfを書きます。

最近はnginxもapache同様、ドメインごとにconfigを書いてシンボリックリンクで有効化/無効化する流れになっています。今回は /etc/nginx/sites-available/example.com で作成しました。
このconfにはリバースプロキシ用の設定も含まれるので少し大変です。

shell> cat /etc/nginx/sites-available/exampel.com.conf
server {
        listen 80;
        server_name www.example.com;
        root        /home/yousan/public_html/www.example.com;
        index index.html index.htm index.php;
        include siteconf.d/global_restrictions.conf;

# ここから下がリバースプロキシ用の設定 最後にドメインの名前を設定すること
# またnginx.confのbackednともセットで設定すること
              location /wp-admin { proxy_pass http://backend; }
              location ~ .*\.php { proxy_pass http://backend; }
        location / {
              set $mobile "";
              if ($http_user_agent ~* '(DoCoMo|J-PHONE|Vodafone|MOT-|UP\.Browser|DDI
POCKET|ASTEL|PDXGW|Palmscape|Xiino|sharp pda browser|Windows CE|L-mode|WILLCOM|SoftB
ank|Semulator|Vemulator|J-EMULATOR|emobile|mixi-mobile-converter)') {
              set $mobile "@ktai";
              }
              if ($http_user_agent ~* '(iPhone|iPod|Opera Mini|Android.*Mobile|NetFr
ont|PSP|BlackBerry)') {
              set $mobile "@mobile";
              }
              if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-post
pass_" ) {
              set $do_not_cache 1;
              }
              proxy_no_cache     $do_not_cache;
              proxy_cache_bypass $do_not_cache;
              proxy_cache czone;
              proxy_cache_key "$scheme://$host$request_uri$is_args$args$mobile";
              proxy_cache_valid  200 301 302 60m;
              proxy_cache_valid  404 5m;
              proxy_cache_use_stale  error timeout invalid_header updating http_500
http_502 http_503 http_504;
              proxy_pass http://backend;
              proxy_redirect http://www.example.com:8080/ /;
       }
}

server {
    listen      8080;
    server_name www.example.com;
    index index.html index.htm index.php;
    include siteconf.d/global_restrictions.conf;
    include siteconf.d/wordpress_base.conf;
    root            /home/yousan/public_html/www.examplecom.com;
}

shell> sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled

このconfでは上のserverディレクティブで80番の待ち受け、リバースプロキシ側の待ち受けを設定します。この中で最後の方にあるproxy_redirectという構文で実際にPHP等が動いている8080で待ち受けをしているウェブサーバへ転送しています。
下の8080で待ち受けをしているところは先ほどwordpress用のconfを書きましたのでそれらをincludeして、あとは少し書いてやるだけでOKです。

ドメインごとのconfを書いたらlnでシンボリックリンクをsites-availableへ移してあげてください。こうすることで有効化されます。

最後にnginx本体のconfを書きます。
今回はリバースプロキシでレスポンスを早くしよう!というが目的ですのでリバースプロキシのキャッシュとgzip圧縮を有効化します。
ココでもリバースプロキシ用の設定を分けてあります。
機能別に分けられたconfはconf.dに入れると自動で読み込まれますのでそちらに入れておきます。自動に読まれますけれど*.confという名前じゃないとダメなので注意してください。

shell> cat /etc/nginx/conf.d/
proxy_cache_path  /var/cache/nginx levels=1:2 keys_zone=czone:4m max_size=50m inacti
ve=120m;
proxy_temp_path   /var/tmp/nginx;
proxy_cache_key   "$scheme://$host$request_uri";
proxy_set_header  Host               $host;
proxy_set_header  X-Real-IP          $remote_addr;
proxy_set_header  X-Forwarded-Host   $host;
proxy_set_header  X-Forwarded-Server $host;
proxy_set_header  X-Forwarded-For    $proxy_add_x_forwarded_for;
proxy_set_header  Remote-Addr        $remote_addr;
#real_ip_header X-Forwarded-For;
#real_ip_recursive on;

upstream backend {
             ip_hash;
             server 127.0.0.1:8080;
}

こちらが本体側のconfです。ほとんど書き換えてないのでコメントを外すだけですね。

shell> cat /etc/nginx/nginx.conf
... 省略 ...
       ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_types text/plain text/css application/json application/x-javascript tex
t/xml application/xml application/xml+rss text/javascript;

これでconfが整いました。
最後にnginxを再起動して動作確認です。

shell> sudo service nginx restart

さて、動いたでしょうか。

今回自分もすぐには動かず、いくつかはまった点がありましたので紹介しておきます。
1. 文末のセミコロン忘れ
2. www.example.comというドメインのまま、ドメイン名を書き換え忘れていた
nginxではほとんどの文末にセミコロンをつける習慣がありますので、これを忘れるとエラーになります。
ドメイン名の書き換えでは色んなサイトを参考にしながらconfを書いたのですが、confの途中にドメイン名がでていて、今回実際に運用するドメインに書き換えるのを忘れていて動きませんでした。

これらのエラーについてはerror.logを見る事でどのファイルのどこでエラーが起きている、ということが分かったりします。

shell> sudo tail /var/log/nginx/error.log

ただ中には直接的にエラーが表示されず、推測に推測を重ねてエラーを取り除く必要もありますがそちらについては…、経験が物をいいますね!

以上となります。

l2tpのTeamSpeak3サーバ IPアドレス変更のお知らせ

こんにちは、l2tp管理人の岡田洋一です。

l2tp TeamSpeakサーバでは皆さんに仲良く元気にボイスチャットを行ってもらえるよう、サーバを提供しています。

実はこの数日、9月2日ごろから、午後9時ごろからTeamSpeakサーバが落ちてしまう事案が発生していました。

原因が不明だったのですがISPと相談したところ「帯域制限を掛けたいほどの異常なトラフィック」を検知したとの報告を受けました。

トラフィックの内容は「不特定多数から、ソースポート53番でのトラフィック」とのことで、どうやらDNS ampと呼ばれる類いのDDoS受けているのでは無いか、とのことでした。

9月1日お昼頃からずーっと断続的に攻撃を受けているようで、「できれば固定IPアドレスを変更してくれ」とのことでした。

こちらとしても皆さんにサーバを使ってもらうためにこの攻撃を回避しなくてはならず、やむを得ずIPアドレスの変更を受け付けました。

従いまして、TeamSpeakサーバのアドレスが変更になります。

TeamSpeakサーバのホスト名(ts.l2tp.org)はそのまま使うことができますが、IPアドレスで接続している方は変更をよろしくお願いします。

 

急なお話なのですが、今夜9月6日(土)の早朝に切り替え作業を行う予定になっています。

ご利用中の皆様にはご迷惑をおかけしますが、何卒ご理解ご協力の程宜しくお願いいたします。

svnで結果をたくさん入れたかった

Ubuntu Serverのアップグレードを行ったんだけど、apacheのconfファイルが色々と変わってて、それに伴って/etc配下をsvnに入れてた分の更新作業を行った。

Ubuntu Serverは12.04から14.04にアップグレードしたんだけれど、一つ前の13.10からapacheのconfのルールなどがちょくちょくかわったみたいで更新作業が結構発生した。

また標準で(?)くっついてくるモジュールが減ったらしく、前のバージョンでは存在したmodsのconfとloadファイルが無くなっていた。

自分は利用するconfファイルをすべてsvn管理下においているんだけど、アップグレードでaptによって削除されてしまったファイルがいくつか。

shell> cd /etc/apache2/
shell> svn st
...(前略)...
? mods-available/authn_core.load
! mods-available/authn_default.load
? mods-available/authn_socache.load
? mods-available/authz_core.load
? mods-available/authz_dbd.load
! mods-available/authz_default.load
? mods-available/buffer.load
? mods-available/cache_disk.conf
? mods-available/cache_disk.load
? mods-available/cache_socache.load
! mods-available/cern_meta.load
? mods-available/data.load
? mods-available/dialup.load
...(後略)....

svn stしてみると出てくるファイルのうち、?は新規に追加されたファイル、!は削除されてしまったファイル。
これらのファイルをsvn del、svn addしてみる。
手動でひとつひとつ追加、削除するのは大変なのでsvn stからcutをつかってxargsにわたしてみる。
svn stの!とか?からファイル名までは空白8文字で、cutに行頭から9文字を削除するオプションがあるらしくてそれをつかう。

shell> svn st | grep ! | cut -c9- | xargs svn del
D authn_default.load
D authz_default.load
D cern_meta.load
D disk_cache.conf
D disk_cache.load
D ident.load
D imagemap.load
D mem_cache.conf
D mem_cache.load
D version.load
shell> svn st | grep \? | cut -c9- | xargs svn add
A cache_disk.load
A cache_socache.load
A data.load
A dialup.load
A echo.load
...(後略)...

</pre>

というわけでたくさんのファイルを一括して処理できました。

addの方ではハテナマークをエスケープするためにバックスラッシュを追加した。またファイル名の途中にハテナやビックリが入っていると引っかかりそうなので、行頭から(-e ‘^!’)を入れた方がいいかも。

WordPressの検索で検索結果が表示されなくて困った

WordPressの検索をいじっている最中に検索結果が出なくて困った。

pre_get_posts (にフックした関数)をいじっていたんだけれど、全然思ったような結果にならずに結構困っていた。

原因は色々あったんだけれど、二箇所で困ったのでメモしておく。

検索結果がでない、ということはSQLに問題がある。

なのでSQLの中身を確認してなぜ出ないかを検討することに。

以下のダンプをpre_get_postではなく、利用したいテンプレート側に記載。

$wp_query->requestは実行されたSQL文なんだけど、これはpre_get_postsの後に実行されたSQL文なので該当するテンプレートに記載しないと読み出せない。

<span style="line-height: 1.5;">var_dump($wp_query->request);</span>

まず一つはpost_typeが’post’にしかなっていない点。これはpre_get_postsの関数内で

$query->set('post_type', 'post');

を記載すること。これが実は結構やっかいで、他の条件文、例えばtax_queryなどと組み合わす際には無くても何とかなったりするので、条件文を組み立てている最中のデバッグと完成したカタチではpost_typeの指定が必要か不要か、結構迷うことになる。

post_typeに何も指定されていない場合に実際に中身を決定づけているのはwp-includes/query.phpのget_posts内でpost_type = ‘any’として処理していき、状況に応じていろんなのが入る。例えばattachmentとかpostなど。

このget_postsは結構大きなメソッドでこれまた結構読みづらいんだけど、詰まるたんびに読んでいればそのうち読めるようになるかも。

さて次いで検索結果がでない状況に当たったんだけど、これはSQL内に 0 = 1 なんていう文が。例えば下記のような感じに。

string(250) "SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND 0 = 1 AND wp_posts.post_type = 'custom_post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 5"

where句の二つ目にばっちり0 = 1の条件が。おかげさまで検索結果はゼロに。

wp-includes/query.phpのget_postsを丁寧に調べていくと下記の部分で付け加えられていた。

// Taxonomies
 if ( !$this->is_singular ) {
 $this->parse_tax_query( $q );

$clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );

$join .= $clauses['join'];
 $where .= $clauses['where'];
 }

ここのtax_query->get_sql() の部分。

今回このpre_get_postsではtax_queryを使用していなかったのでどうやらそれが原因らしい。

結果としては今回対象となるページが http://www.example.com/search/ なんていう検索用のURLだったんだけど、searchを普通のpostのcategoryとして見做していたようで、get_posts内の$this->tax_queryという$qを元に作られたタクソノミクエリでは

object(WP_Tax_Query)#9 (2) {
 ["queries"]=>
 array(1) {
 [0]=>
 array(5) {
 ["taxonomy"]=>
 string(8) "category"
 ["terms"]=>
 array(1) {
 [0]=>
 string(6) "search"
 }
 ["include_children"]=>
 bool(true)
 ["field"]=>
 string(4) "slug"
 ["operator"]=>
 string(2) "IN"
 }
 }
 ["relation"]=>
 string(3) "AND"
}

という状態に。

$qや$parsed_query、$wp_queryを調べてみると’category_name’に’search’が設定されていた。

これはパーマリンク設定で記事名を/%category%/%postname%/のようにしていたことで、最初のスラッシュからスラッシュの間をカテゴリ名として認識していたから、のようだ。

なのでpre_get_postsの中で

$query->set('category_name', '');

としてやることで不必要なtax_queryを省くことができた。

WordPressのペジネーションで、アーカイブページの「次の一覧」「前の一覧」を出す方法

WordPressのペジネーションで、アーカイブページの「次の一覧」「前の一覧」を出す方法

WordPressのペジネーションについて、次のページ、前のページだとか、1,2,3,4だとか、記事がたくさんある物に対してのページング、ペジネーションをやる必要があった。

ペジネーションって結構いろんな解決策があって、そして求められることがあって、それをどうやって実現しようかな、と。

今回のケースでは5件ずつ表示の投稿タイプで、次の5件、前の5件と、前後だけへのリンクが欲しかった。

こういう場合には posts_nav_link で解決!

http://codex.wordpress.org/Template_Tags/posts_nav_link

 

ちなみにこのcodexにもあるとおり、この関数の使い方でちょっとしたテクニックがあったりする。

この関数ってこれ一つで「前のページ」「次のページ」の両方を出してくれるんだけど、その際にコードを書く人が変更できるのは、この「前のページ」「後ろのページ」という、リンクの中に入っている文言だけだったり。

となると出力されるのが

<a href="foobar"><span class="iWantToAddClass">前のページ</span></a><a href="hogepiyo"><span class="uCoudInsert">次のページ</span></a>

というのはできても

<span class="nobodyCanMakeIt"><a href="foobar">前のページ</a></span><span class="beGod"><a href="hogepiyo">次のページ</a></span>

こういうのはできない。つまりaタグの内側はいじれるのでaの中にspanは入るけれど、aの外側にspanは付けれない。
CSSで前、次のそれぞれに違うクラスを付けて色つけする、っていうのはできない。

そこでcodexにあるようなやり方だとうまくいく。

<pre><div>
<div><?php posts_nav_link('','','&laquo; Previous Entries') ?></div>
<div><?php posts_nav_link('','Next Entries &raquo;','') ?></div>
</div></pre>

すごいね!

WordPressの記事でquery_postsをするとうまく通らないことがある

WordPressの記事でquery_posts、WP_Queryをするとうまく通らないことがある問題について。

PHPからWordPressの記事をいじってたんだけれどうまく取れないケースがあったのでメモ。

まずはうまく取れない条件について。

  1. カスタム投稿タイプの記事
  2. post_typeがfuture
  3. 上記に付して、投稿日時は2100-01-01みたいな未来
  4. post_name=hoge のように名前指定での取り出し

上記条件においてWP_Queryを利用するとうまく取れなかった。

ちなみに最後のpost_nameを指定しないとうまくいく。 うーんというところ。

$args = array(
  'name'=>'one_b38ptwd9', //コレを外すとうまくいく
  'post_type' => '1secwp',
  'post_status' => 'future',
);
$the_query = new WP_Query($args);
while ($the_query->have_posts()) {
  $the_query->the_post();
  // ここにこない
}

上記のコードだとうまくいかない。記事が存在するハズなのに記事が取得されない。WP_Queryインスタンスの$the_queryの持っている記事が0件となっていまう。
該当のコードを洗ってみる。wp-includes/query.phpの中盤あたりがそれ。

</pre>
if ( !empty($this->posts) && ($this->is_single || $this->is_page) ) {
  var_dump('hoge');
  exit;
  $status = get_post_status($this->posts[0]);
  $post_status_obj = get_post_status_object($status);
  var_dump($post_status_obj);
  //$type = get_post_type($this->posts[0]);
   if ( !$post_status_obj->public ) {
     if ( ! is_user_logged_in() ) {
     // User must be logged in to view unpublished posts.
     $this->posts = array();
   } else {
     if ( $post_status_obj->protected ) {
       // User must have edit permissions on the draft to preview.
       if ( ! current_user_can($edit_cap, $this->posts[0]->ID) ) {
         $this->posts = array();
       } else {

調べてみるとこのif文に入る条件となっている、

!empty($this->posts) && ($this->is_single || $this->is_page)

というのがおかしいことが原因。nameを指定市内と$this->is_singleがfalseなんだけど、nameを指定するとis_single=trueとなっちゃう。

そのせいで中盤にある

// User must be logged in to view unpublished posts.
$this->posts = array();

がひっかかってしまい、$this->postsが空っぽのarrayとなっちゃう。

というわけで解決方法は、この状態でログイン状態にしてやること、だ。

WordPressでログイン処理を強制的に行う方法
コードの最中にログインをする方法は上記に詳しい。

global $current_user;
$current_user = new WP_User(1);

これを加えてやっておしまい。

ちなみに記事が読めるか読めないか、みたいな詳細な権限は調べてないようなのでご注意。

公開TeamSpeakのチャンネル整理について

こんにちは。

L2TPで公開しているTeamSpeak3サーバにおいてチャンネルの整理を行います。
L2TPのTeamSpeakサーバではみなさんに比較的自由にご利用頂いています。チャンネルの作成についても永久的チャンネルOK、サブチャンネルOKと特に制限を設けておりません。

そのためチャンネルが乱立しやすく、後から参加される際にはチャンネルが多くて見づらい!という事になりがちです。

つきましてはチャンネル見づらいよ状態を解消するために使われていないチャンネルを消去します。

使われていないチャンネルのみを消去しますので「僕らのチャンネルがいきなり消されちゃった!」という事はありません。ご安心下さい。

使われていないチャンネルの判別のために「チャンネル名の編集」をお願いします。
チャンネル名の編集は、一番トップのチャンネルの末尾に数字の1を付けて下さい

例えば元々「ぼくらのチャンネル」というチャンネル名でしたら、「ぼくらのチャンネル 1」としてください。
(7)でも -1でも、1という数字が入ってる事がわかれば消しません。
1は今月が1月ということにちなみます。
チャンネル名の編集は消去の日時までに行えば結構です。

Capture

というわけでチャンネルの消去は以下の条件に当てはまった場合に行われます。

  • 消去時に人が居ないチャンネル

  • 告知から消去までの間にチャンネル情報が書き換わっていない(チャンネル名の末尾に1が入っていないチャンネル)チャンネル。

チャンネル消去予定日時: 2014年1月27日 (月)  12:00~13:00

なおチャンネルの削除を行っても作成に制限はありません。
気がついたら消えていた、という場合にはお手数ですが再度作り直して下さい。

利用者の皆様には不便を掛けますがどうぞ仲良く使って下さい。

Subversionのサーバをsvnからhttpに切り替えた時に作業ディレクトリで起こったエラー

0. サブバージョンを置き換えたらエラー発生

Subversionのサーバをsvnからhttpに切り替えたんだけど、作業ディレクトリ側でエラーが発生した。
当初Subversionはsvnservによって、svnプロトコルでやりとりを行っていた。svn coとかするときにsvn://で始まるURLを使っていた、ということですね。

<br />shell&gt; svn co svn://svn.example.com/hogerepo/<br />

LAN内で利用する分には不満がなかったんだけど、外部のインターネット越しに利用するとなると色々と不便があったのでHTTPで外部からも取得できるように変更した。
svnプロトコル自体を公開すれば良いじゃないか、という意見もあるかもしれないけれど、色々と考えた結果apache経由のhttpのみを公開することに。その辺の移行も大変だったけれど、それはまた別の機会に。

今回svn -> httpにする事で、今までチェックアウトしてきた作業ディレクトリで、svnの操作が行えなくなってしまった。もちろんプロトコルを変えたので当然と言えば当然。今回はこの動かなくなってしまった作業ディレクトリのリポジトリ参照先を変更することに。

1. 発生するエラー

まず現状のエラーを確認。

<br />shell&gt; svn update<br /> Updating '.':<br /> svn: E000061: Unable to connect to a repository at URL '<span style="text-decoration: underline;">svn://svn.example.com/hogerepo</span>'<br /> svn: E000061: Can't connect to host '<span style="text-decoration: underline;">svn.example.com</span>': Connection refused<br />

svn.example.comへ接続できない、と怒られていますね。もちろんこれはsvnservの廃止、httpの移行を行ったのでsvn.example.comへsvnプロトコルではアクセスできない、ということですね。

2. svnの参照リポジトリの変更

svnコマンドには参照リポジトリの変更というものがある。今回はそれを利用する。まず前段階として現在どういったリポジトリを参照しているか、を確認する。

<br />shell&gt; svn info | grep URL<br />URL: <span style="text-decoration: underline;">svn://svn.example.com/hogerepo</span><br />

svn infoで出力される情報の内、URLの項目を調べておく。今回の件ではsvn://svn.example.com/hogerepo が該当する。
今回の svn -> http の移行はプロトコルの変更のみで、svnサーバの名前は従来と同様に svn.example.com で行くこととなった。
(実際にはsvn_test.example.comのように移行時には別のサーバを立て、移行作業が完了した後にsvn.example.comの指すホストを書き換えている)
今回の移行はサーバのリポジトリの配下にあるリポジトリ構造、ディレクトリ構造もそのまま引き継いだので、

svn://svn.example.com/hogerepo
というリポジトリが
http://svn.example.com/hogerepo
になるだけとなった。

ちなみにリポジトリの配下のディレクトリ(ここではhogerepoリポジトリにあるディレクトリ)以下を変更する場合も同様。
さてこのURLを使って、以下のようにsvn switchを発行してやる。

<br /></span><span style="line-height: 1.714285714; font-size: 1rem;">shell&gt; svn switch --relocate </span><span style="text-decoration: underline;">svn://svn.</span><span style="text-decoration: underline;">example.com/hogerepo </span><span style="text-decoration: underline;">http://svn.example.com/hogerepo<br />

これで完了。簡単ですね。

ちなみに今回はサーバの移行をディレクトリを丸ごとコピーしたんだけれど、場合によってはリポジトリをダンプして読ませて、といった他のケースも有り得る。そうなってくるとこの作業ディレクトリの指すリポジトリ変更にはちょっと問題があったりする。具体的にはUUIDと呼ばれるリポジトリを一意に識別するものがあるのですが、これが一致しないとsvn switchの段階で怒られてしまいます。

この件に関しては下記に解決方法の一例があるので参照を。

svn switch –relocate: wrong uuid?

複数人開発の時にデータベースの同期をうまく取る方法

最近「意外に若かったんだね」と言われるようになりました、その言葉の指すところについては考えないようにしています岡田洋一ですコンニチハ。

さて、僕はしばらくチームで開発を行っています。大人数というわけでも無く、2〜3人での小規模プロジェクトチームです。少人数制のチームではありがちで、僕以外のメンバーもプログラム〜インフラ周りも修正することが多く、データベースのテーブル構造を修正することが各自にありえます。

さてそんなときにデータベースの同期を取る方法について考えてみました。

現状はテーブルのCREATE分を出力させ、プロジェクトのwikiに書き込んでいます。

wikiは変更情報を保存しますので、ある程度のバージョン管理ができました。が、いかんせん「出力、編集、保存、確認、伝達、適用」と、修正内容をチーム内で共有するためのステップが多いのです。少人数チームでステップが多いのは良くないですね。素早さこそがウリですから。

次に「テーブルの差分になるALTERのみを書く」ということについて勘案しました。が、こちらも開発を兼ねているため、テーブルの変更となるALTER分はそこそこの数が出てしまい、すべてのALTERを記録して適用することは面倒となりました。素早さがなくなってしまいます。

 

というわけで、現在のテーブル情報を書き出しつつバージョン管理システムで共有、という手段にしました。

現在プロジェクトはSVNで管理されていたので、テーブル情報のみをファイルに書き出してコミット、それを手元へ適用という手段をとりました。ちなみにデータベースはMySQLです。

テーブル情報のみを書き出す為に必要なコードは

shell> mysqldump -h mysqlhost -u user -ppassword --no-data target_schema | sed 's/ AUTO_INCREMENT=[0-9]*//' > tables.sql

shell> svn commit tables.sql -m 'update table sql.'

となりました。mysqlのホスト名、ユーザ名、パスワード、対象スキーマは読み替えて下さい。パスワードはナシにするとプロンプトで聞いてきます。

パイプでsedに渡してAUTO_INCREMENTを消しています。

最後にこの出力をファイルに書き出して、コミットして共有します。

 

これだと差分をうまく見ることができるので便利ですね!