WordPressの投稿に複数のアップロード画像を一括挿入する

WordPressの投稿に複数のアップロード画像を一括挿入する
例えば記事中に画像を多く使うスタイルのブログでは、WordPress の「画像を追加」機能は少々操作が面倒です。アップロードは一括でできますが、「投稿に挿入」は1枚ずつしかできません。挿入後に「画像を追加」ウインドウが閉じてしまうのも手間を増やしています。「ギャラリー」で「1列」が指定できれば良さそうな気もしますが、本文が画像文章画像文章という構成だとギャラリーショートコードでは間に合いません。
そこで「画像を追加」ウインドウに「一括挿入」的なボタンを追加して、画像をダラダラッと本文に挿入する機能を実現してみましょう。この機能はクライアントの ART FACTORY GRAPHICS のサイトリニューアル時に絶対メンドクサイ言われるな、と対策したものです。過去ブログの大量データ移行にも大活躍でした。
ところで、この機能のプラグインを作るという話題をうっかり twitter で見かけたのですけど、世の中にこんだけ人がいりゃぁ同じタイミングで同じ挙動もするだろう、と改めて B'z は Aerosmith をパクってなどいないと確信するに至りました。そう、夢は時間を裏切らないのです。
なお、wpxtreme では 非プラ三原則 に則ってそうそうプラグインは作らないのでヨロシクお願いいたします。

一括挿入ボタンを表示する

なんだかんだ言っても見た目が大事、と なんでもかんでもカタチから入って結局長続きしないアナタに倣って、まずは「画像を追加」ウインドウに[すべての添付画像を投稿に挿入]ボタンを表示するだけ、にトライしてみましょう。ところでなんは何だけどかんは何なんだろうね?
「画像を追加」ウインドウの一番下には[すべての変更を保存]という特に何をするでもない雰囲気のボタンがあります。このボタンを押すことで新たに何か保存されるものがあるのかどうか、これは皆へのGWの宿題にしておきますね。それでは捜査の基本に従って、まずは対象者の身辺を洗ってみましょう。

[すべての変更を保存]ボタンが savebutton クラスを持つ p タグで囲まれてますね。次はこの「savebutton」を追跡します。皆さんのローカルにあるはずの WordPress のソース一式から「savebutton」を検索すると、どうやら wp-admin/includes/media.php media_upload_type_form() で出力しているようです。

function media_upload_type_form($type = 'file', $errors = null, $id = null) {
 (略)
<p class="savebutton ml-submit">
<input type="submit" class="button" name="save" value="<?php esc_attr_e( 'Save all changes' ); ?>" />
</p>
<?php
}

が、ここに追加出力できるようなフックは見当たりません。残念。
では media_upload_type_form() の呼び出し元はどうでしょう。同じ media.php で wp_iframe() の引数に使われて呼び出されているようです。

return wp_iframe( 'media_upload_type_form', 'image', $errors, $id );

wp_iframe() の最後の方で、$content_func という引数で受け取った media_upload_type_form() が呼び出されています。

function wp_iframe($content_func /* ... */) {
 (略)
  call_user_func_array($content_func, $args);  // ここで savebutton の HTML を出力
  do_action('admin_print_footer_scripts');
?>
<script type="text/javascript">if(typeof wpOnload=='function')wpOnload();</script>
</body>
</html>
<?php
}

どうやら独自の HTML を出力できるのはその直下の do_action('admin_print_footer_scripts') のタイミングしか無さそうです。では、早速以下のような javascript でボタンを追加してみましょう。

add_action( 'admin_print_footer_scripts', 'my_add_insert_all_images_button' );
function my_add_insert_all_images_button() {
?>
<script type="text/javascript">
//<![CDATA[
jQuery(document).ready(function($){
  $('p.savebutton.ml-submit')
    .append(
      '<input type="submit" class="button" name="bulkinsert" value="<?php esc_attr_e( 'Insert all images' ); ?>" />'
    );
});
//]]>
</script>
<?php
}

はい、バッチリ表示できました。ところが、このボタンは「画像を追加」ウインドウの「コンピュータから」タブで画像をアップロードしたときにしか表示されません。[すべての変更を保存]ボタンの身辺をもう一度洗い直してみましょう。

  • 「コンピュータから」タブ
    p.savebutton.ml-submit > input.button[name=save]
  • 「URLから」タブ
    なし:以降、このタブについては無視する。
  • 「ギャラリー」タブ
    p.ml-submit > input#save-all.button.savebutton[name=save]
  • 「メディアライブラリ」タブ
    p.ml-submit > input.button.savebutton[name=save]

