WordPressでTwitter Cardに対応してみた

Twitter Cardなる仕様を知った。TwitterでTweetに含まれる画像やYouTubeのリンクなんかがカードのように表示される機能には気づいていてTwitterが勝手に自動で表示しているだけと思っていたけど、FacebookのOpenGraphProtocolのようにメタタグを指定して通知することができるのがTwitter Card。対応するブログのリンクがつぶやかれると Twitter 上でこのように概要として表示される。

Twitter Card

Twitter Cards の設定項目

Documentation > Twitter Cards にドキュメントがある。カードには画像や動画などいくつか種類があるようでブログの投稿(文章)であれば summary タイプを選ぶ。

summary-cardに設定するプロパティは以下のようなものがある。
https://dev.twitter.com/docs/cards/types/summary-card

カードプロパティ 詳細 必須かどうか
twitter:card カードの種類、summaryを設定する 必須ではない(デフォルトがsummaryなので)
twitter:title タイトル(70文字) 必須
twitter:description 要約(200文字) 必須
twitter:image:src 画像URL(120×120は縮小されたり、60×60以下だと表示されないかも) 必須じゃない

なお、title, description, imageなどはOGPのメタデータがあればそれを代用してれるらしい。これに加えて以下のような共通プロパティがある。

カードプロパティ 詳細
twitter:site カードのフッタに表示されるサイトの@username
twitter:site:id twitter:siteのID版、@usernameと違ってIDは変わらないというメリット
twitter:creator コンテンツの作者の@username
twitter:creator:id twitter:creatorのID版
twitter:domain フッタに表示される

画像をみると誰のアカウントのCardか、この内容はどのdomainで見れるかなどを合わせて表示できるようだ。

@screen_nameを変える気はあまりないのでidではなく@screen_nameを使う。こんな感じのコードをWP_OGP_TWITTER*の値を適宜設定してテーマのheader.phpの <head>〜</head>の間に入れる。サムネイルは最初の画像リンクを使い、なければテーマのディレクトリの/images/ogp_default.pngを使う。抜き出すと以下のような実装になる。

<?php
define('WP_OGP_POST_DESCRIPTION_KEY', 'description');
define('WP_OGP_POST_IMAGE_SIZE', 'medium');
define('WP_OGP_DEFAULT_IMG', get_template_directory_uri() .'/images/ogp_default.png');

// Twitter Usernames
define('WP_OGP_TWITTER_SITE_USERNAME', '@site_username');
define('WP_OGP_TWITTER_CREATOR_USERNAME', '@creator_username');
define('WP_OGP_TWITTER_DOMAIN', 'YourDomain.com');

function ogp_post_description() {
    global $post;
    $description = null;
    if (defined('WP_OGP_POST_DESCRIPTION_KEY')) {
        $description = get_post_meta($post->ID, WP_OGP_POST_DESCRIPTION_KEY, true);
    }
    if (empty($description)) {
        $description = get_the_excerpt();
    }
    return $description;
}
function ogp_post_image() {
    $image = null;
    if ($image_id = get_post_thumbnail_id()) {
        $image = wp_get_attachment_image_src($image_id, WP_OGP_POST_IMAGE_SIZE);
    }
    if (empty($image)) {
        // find first img element
        global $post;
        if (preg_match('/<img[^>]*src\s*=\s*("|\')([^"\']+)("|\')[^>]*>/i', $post->post_content, $matches)) {
            return $matches[2];
        }
    }
    return $image;
}?>
<?php if( is_single() || is_page()): ?>
<?php while(have_posts()): the_post(); ?>
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="<?php echo mb_strimwidth(get_the_title(), 0, 70, '...'); ?>" />
<?php if ($og_description):?>
<meta name="twitter:description" content="<?php echo mb_strimwidth(esc_attr($og_description), 0, 200); ?>" />
<?php endif; ?>
<?php if ($og_description = ogp_post_description()):?>
<meta name="twitter:image:src" content="<?php echo $og_image ?>" />
<?php else: ?>
<meta name="twitter:image:src" content="<?php echo WP_OGP_DEFAULT_IMG ?>" />
<?php endif; ?>
<?php if (defined('WP_OGP_TWITTER_SITE_USERNAME')): ?>
<meta name="twitter:site" content="<?php echo WP_OGP_TWITTER_SITE_USERNAME; ?>" />
<?php endif ?>
<?php if (defined('WP_OGP_TWITTER_CREATOR_USERNAME')): ?>
<meta name="twitter:creator" content="<?php echo WP_OGP_TWITTER_CREATOR_USERNAME; ?>" />
<?php endif ?>
<?php if (defined('WP_OGP_TWITTER_DOMAIN')): ?>
<meta name="twitter:domain" content="<?php echo WP_OGP_TWITTER_DOMAIN; ?>" />
<?php endif ?>
<?php endwhile; ?>

