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を省くことができた。

MacのAndroid Developer Toolsで実機デバッグをやってみた

MacのAndroid Developer Tools(adt)のEclipseで実機デバッグをやってみた。

やり方をググってみたんだけれど、なかなかうまく認識できなかったのでやり方をメモ。

使用している実機はKaihouというメーカーのkh-mid-700という中華パッド。通常だったらベンダから提供されるUSBドライバを使うんだけれど、マイナー機種だったのでググってみると汎用USBドライバを使えとのこと。

流れとしては

  1. 実機を接続
  2. 実機のベンダIDを調べて
  3. manifest.iniにベンダIDを記載
  4. android adb update を実行して ~/.android/adb_usb.ini にベンダIDを追記
  5. (上記の手順では ~/.android/adb_usb.ini にベンダIDが追記されないことがあるが、その際には手動で追記)
  6. adbサーバを再起動
  7. adb devicesで確認

という流れになる。

結構色々と大変なところがあるんだけれど、情報も増えてきているので状況は良くなっているかも。

2の実機のベンダIDの調べ方は何種類かある。一つはターミナルからsystem_profilerを使う方法。もう一つはGUIからUSBの情報を調べる方法。

system_profilerは元々macに入っているツールで、USBの一覧などを取得できるらしい。


shell> system_profiler SPUSBDataType
(中略)
KH-MID700:

Product ID: 0x0010
 Vendor ID: 0x2207 (Fuzhou Rockchip Electronics Co., Ltd.)
 Version: 2.22
 Serial Number: YRVVHROS0U
 Speed: Up to 480 Mb/sec
 Manufacturer: rockchip

(後略)

GUIから調べる場合にはFinderのアップルマークから

About This Mac > More Info > System Report

で出てくるウィンドウの
Hardware > USB

に同じ情報が載っているのでそこから Vender IDを取得。

このVendor IDは実機を認識するために何度も使って必ず必要になるのでメモメモ。

ついで実機用のmanifestを作成。このmanifestはadtのsdkディレクトリにあるadd-onに実機名でディレクトリをつくって manifest.ini を作成する。


shell> cd /Applications/adt/sdk/add-ons
shell> mkdir kh-mid700; cd kh-mid700
shell> vi manifest.ini

--- from here ---
name=kh-mid700
vendor=kaihou
description=kaihou mid700
api=10
<span style="line-height: 1.5;">revision=1
</span>usb-vendor=0x2207

--- for here ---

manifest.iniを記載したらこの情報をAndroid Developer Bridge(adb) に教えてあげる。これは android update adb を実行する事でできるんだけれど、うまくいかないことも多々ある。


shell> /Applications/adt/sdk/tools/android update adb

adb has been updated. You must restart adb with the following commands
 adb kill-server
 adb start-server

これを実行するとadbのサーバを再起動するように促される。

このandroid update adbを実行するとホームディレクトリにある‘ ~/.android/adb_usb.ini に追記されるはずなんだけれど、そうじゃないことも多々ある。確認を行って追記されていない場合、コメント文しかない場合には手動でVendor IDを追記してやるといい。


shell> more ~/.android/adb_usb.ini

# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.
# USE 'android update adb' TO GENERATE.
# 1 USB VENDOR ID PER LINE.

shell> echo "0x2207" >> ~/.android/adb_usb.ini

shell> more ~/.android/adb_usb.ini

# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.
# USE 'android update adb' TO GENERATE.
# 1 USB VENDOR ID PER LINE.
0x2207
shell> adb kill-server; adb start-server

で、最後に確認する。


shell> adb devices
List of devices attached
YRVVHROS0U device

 

いろいろとやり方が書いてあってわかりにくいのだけれど、ここまでたどり着ければオッケーみたい。

 

 

 

参考サイト

MacでAndroid端末が認識されない

Macにgalaxy S2をつなげたのに認識してくれなかった

 

zfsのプールを他のサーバに効率よく移設する

zfsのプールを他のサーバに効率よく移設してみた。

現在zfsで構築されているファイルサーバから、新規に作成されたzfsのファイルサーバへzpoolを転送してみた。

 

流れとしては元サーバでsnapshotを取り、その後にsshで転送して受け側で展開するというもの。


zfs snapshot tank/pool@`date +%Y%m%d`;