これらの論理積っぽいセレクタを検討した結果、「画像を追加」ウインドウのすべての[すべての変更を保存]ボタンの隣に[すべての添付画像を投稿に挿入]ボタンを追加するには以下のようにします。

$('p.ml-submit>input.button[name=save]')
  .after(
    '<input type="submit" class="button" name="bulkinsert" value="<?php esc_attr_e( 'すべての添付画像を投稿に挿入' ); ?>" />'
  );

無事、「画像を追加」ウインドウのすべての[すべての変更を保存]ボタンの隣に[すべての添付画像を投稿に挿入]ボタンを追加することができました。すべてボタンすべてボタンくどい。

「画像を追加」ウインドウから本文にテキスト挿入、の仕組み

ボタンを表示するだけでヒト苦労でしたね。カタチから入って長続きしない感じがよくわかりました。投稿本文中に添付画像の HTML を挿入するのは[投稿に挿入][ギャラリーを挿入]ボタンの仕組みをパチれば OK でしょう。んでは、日頃からお世話になっている[投稿に挿入]ボタンの方が親近感があるので、こちらを調べることにします。だってオレ人見知りするし。なお、このボタンの name="send[画像の ID ]" という属性は重要な手がかりかもなので証拠として押収しておきます。 では、コレをクリックするとどういう流れになるのかを明らかにするために、まずは親から攻めてみましょう。コレを含む form タグの action 属性は

action="http://example.com/wp-admin/media-upload.php?type=image&tab=gallery&post_id=投稿ID"

のようになっています。パッと見 wp-admin/media-upload.php を調べても怪しいところはありませんが、最後に

$type = strval($_GET['type']);
  :
$tab = strval($_GET['tab']);
  :
if ( $tab == 'type' || $tab == 'type_url' )
  do_action("media_upload_$type");
else
  do_action("media_upload_$tab");

という記述があります。そう言えば先ほどの actiontype=image&tab=gallery が指定されていました。このクエリ文字列とアクションの関係は以下の通り:

  • 「コンピュータから」タブ ... type=image&tab=type ... media_upload_image アクション
  • 「ギャラリー」タブ ... type=image&tab=gallery ... media_upload_gallery アクション
  • 「メディアライブラリ」タブ ... type=image&tab=library ... media_upload_library アクション

ヤツらはこういった暗号を使って捜査の網をかいくぐっていたんですね。どうやら「ギャラリー」タブの黒幕は media_upload_gallery アクション、wp-admin/includes/media.php に潜伏している模様です。ところでmedhia_upload_なんちゃら アクションは、wp-includes/default-filters.php には記述が無いんですね。これも我々の目を欺くためでしょう。
え、admin 画面でしか使わないからじゃ ... と思ったキミは先入観で目が曇っておるよ。濁っておるのだよ。キミこそがオレの天使だ!最高の女だ!と思い込むのは勝手だが、世の中にはもっと最高な天使がゴロゴロいるので視野を狭めないようにな。それに、金品の授受により天使になってくれるあのコはウソもんなので本気になってはいけない。
さて、media_upload_なんちゃら アクションは media_upload_なんちゃら ファンクションを実行するようになってますので当該のファンクションを調べます。