このブログではFacebook向けの設定も含めて使っているので以下のファイルあたりが参考になります。
header.php
header_ogp.php

対応は簡単なので誰かがプラグインを作っているかもしれません。探してみると良いかと。

検証と承認

ブログに設定したらCard Validator を開き、Summaryをカタログから選択。

Validate & Apply のタブを開いて適当な投稿のURLを設定。Server Errorと表示されることもあるけど何度かやっていると成功した。

キャプチャを撮りそこねたが、最初 blog.makotokw.com はまだ Apploval じゃないと赤い文字が出たのでそこにあったリンクからドメインの情報を入力した。どうやらdomain単位でCardを許可するかどうかTwitter側が制限しているようだ。

ドメインの必要な情報はCardプロパティをベースにほとんど埋まっていたのでサイトの所有者に自分のtwitter名を入れてサイトのdescriptionにmakoto_kw’s blogと入れて登録したらすぐにメールが来て承認された。

WordPress Jetpackのパブリサイズ共有のメッセージを自動設定

WordPress.comの機能を個人でインストールしたWordPressでも使うことができるJetpackプラグインが強力すぎる。その中でパブリサイズ共有機能が便利。

これは予めアカウントを設定しておくことでTwitter,Facebook,LinkedIn,Tumblrといったサービスに投稿を公開したときに自動的に共有する機能。

これでTwitterにブログの投稿を共有していたのだけど1つ不満なことがあってそれはデフォルトのメッセージが「%title% %shortlink%」であること。

このメッセージだと自分の書いたリンクを共有しているのか、他の人のページを共有しているのかとてもわかりにくい、またブログのタイトルによってはつぶやきの内容なのかURLのタイトルなのかもわかりづらく不親切である。で「ブログ書いた: %title% %shortlink%」のように変更したい。このメッセージは投稿の編集画面から手動で変更できるのだけど毎回それを設定するのは面倒でやっていなかった。

コードを読んでみると_wpas_messというメタデータに保存しているようなのでactionをフックして書き換えるようにしてみた。

function makotokw_publicize_save_meta($submit_post, $post_id, $service_name, $connection)
{
    if ($service_name != 'twitter') {
        return;
    }
    $prefix = 'ブログ書いた:';
    $title = get_the_title($post_id);
    $publicize_custom_message = get_post_meta($post_id, '_wpas_mess', true);
    if (empty($publicize_custom_message)
        || ($publicize_custom_message &&
            ((strpos($publicize_custom_message, $prefix) === 0 && strpos($publicize_custom_message, $prefix . ' ' . $title) !== 0 )
                || strpos($publicize_custom_message, $title) === 0))) {
        $publicize_custom_message = sprintf(
            "%s %s %s",
            $prefix,
            $title,
            wp_get_shortlink($post_id, 'post')
        );
        update_post_meta($post_id, '_wpas_mess', $publicize_custom_message);
    }
}
add_action('publicize_save_meta', 'makotokw_publicize_save_meta', 10, 4);

publicize_save_metaは共有しているサービスxアカウントの数だけ呼ばれるので若干無駄な気もしたけどTwitterしか共有していないのでこれで良しとした。

プラグインにするほどでも無いので上記のコードはテーマに入れています。

Twitter API変更による3/5というXデー

torneがライブ機能のサービスを休止するというニュースが先週あった。

torne(トルネ)™」ライブ機能サービス休止のお知らせ
「torne(トルネ)™」のライブ機能のサービスを3月4日をもちまして休止いたします。
お客様にお知らせできる追加情報がございましたら、当サイトを通じてお知らせいたします。

torneのライブ機能はチャンネル毎にハッシュタグを設けてTwitterのタイムラインを並べて表示するというTwitter APIをガッツリつかった機能だったので、Twitter API v1.1の仕様変更が原因であることは間違いない。古いTwitter API v1.0は3/5に使えなくなることがアナウンスされている。

TwitterのAPI変更によってAPIを利用しているサービスは頑張って対応するか、対応を諦め機能を削除するか、有料にするなど対象ユーザを絞り込むかなどの選択が迫られている(v1.1にはユーザ数制限があるので)。まだtorneのように対応が明確になるのは良い方で、開発者が対応を発表せず、APIを意識していないユーザがいると3/5は使っているサービスが突然使えなく日になるんじゃないかという気がする。

人事ではないので今更ながらTwitter Libraryを更新しているところ。