zfs send tank/pool@`date +%Y%m%d` | ssh -c arcfour256 root@hosts_to_transfer zfs recv tank/pool@`date +%Y%m%d`


気をつけるのはdateを使って転送しているのでスナップショット作成から転送開始までに日をまたぐと失敗する。(スナップショットの作成はだいたい早いので大丈夫なハズ…)

またsshは-cで軽い暗号化方式を選ぶことで転送速度が上がるらしい。

ちなみに今回のテストでは標準のもの(aes128-cbc)だと8.1MB/sでしたが、arcfour256では でした。

 

一瞬でのバックアップを実現するSolaris ZFS (3/4)

http://www.atmarkit.co.jp/ait/articles/0804/08/news138_3.html

大容量ファイルのSCP転送を高速にする方法

http://d.hatena.ne.jp/rx7/20101025/p1

 

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>

すごいね!

UNIX/Linux環境でFTPを再帰的に拾ってくる方法

こんにちは、岡田洋一です。

UNIX/Linux環境でFTPのディレクトリをまるごと取りたかったんです。が、ftpコマンドだとディレクトリを取ろうとしてもダメなんですね。

get somedirectoryってやるとNot a regular fileって怒られます。


shell> ftp ftp.example.com
Connected to ftp.example.com.
220 ::ffff:192.0.2.1 FTP server ready
Name (ftp.example.com:user): user
331 Password required for user
Password:
230 User user logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> get tmp
local: tmp remote: tmp
550 tmp: Not a regular file

ディレクトリを再帰的に取りたい、っていう場合にはwgetを使うと良いようです。


shell> wget -r ftp://user:password@ftp.example.com//path_to_get/

多くの環境でインストールされているwgetで、ワンライナーで取得できるのはいいですね。

参考:  How do you recursively ftp a folder in linux

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);

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

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

wgetでベーシック認証をかける方法

こんにちは、最近スノボによく行っている岡田洋一です。

スノボに行ったりしている間はサーバが自動で処理をしてくれたりする機能、いわゆるバッチ処理があるんですが、こちらがエラーになっていたのでその対処。


shell> wget -O - http://www.example.com/action.php
--2014-01-22 19:23:10-- http://www.example.com/action.php
Resolving www.example.com (www.example.com)... 192.168.1.1
Connecting to www.example.com (www.example.com)|192.168.1.1|:80... connected.
HTTP request sent, awaiting response... 401 Authorization Required
Authorization failed.

見ればベーシック認証でコケている様子。前はどこからでもアクセスできたんですが、今はアクセス制限をしているのでその対応ですね。

wgetのオプションを調べると –userと–passwordで設定する様子。


shell> wget -O - --user=username --password=somepassword http://www.example.com/action.php

HTTP request sent, awaiting response... 401 Authorization Required
Reusing existing connection to www.example.co:80.
HTTP request sent, awaiting response... 200 OK
Length: 11206 (11K) 
Saving to: `STDOUT'

というわけでうまくいきました。

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

こんにちは。

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

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

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

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

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

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

Capture

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

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

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

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

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

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

Debian7.3でsudoができないエラー

所用でDebian7.3を入れることになった。

いつも通り手順に従ってサックリインストールをしたんだけれど、sudoができない。
sudoができないと話にならない。

エラーの内容は以下の通り。

$ sudo ls
[sudo] password for yousan:
sudo: PERM_ROOT: setresuid(0, -1, -1): Operation not permitted
sudo: unable to open /var/lib/sudo/yousan/1: Operation not permitted
sudo: ls: command not found

うーん、setresuidっていうのでエラーが出てる。setresuidってユーザのUIDを切り替えるときに使う関数で、sudoなんかはモロに使ってるんだけど、こいつがエラーを出している模様。
ググってみると同様のエラーが報告されてた。

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=658896

2012年の3月にすでに確認されていたらしい。
内容では
libnss-ldapの代わりにlibnss-ldapdを使え
nscdの代わりにnslcdを使え
とのこと。今回問題が起きた環境はldapでの認証を使っていたのでこの対処をしてみることに。

shell> aptitude purge libnss-ldap nscd
shell> aptitude install libnss-ldapd nslcd

サックリいれかえたらうまくsudoが使えるようになりました。
今後はこれらの二つを使っていこうと思います。

Life 2 Treat Pork