Because We Love Happy Coding

フリーライターからエンジニア × 講師。発信力だけあり余ってる感じ

友人のブログの移行を手伝った JavaScriptからWordPressAPIへ投稿

今日もまたコーディング。だって僕らはHappy Codingが大好きだから。

目次

友人が古いブログをWordPressに移行したいという。古いブログはMovableTypeなので、普通ならエクスポートインポートすればいいのだが、あまりに古いこともあり、もうMovableTypeの管理画面に入れないとのこと。画面から手作業でコピペをするのが辛いということだった。ふうむ。

環境

試行錯誤など

画面から取り出すということであれば、JavaScriptを使うのが良さそう。

ChromeのTampermonkeyであれば、JavaScriptをねじこんで実行させることができる。DOMごと引っこ抜くのはできそうだ。

データを抜いたら次はWordPressへの投稿だ。

最初は昔懐かしい「PressThis」ブックマークレットで突っ込めないかと思ったのだが、4.9で標準機能から外されてプラグインになっていたうえに、どうもHTMLを拾わず、プレーンテキストで読み込んでしまうみたい。これだとあまり旨味がない。

他にブックマークレットを探してもみたが、あまりこれ、というものがなかった。

Webであれこれ調べるうち、書き込むならREST APIを使えばいい、と思いついた。

最初はPHPを踏み台にするつもりだったが、JavaScriptからajaxで投げればなんとかなりそう。

つまずくポイント

Application Password」プラグインを導入

APIを使って投稿するためには、「Application Password」プラグインを導入し、ユーザーの設定画面で専用のパスワードを生成する必要がある。

Application Passwords – WordPress プラグイン | WordPress.org 日本語

APIに投げるパスワードにうっかりログインパスワードを使ってしまうと失敗する。WordPressのユーザー設定画面で、専用パスワードを発行してそれを使う。

パーマリンク設定を変更する

後述するが、パーマリンク設定がデフォルトの状態ではREST APIが無効化されているという不思議な仕様がある。まあ素人向けにはAPI開いておくの危険ということなのかもしれない。

WP REST APIで404が返ってくる。これはパーマリンク設定のせいだ! | PRESSMAN*Tech

元記事のフォーマットを確認

記事はh2が日付、h3がタイトル、本文はだいたいpタグだった。画像もpタグに囲まれている。

html4.1の時代なので、articleのように記事の明確な範囲を示すタグがないのは残念。幸い、記事のフッターのような感じでp.postedがあったので、これを目印に切り出すことにする。jQuerynextUntil()メソッドが使えそうだ。

記事の日付というか時刻表示もp.postedからslice()する方が早そう。

var date = $('p.posted')[0].innerHTML.slice(0,16);

moment.jsの実力

記事の日付にちょっと不整合があった。月は1桁か2桁なのだが、日付だけがなぜか半角スペースで桁揃えされている。

「2005年1月 9日」 「2005年10月10日」

これをJSで区別するのはめんどくさそうだなと思ったが、試しにMoment.jsを導入してみたら難なく差を吸収した。

Moment.js | Docs

var test = moment('2009年6月 2日 10:09','YYYY年M月D日 hh:mm');

これはありがたい。WordPressに出力する時は.format('YYYY-MM-DDThh:mm:00')を使った。

Cors

Cross Origin Resource Sharing 。

テスト環境では別ドメインのサーバーに投げるので、functions.phpに記述する。

//Allow Cors for Tampermonkey
function add_cors_http_header(){
    header("Access-Control-Allow-Origin: http://www.original_blog_domain");
}
add_action('init','add_cors_http_header');

本番は同じドメイン上にあるから大丈夫なはず、かな。

Uncaught TypeError: Illegal invocation

Base64エンコード

Base64エンコードについては以下の記事が参考になる。

javascriptでBase64 - Qiita

  var url = blog_domain + '/wp-json/wp/v2/posts';
  var Base64 = {
    encode: function(str) {
        return btoa(unescape(encodeURIComponent(str)));
    },
    decode: function(str) {
        return decodeURIComponent(escape(atob(str)));
    }
};
//passwordはApplication Passwordプラグインを導入してユーザー設定で取得
var authorizationToken = Base64.encode(username + ':' + password);

ContentType

contentType: 'application/json',ajaxのパラメータに含める必要があった。これに気付かなかったばっかりに苦労した……。

    $.ajax({
      url : url,
      cache : false,
      type : 'POST',
      processData: false,
      beforeSend : function(request) {
        request.setRequestHeader("Authorization", 'Basic ' + },
      contentType: 'application/json',
      dataType: "json",
      data:JSON.stringify({
        title : "test",
        content : "<p>testtester</p>",
        status : "draft",
        date: '2019-11-12T23:23:00'
        })
    }).done().fail().always();

とりあえずテスト投稿には成功。

どっこい本番環境では動かない

CORSのヘッダーが戻らずpreflight requestが拒否られる。

WP REST APIで404が返ってくる。これはパーマリンク設定のせいだ! | PRESSMAN*Tech

パーマリンク設定を変えたらたしかにCORSのヘッダーが表示されるようになった。

だが、Access-Control-Allow-Headersが戻ってないと言われる。ほかのヘッダーもどうも思った通りになっていない。

WordPressの方で出しているヘッダーがあって、置き換えられていないみたい。

未解決

一晩おいたらアクセス可能になった。なぜぇ?

何かのキャッシュの問題のようだけども。

参考記事