twitterがダウンしてもいいようにtwitterウィジェットを最後に読み込む

深夜のアクセス障害について ( 2012年7月27日金曜日 )
http://blog.jp.twitter.com/2012/07/blog-post_27.html

以前からjavascriptによる表示の更新が、twitterのつぶやきの取得で止まることが気になっていた。今回twitterがダウンしたことをいいことにページが表示されてから読み込むテストをしてみた。

twitterがダウンしている時に調べたところ以下のjsonの読み込みでずっとページが待たされていた。
http://api.twitter.com/1/statuses/user_timeline.json

なんとかしてこの処理を後の方へ持っていきたい。そこで今回参考にしたのは以下のサイト。(user_timeline.jsonの読み込みは非同期にならない気がするが)
asynchronous loading of twitter widgets

この記事によるとtwitterウィジェットは内部でdocument.writeしているので単にtwitterウィジェットの処理をページの最後に持っていくことはできない。しかし、ソースを読んでみると

if (!opts.id) {
  document.write('<div class="twtr-widget" id="' + this.id + '"></div>');
}

という処理になっているので先に <div id="twitter_div"></div> を表示したいところに置いておいて、id:'twitter_div'WTR.Widget() に追加すると document.write 処理から解放される。

ということで実際にやってみた。

<div id="twtr-widget"><span class="twitter_loading">LOADING...</span></div>
<script>
(function($){
    $(window).load(function () {
        $.getScript('http://widgets.twimg.com/j/2/widget.js', function(){
            var twitterWidget = new TWTR.Widget({
              version: 2,
              id: 'twtr-widget',
              type: 'profile',
              rpp: 3,
              interval: 30000,
              width: 'auto',
              height: 300,
              theme: {
                shell: {
                  background: '#ffffff',
                  color: '#363636'
                },
                tweets: {
                  background: '#ffffff',
                  color: '#363636',
                  links: '#777777'
                }
              },
              features: {
                scrollbar: false,
                loop: false,
                live: false,
                behavior: 'all'
              }
            }).render().setUser('makoto_kw').start();
    });
  });
})(jQuery);
</script>

ポイントはハイライト部分。widget.jsも遅延で読み込んでいる。

説明するとややこしいが、twitterウィジェットをWordPressのウィジェットとしてサイドバーに表示していて、特にこの処理をフッターへ移動していない。実行はwindow.loadでされるので無理に可読性を悪くしてまで最後に持っていかなくてもいいと判断している。

インスペクタでtwitter関連のファイルの読み込みが一番最後で行われていることを確認。オッケー。

Ubiquity 0.6 でつぶやいてリプライだけ確認する

昔の記事の続編。UbiquityアドオンがFirefox 3.6に対応していなくてずいぶん放置して段々Chromeしか使わないようになって・・・。でもMozillaのホームページをからUbiquityにたどるとバージョン 0.6にてFirefox 3.6に対応していた。なぜか、0.5.4から0.6にアップデートされなかったのだが、0.5.4を削除して0.6を直接入れたら無事使えるようになった。

なお、現時点のFirefox 4 Beta 6には対応していなかった。というか、Ctrl + Spaceがすでに違う機能に割り当てられているあたりが将来を不安にさせる。面白い機能だし標準でいれてほしいくらいの機能なのだが・・・

さて、前回つくったtwitter用のコマンドはtwitterが基本認証をやめたのでつかえなくなっている。でもUbiquityの標準コマンドでtwitterのpostは残っている。コードを見てみたらoauthにがんばって対応していた。これをコピーしてもよいのだけどjavascriptなんだからそのまま使いたい。

調べてみたら var twitter = CmdUtils.getCommand('twitter'); で既存のCommandを取得できた。javascriptにはプロパティのアクセス制限はないからTwitterCommandのoauthがらみのfunctionを直接呼び出せる。

対応したコードはgistで更新しています。

symfonyとphp-twientでtwitterでログインするサイトをつくる

symfonyでつくったウェブサイトでtwitterを使ってログインするようにしてみた。自分でphp-twientというライブラリをつくっておいてもともとクライアント(bot)用だったのとOAuthも忘れてしまっていたので備忘録メモ。

復習。

まず、サイトをtwitterにアプリケーション登録する。http://twitter.com/oauth_clients
サイトなのでApplication TypeをBrowserにして、さらにログインに使うのでYes, use Twitter for login にチェックを入れる。

ここからsymfonyによる開発。今回のメモの実績はsymfony 1.4.2 (sandbox)。

twitterにアプリケーション登録して取得したキーを /apps/frontend/config/app.yml に書く