function media_upload_gallery() {
  $errors = array();

  if ( !empty($_POST) ) {
    $return = media_upload_form_handler();
  :

ん、こいつ media_upload_form_handler() 匂うな。しかも media_upload_なんちゃら ファンクションのすべてで if ( !empty($_POST) ) { $return = media_upload_form_handler(); してやがる。どれ。

function media_upload_form_handler() {
  :
if ( isset($_POST['send']) ) {
  $keys = array_keys($_POST['send']);
  $send_id = (int) array_shift($keys);
}
  :
if ( isset($send_id) ) {
  :
  $html = apply_filters('media_send_to_editor', $html, $send_id, $attachment);
  return media_send_to_editor($html);
}

証拠品の "send[画像の ID]" から画像用の HTML を作成して media_send_to_editor($html) しておるな。どうやら media_send_to_editor() が HTML を editor つまり投稿本文に送っているということらしい。

function media_send_to_editor($html) {
?>
<script type="text/javascript">
/* <![CDATA[ */
var win = window.dialogArguments || opener || parent || top;
win.send_to_editor('<?php echo addslashes($html); ?>');
/* ]]> */
</script>
<?php
  exit;
}
よし、わかった。

すべての添付画像の HTML を投稿に挿入する

以下、仕様。

  • media_upload_ なんちゃらアクションをひととおり先取りする。
  • [すべての添付画像を投稿に挿入]ボタンがクリックされていれば横取りする。
    これは isset( $_POST['bilkinsert']) で判断できる。
  • 投稿に添付されている画像を取得する。
    投稿の ID は $_REQUEST['post_id'] で取得できる。 添付画像の ID の昇順=先に添付した画像を先に出力する。
  • 画像の HTML をお好みで作成する。
  • media_send_to_editor() で本文に挿入!

以下、コード。

add_action( 'media_upload_image', 'my_bulk_insert', 1);
add_action( 'media_upload_gallery', 'my_bulk_insert', 1);
add_action( 'media_upload_library', 'my_bulk_insert', 1);
function my_bulk_insert() {
  if ( isset($_POST['bulkinsert']) ) {
    $postid = $_REQUEST['post_id'];
    $images = get_children(array(
      'post_parent' => $postid,
      'post_type' => 'attachment',
      'post_mime_type' => 'image',
      'order' => 'ASC',
      'orderby' => 'ID'
    ));
    $html = '';
    if($images){
      $ids = array_keys($images);
      $first = true;
      foreach((array)$ids as $id){
        if($first)
          $first = false;
        else
          $html .= '<p><br class="spacer_" /></p>';
        $html .= wp_get_attachment_link($id, 'large');
      }
    }
    return media_send_to_editor($html);
  }
}

以下、実行結果。

CONCLUSION

お待ちかねのまとめソースです。日本人ってまとめ記事とか大好きだよね。以下を functions.php に貼り付ければ OK だぜ。エクケープ忘れとかあったらよきにはからってくれ。

add_action( 'admin_print_footer_scripts', 'my_add_insert_all_images_button' );
function my_add_insert_all_images_button() {
?>
<script type="text/javascript">
//<![CDATA[
jQuery(document).ready(function($){
  $('p.ml-submit>input.button[name=save]')
    .after(
      '<input type="submit" class="button" name="bulkinsert" value="<?php esc_attr_e( 'すべての添付画像を投稿に挿入' );   ?>" />'
    );
});
//]]>
</script>
<?php
}

add_action( 'media_upload_image', 'my_bulk_insert', 1);
add_action( 'media_upload_gallery', 'my_bulk_insert', 1);
add_action( 'media_upload_library', 'my_bulk_insert', 1);
function my_bulk_insert() {
  if ( isset($_POST['bulkinsert']) ) {
    $postid = $_REQUEST['post_id'];
    $images = get_children(array(
      'post_parent' => $postid,
      'post_type' => 'attachment',
      'post_mime_type' => 'image',
      'order' => 'ASC',
      'orderby' => 'ID'
    ));
    $html = '';
    if($images){
      $ids = array_keys($images);
      $first = true;
      foreach((array)$ids as $id){
        if($first)
          $first = false;
        else
          $html .= '<p><br class="spacer_" /></p>';
        $html .= wp_get_attachment_link($id, 'large');
      }
    }
    return media_send_to_editor($html);
  }
}

コードだけ見ると結構短いが、ここに到達するまでには地道な捜査と検証を毛が抜ける思いをしながら繰り返しているのだ。キミがお手軽便利に使っているそのプラグイン、仕上げるまでにどれほどの労力が費やされているか。そのサポートにどれほどの労力を費やさなければならないか。そういったことを少し考えてみると、改めて、感謝せずにはいられなくなるだろう。
感謝のカタチはそれぞれで良い。寄付(ドネーション)も良いし、パッチやアイデアの提供でも良い。毎晩ありがとうございますとプラグイン作者に祈るのも、まぁ、良いかもしれん。そして、いつかは与える側になって欲しい。それがお世話になったコミュニティへの最大の恩返しになる。そういう流れを繰り返すと世の中はどんどんキレイになっていくのだ。ステキやん。
最後に、恩知らずはロクな死に方をしない、とだけ言っておこう。

プログラムを作る仕事に就きたい皆さんへ

ん、全然最後になっとらへん。
仕様仕様と偉いさんは口にするが、そんなものは実在しない。キミのかわいい彼女と同じだ。マボロシなのだよ。いいかい、現場ではソースコードがすべてだ。なので、今のうちにソースを読み解く力をつけておくと良い。
どうやってそんな力をつければいいんですか、ってそりゃアンタ人の書いたコード読み倒しゃぁええがね。世の中にはオープンソースと呼ばれる、世界中のステキな開発者達が書いたコードがそこら中に転がっている。超絶クールなコードもなにこれウンコなコードもあるだろう。何でもいいから読み倒すのだ。WordPress なんて読んで試してがヤリたい放題だぜ。猿並みに限度を知らない思春期のキミにはうってつけだ。
そしてキミが男性なら、もっと女性と接すると良い。話し易いあんまり女っぽくないコじゃダメだぜ。話しかけ難い超イカすあのコ、そうムラムラしちゃうあのコだ。そういうえびちゃんとどう接すると良いかは、今まで wpxtreme で散々説明しているので改めて全記事を読み直すと良い。GW だから時間はたっぷりあるだろう。で、どんなコだろうとメロメロにさせることができるようになれば、何故だかソースを読むセンスが養われていることに気づくはずだ。
読み解く力とは察する力である。察する力を身につけるには女性と接するのが良い。世の中にえびちゃんほどわけのわからんものはないのだ何の話だ。

動作確認バージョン
  • WordPress 2.9.2

18 Comments

  • BLOG: WordPressの投稿に複数のアップロード画像を一括挿入する http://bit.ly/dBDipe

  • おお!WordPressの投稿画面で、複数の画像一括挿入! ソースのコピーだけでなく、ぜひぜひぜひコメントも読んでほしいです。こういう方々がいて下さるので、いろんな事ができるわけです。感謝です。 http://bit.ly/ayWAX0

  • 毎度どうもです。こんな素敵な記事を勝手に紹介などしたら、ロクな死に方しなさそうなので、一言言わせてください。
    ありがとうございますぅ…、いや、マジで。ちょうど一括で画像をポストしたい案件があったのですが、めんどくさくて、どうしようかと思っていたとこだったんですよ。もうね、師匠と呼ばさせてもらいますよ。
    ウチのブログでも、紹介してもいいですかww?

    • kz kz

      どうぞどうぞ。代わりに性格の良い佐々木希を紹介してください。

  • バナー貼ったぜ http://bit.ly/dBDipe RT @ wordcampjp 告知バナーをみんなで貼ろう! #wcYokohama http://wp.me/pTJUA-3P

  • WordPressの投稿に複数のアップロード画像を一括挿入するhttp://bit.ly/bWDnvQ

  • フックがない場所にjsでボタン追加したり、メディアギャラリー周りのコードの読み解きが参考になる。

  • RT @kzxtreme WordPressの投稿に複数のアップロード画像を一括挿入する http://bit.ly/dBDipe

  • どうも、まだまだコミュニティにも恩返しができとらんみみすけです。
    すいませんが、またひとつ教えてください。

    教えていただいたコーディングをfunctions.phpに埋め込み、
    まだ半分ぐらいの理解でソースを眺めながら
    無事、一括貼り付けできました。

    なんて素敵なんでしょう、と小2時間ばかり感動したのち
    サムネイル&リンクでも入れてみようと思いました。
    (ギャラリーだとちょっとやりたいことと違ってまして・・
     そっちをカスタマイズしなさいという話もあるんですが)

    なんか、これが思ったように行かないのですが
    それは当り前で、もっとコード足すんですよ
    というお話しか、いや普通動きますよということで
    私の環境の関係かを知りたいです。

    すいませんが、教えてください
    (ちなみに私はえびちゃんではありません)

    • kz kz

      サムネイル&リンクはどうやって入れましたか?
      間違ってなければ何でも動きます◎イェィ!

  • 結局、
    $html .= wp_get_attachment_link($id, ‘thumbnail’ ,true);
    で、やりたいことができるかと思ったんですが、
    ファイルではなく、ページにリンクされてしまい、
    試行錯誤しましたがうまくいかず断念しました(リビジョン3.0.1)。

    ‘post_type’ => ‘attachment’,
    となっているので、いけるように思うんですが、なんでだろう。。。
    まあ、ギャラリーでカッコ悪いけど似た事が出来たので
    良いことにしました。
    こっちをカスタマイズすることを考えることにします

    どうもです。

  • kz kz

    画像ファイルへのリンクなら wp_get_attachment_link($id, ‘thumbnail’ ); で OK ですよ◎ true を指定してるので画像ページへのリンクになってしまってるんですね。
    ご参考)http://codex.wordpress.org/Function_Reference/wp_get_attachment_link

  • どうも、ありがとうございます。
    そしてレスも遅れてすいません。
    確認しました。できました。
    「俺はいま猛烈に感動している~@星飛雄馬」的に感動中です。

    最初に調べて、なぜかtrueだと信じ込んでいました。
    指摘が無ければ絶対に気がつきませんでした。
    感謝です。

    ちかくに佐々木希がいればいくらでも紹介したいところ
    番宙太ぐらいしかおりません。。。

    ありがとうございました~

  • 最近、WordPress使うのでメモメモ。 > WordPressの投稿に複数のアップロード画像を一括挿入する http://j.mp/avTTKL via @AddToAny

  • ブログの投稿って、画像とかをポチポチ上げるのが辛い。むー

  • ブログの投稿って、画像とかをポチポチ上げるのが辛い。むー / “WordPressの投稿に複数のアップロード画像を一括挿入する  |  wpxtreme” http://t.co/Z5fJU7rB