all:
  twitter:
    consumer_key: ’your twitter consumer key'
    consumer_secret: 'your twitter consumer securet'

次にphp-twientを取得して配置する
/path-to-sf_project/lib/vendor/php-twient

php-twientはautoload機能を持っているがとりあえずsymfonyにやらせる
apps/frontend/config/autoload.yml をつくって以下を書く

autoload:
  php-twient:
    name: php-twient
    path:        %SF_LIB_DIR%/vendor/php-twient/src
    recursive:   on

認証の処理を担当するモジュールを作成する、とりあえずoauthという名前にする。

$ php symfony generate:module frontend oauth

oauth/twitterというURLで認証実行、oauth/twitter_returnというURLでtwitterからのcallbackを処理する。すなわち /apps/frontend/modules/oauth/actions/actions.class.phpexecuteTwitter メソッド, executeTwitter_return メソッドをそれぞれ実装する。

処理の流れとしては

executeTwitter

  1. Twitter::oAuth() でサイトのConsumerキーを渡して Twitter_Auth_OAuth を取得
  2. Twitter_Auth_OAuth::getRequestToken にてcallbackを指定してリクエストを投げる
    • (アプリケーション登録したcallbackとは別のものも指定できるので開発時は重宝する)
  3. 成功するとユーザのauthTokenが手に入るので Twitter_Auth_OAuth::getAuthorizeUrl で認証URLを取得し飛ばす。コールバックに戻ってきたときに使うので一旦このtokenはセッションに入れとく
  4. twitterの認証URLでごにょごにょしてユーザが許可したらcallbackのURLに戻ってくる( executeTwitter_return が実行される)

executeTwitter_return

  1. Twitter::oAuthで今度はConsumerキーとセッションに入れてたtokenを使って Twitter_Auth_OAuth を取得
  2. twitterからもらった oauth_verifier をつかって Twitter_Auth_OAuth::getAccessToken を呼び出す
  3. twitter_idを取得できたのでこれをユーザのIDとしてログイン済みとする

コード

class oauthActions extends sfActions
{
     public function executeTwitter(sfWebRequest $request)
     {
          sfContext::getInstance()->getConfiguration()->loadHelpers(array('Url'));
          $twitter = new Twitter();
          $auth = $twitter->oAuth(sfConfig::get('app_twitter_consumer_key'),sfConfig::get('app_twitter_consumer_secret'));
          $token = $auth->getRequestToken(url_for('oauth/twitter_return',true));
          if ($token['oauth_callback_confirmed']) {
               $this->getUser()->setAttribute('oauth_token',$token);
               $this->redirect($auth->getAuthorizeUrl($token));
          }
          return sfView::NONE;
     }
     public function executeTwitter_return(sfWebRequest $request)
     {
          $oauth_token = $request->getParameter('oauth_token');
          $oauth_verifier = $request->getParameter('oauth_verifier');
          $token = $this->getUser()->getAttribute('oauth_token');
          if (!$token || $token['oauth_token'] != $oauth_token) {
               return sfView::ERROR;
          }
          $twitter = new Twitter();
          $auth = $twitter->oAuth(
               sfConfig::get('app_twitter_consumer_key'),
               sfConfig::get('app_twitter_consumer_secret'),
               $token['oauth_token'],
               $token['oauth_token_secret']);
          $token = $auth->getAccessToken($oauth_verifier);

          $this->getUser()->setAuthenticated(true);
          $this->getUser()->setAttribute('twitter_id', $token['user_id']);
          $this->getUser()->setAttribute('twitter_account', $token['screen_name']);
          return sfView::NONE;
     }
}

一応php_twientうごいた。ほっ。symfonyなんか知らんって人でも $request->getParameter$_REQUEST ,
$this->getUser()->getAttribute , $this->getUser()->setAttribute$_SESSION に脳内変換してもらえればだいたいわかるはず。

php_twientはエラーは例外吐くので適当にtry-catchしてエラーページ表示してください。

twitterで読んだ本の著者をfollowする活動

相変わらず人見知りで知らない人をfollowerしていないので読んだ本の著者がアカウントもってたらfollowerして感想をmentionで送る活動をはじめようとしてみた。

しかし著者のtwitterを探すのが難しい。まずtwitter以前に本に自分のサイトのURLの載せている人がほとんどいない。もうその時点でアウトだと思われる。インターネットで自分から情報を公開しようという意図がそこで見えないと仮にtwitterをやっていたとしても周囲の人しか本人とはわからないんじゃないという気がする。

またサイトのURLがあれば勝ちかというと、もちろんそこで初めて見つけてからがスタートであって意外にブログの中からtwitterアカウントを探すのが難しい。思いもよらない画像にリンクがはってあったりしてスルーしそうになったり。そんなこんなで家にある書籍から探してみたもののたったの10人しか見つからなかった。

これからアカウントをとる人もいるだろうし、くじけずに続けてみよう。

php-twient 0.2リリース – TwitterのStreaming APIに対応

今まで横目でちらっと見てるだけだったTwitterのStreaming APIですが、今年に入って正式リリースになったのでphp-twientで対応してみました。

サンプルコードは以下のような感じ

<?php
require_once 'Twitter.php';

$user = 'your account';
$pass = 'your password';

$twitter = new Twitter();
$twitter->basicAuth($user, $pass);
$twitter->streaming('statuses/filter',array('track'=>'Sushi,Japan'),'_callback');
function _callback($status) {
    static $count = 0;
    echo $status['user']['name'].':'.$status['text'] . PHP_EOL;
    return ($count++<5);
}
?>

Twitter Streaming API by php-twient 0.2

PHPでStreamingする場合、数行でかけるのでライブラリは必要なさそうな気がしますが。

OAuth対応、cURLなしで動くphp twitter library, php-twientを公開

今年最初のリリースはphpクラスライブラリ。つっても去年つくったのをリリース用に整形しただけなんだけど・・・

特徴

  • PHP5で動く、PHP4は非サポート(動くかもしれないが基本的に対応する予定なし、むしろPHP5.3以降必須にしたいくらい)
  • cURLが無くても動く。あったらcURLを使う。
  • Basic認証とOAuthに対応(Andy Smith 氏のbasic php libraryを利用)
  • twitter APIの各メソッドは一つのcallメソッドで対応、twittterのメソッド名とarrayパラメータを渡して実行する
  • エラーは例外を発行

予定

  • まだ自分が使うメソッドしか用意してないのでゆくゆく他のメソッドに対応する
  • OAuthの部分がPHP5.3以降だとPHP Deprecatedなので修正する

余談

むかしPEARのService_Twitterとかつかってちょっと遊んだことがあったけど、なんで今更こんな後発のライブラリをつくったのか。

twitter-botをつくる機会があったんだけどPEARは使いたくなくて、http://apiwiki.twitter.com/Libraries#PHPあたりを見て試してたんだけど、なんと動かすマシンでphp-curlが入ってない。そして使いたいやつに限ってcURLが使われてる。好きにいじっていいマシンじゃないし、別にbasic認証ごときfile_get_contentsでいけるやろ。単にrequestをpostするだけやしすぐできるわ。とタカを括って書き始めてすぐできたんだけどクライアント名を表示するにはOAuthに対応しないといけないという現状を認識。

ここで使えそうなライブラリをまた探すのか、自分で書くのかいろいろ葛藤したあげく最終的に下記あたりの理由で作ることにした。

  • やっぱりみんなcURLつかってる
  • 最近Retweetなメソッドが増えるなどtwitter-apiが更新されており、追いついてないライブラリがちらほらある
  • 最近増えたtwitter-apiやOAuthをちょこっと触っておくのも良いだろう

cURLを使う、つかわない。Basic認証とOAuthでrequestクラスとauthクラスを抽象化することにした。個人的にどうもauthクラスがrequestを投げる設計が気持ちが悪くて、requestクラスにauthオブジェクトをbindするような感じにしたかったんだけど、実際書いてみたところOAuthの仕組み上、そう書きたくなることがわかった。そのためOAuthのところだけ相互参照で駄目なつくりになっている・・・ちゃんとやるならOAuthのところを2階層にわけなくちゃいけないんだけどあんまり冗長にしたくないので駄目な設計とわかりつつ妥協をしている。

Ubiquityでつぶやいてリプライだけ確認する

大体の場合つぶやこうと思ってクライアントなりWebなり使おうとすると、自分のtimelineも見ることになってしまい、仕事中だと気が散ってしまう。

そこでUbiquityでつぶやくことにした。(UbiquityはFirefoxのプラグインでキーボードインタフェースでコマンドを打っていろいろできるツールで、コマンドはjavascriptで簡単に追加できる)
ただこれだとreplyとかあっても気がつけないので、replyだけみるUbiquity Commandを書いてみた

Twitterをチラ見するUbiquityコマンド作った

http://d.hatena.ne.jp/snaka72/20080918/1221729673
を参考にさせてもらった。

ふと思ったけど、firefoxだから文字列の結合のためにArray.joinは使わなくてよい気がした。IEを気にしないでjavascriptが書けるのもUbiquityの素敵なところだ。