WordPress投稿の更新日時を変更しないようにする(Gutenberg対応)

投稿の更新日時を変更しないようにする機能は様々なテーマやプラグインで提供されていますが、Gutenbergで動作しないものが多いのでその対処方法です

この機能がGutenbergで動作しない

これまで更新日時を変更しないようにする機能をプラグインで利用していました。

「WP Last Modified Info」というプラグインは投稿の更新日時を変更しないようにしたり、入力した日時にできる機能のプラグインです。

最近まではClassic Editorを使っていてGutenbergはほとんど利用したことがありませんでした。

なので、Classic Editorでは問題なく動作していました。

実はこのプラグインは基本的にはGutenbergに対応しています。

最近になって投稿の作成・更新は主に標準のGutenbergを使うようになったのですが、このプラグインでも動作はしているように見えました。

ところが動作しない場合もあります。

このプラグインの他でもテーマにこの機能があるものもあります。

そういったものも比較のために見てみましたが、プラグインよりさらに動作しなかったりします。

動作しない機能は「更新日時を変更しないで投稿を更新する」機能です。

複雑なことにその機能のあるプラグインやテーマによって、動作しない内容が違います。

例えば、あるプラグインは投稿編集画面を開いた状態で設定を切り替えて更新ボタンを押すと、更新日時を変更したりしなかったり、変更しない設定にしたときに期待した日時になってなかったりします。

あるテーマの機能を使うと、更新日時が変更されてしまいます。

原因

なぜプラグインやテーマによって動作が違うのかと思いソースコードを少し見てみました。

まずは、この機能を実現するための仕組みを簡単に整理します。

  • 投稿画面に更新日時を変更するかしないかを選択するラジオボタンを設置
  • 更新ボタンが押されたときにラジオボタンで選択している設定をカスタムフィールドに保存
  • 「wp_insert_post_data」をフィルターフックしてラジオボタンで選択している設定を元に更新日時を操作

上記2つ目の設定内容をカスタムフィールドに保存するのは、次に投稿画面を表示したときに前に設定した内容を表示するための記憶です。

上記3つ目のフィルターフックで、実際の更新日時を操作しています。

フィルターフックして呼び出される関数内で投稿画面の更新日時を変更するかどうかのラジオボタンの設定内容を取得します。

その設定が「更新日時を変更しない」設定の場合は以下の処理を行います。

ここでは3種類の処理例を挙げます。そしてこの処理の動作の違いがGutenbergで動作しない(表面的な)原因となります。

例①

$mydata = isset($_POST['ラジオボタンの名前']) ? $_POST['ラジオボタンの名前'] : null;
if( $mydata == "更新日時を変更しない" ){
  unset( $data["post_modified"] );
  unset( $data["post_modified_gmt"] );
}

例②

if( isset($postarr['hidden入力名'] ) && $postarr['hidden入力名'] == 1 ) {
    if ( !empty($postarr['post_modified']) && !empty($postarr['post_modified_gmt']) ) {
	    $data['post_modified'] = $postarr['post_modified'];
		$data['post_modified_gmt'] = $postarr['post_modified_gmt'];
	}
}

例③

if( isset($postarr['hidden入力名'] ) && $postarr['hidden入力名'] == 1 ) {
	if ( !empty($postarr['hidden更新日時入力フィールド']) ) {
	    $data['post_modified'] = $postarr['hidden更新日時入力'];
		$data['post_modified_gmt'] = get_gmt_from_date( $postarr['hidden更新日時入力'] );
	}
}

これらのコードを読むときに改めてどのような動作になることを期待するのか確認すると、「現在の更新日時のまま編集した投稿内容だけを更新」ができることを期待します。

【例①を見ます】

ラジオボタンの設定を取得して「更新日時を変更しない」場合は、

    unset( $data["post_modified"] );
    unset( $data["post_modified_gmt"] );

を実行します。しかし、これがGutenbergでは期待した動作をせず、この更新日時を格納している領域をunsetしても更新日時を変更してしまいます。Classic Editorでは動作します。

【例②を見ます】

「更新日時を変更しない」hiddenのinputタグの値が有効になっている場合(hiddenで作成しているinputタグの値は$postarr[‘タグの名前’]で取得できます)、現在更新しようとしている更新日時を前の更新日時に置き換えます。

しかし、これもGutenbergでは期待した動作をせず、この方法でも更新日時を更新したときの日時に変更してしまいます。Classic Editorでは動作します。

【例③を見ます】

「更新日時を変更しない」hiddenのinputタグの値が有効になっている場合(hiddenで作成しているinputタグの値は$postarr[‘タグの名前’]で取得できます)、現在更新しようとしている更新日時をhiddenのinputタグの値に入った前回の更新日時に置き換えます。

これがGutenbergでも期待通りの動作をします。ただし、動作しない場合もあります。

それは投稿画面を開いたまま設定を変えても動作しません。

例えば、通常更新した後で、投稿画面を開いたまま再び更新しようと設定を変えて「更新日時を変更しない」にして更新すると更新日時が消えたりします。

これはどうも、投稿画面をリロードしないと発生するようで、更新ボタンを押して更新した後、画面をリロードするか違うページへ行ってから投稿画面を開き直すと現象は出ないようです。

これもClassic Editorでは投稿画面を開いたまま設定を複数回変えても期待通りの動作をします。

対策

結局、例③の処理をしているテーマかプラグインを使えって、変更するたびに画面をリロードすればGutenbergでも期待通りこの機能を使うことができます。

プラグインだと「WP Last Modified Info」が例③の処理で実装されているので動作すると思います。

そこで、もう少しなんとかならないかと、この処理をベースに少し改造してみました。

「更新日時を変更しない」設定を取得する処理で、画面のinputタグの値を取得していますが、これをカスタムフィールドに設定を保存してフィルターフックしたときに設定をカスタムフィールドから取得するように変更してみました。

ついでに更新日時を削除する処理も実装しました。結果として、現象は変わりませんでした・・

ですが、画面をリロードせずに期待通りの動作をする場合もあり、こちらの方が動作は安定しているように見えました。なので一応実装したソースコードを掲載します。

できるだけCodexのサンプルや説明に近い形で実装しています。参考にどうぞ。

/**
 * 更新日時をコントロール
 * 投稿と固定ページの編集画面メインカラムにボックスを追加
 */
function add_update_custom_box() {
  /* 投稿・カスタム投稿編集画面にカスタムメタボックスを追加 */
  $screens = get_post_types();
  foreach( $screens as $screen ) {
    add_meta_box( 'update_mode', __('Update Mode', 'codeaid'), 'update_mode_custom_box', $screen, 'side', 'high' );
  }
}
add_action( 'admin_menu', 'add_update_custom_box' );

/**
 * ボックスのコンテンツをプリント
 *
 * @param WP_Post $post The object for the current post/page.
 */
function update_mode_custom_box( $post ) {
  // nonceフィールドを追加して後でチェックする
  wp_nonce_field( 'save_update_mode_custom_box_data', 'update_mode_custom_box_nonce' );

  /*
   * DBから既存のvalueを検索してフォームのvalueに使うためにget_post_meta()を使用する
   */
  $update_mode = get_post_meta( $post->ID, 'update_mode', true );
  $list = array( 'normal'=>'通常更新', 'postonly'=>'更新のみ', 'delete'=>'更新削除' );

  echo '<div style="padding-top: 3px; overflow: hidden;">';
  foreach( $list as $_id=>$_name ) {
    echo  '<div style="width: 100px; float: left;">';
    echo    '<label>';
    if ( $_id == $update_mode || (empty($update_mode) && ($_id == 'normal')) ) {
      echo  '<input type="radio" name="update_mode" value="'.$_id.'" checked="checked">';
    } else {
      echo  '<input type="radio" name="update_mode" value="'.$_id.'">';
    }
    echo    $_name;
    echo    '</label> ';
    echo  '</div>';
  }
  echo '</div>';

  if( empty(get_post_meta( $post->ID, 'last_modified', true )) ) {
    add_post_meta( $post->ID, 'last_modified', get_the_modified_time( 'Y-m-d H:i:s' ), true );
  } else {
    update_post_meta( $post->ID, 'last_modified', get_the_modified_time( 'Y-m-d H:i:s' ) );
  }
}

/**
 * 投稿が保存されたとき、カスタムデータも保存する
 *
 * @param int $post_id The ID of the post being saved.
 */
function save_update_mode_custom_box_data( $post_id ) {
  /*
   * save_postアクションは他の時にも起動する場合があるので、
   * 先ほど作った編集フォームのから適切な認証とともに送られてきたデータかどうかを検証する必要がある。
   */

  // nonceがセットされているかどうか確認
  if ( ! isset( $_POST['update_mode_custom_box_nonce'] ) ) {
  	return;
  }

  // nonceが正しいかどうか検証
  if ( ! wp_verify_nonce( $_POST['update_mode_custom_box_nonce'], 'save_update_mode_custom_box_data' ) ) {
  	return;
  }

  // 自動保存の場合はなにもしない
  if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
  	return;
  }

  // ユーザー権限の確認
  if ( isset( $_POST['post_type'] ) && 'page' == $_POST['post_type'] ) {
  	if ( ! current_user_can( 'edit_page', $post_id ) ) {
  		return;
  	}
  } else {
  	if ( ! current_user_can( 'edit_post', $post_id ) ) {
  		return;
  	}
  }

  /* 安全が確認できたのでデータを保存する */

  // データがセットされているか確認する
  if ( ! isset( $_POST['update_mode'] ) ) {
  	return;
  }

  // ユーザーの入力を無害化する
  $mydata = sanitize_text_field( $_POST['update_mode'] );

  if( empty(get_post_meta( $post_id, 'update_mode', true )) ) {
    add_post_meta( $post_id, 'update_mode', $mydata, true ); // update_modeがなければカスタムフィールド作成
  } elseif( $mydata != get_post_meta( $post_id, 'update_mode' )) {
    update_post_meta( $post_id, 'update_mode', $mydata ); // update_modeと入力した値が違う場合はカスタムフィールド更新
  } elseif( "" == $mydata ) {
    delete_post_meta( $post_id, 'update_mode' ); // 入力したデータがなければカスタムフィールド削除
  }
}
add_action( 'save_post', 'save_update_mode_custom_box_data' );

/* 「更新」以外は更新日時を変更しない */
function my_insert_post_data( $data, $postarr ){

  if( isset($_POST['update_mode']) && $_POST['update_mode'] == 'postonly' ) {
    $last_modified = get_post_meta( $postarr['ID'], 'last_modified', true );
    if ( isset($last_modified) ) {
      $data['post_modified'] = $last_modified;
      $data['post_modified_gmt'] = get_gmt_from_date( $last_modified );
    }
  } elseif( isset($_POST['update_mode']) && $_POST['update_mode'] == 'delete' ) {
    $data['post_modified'] = $data['post_date'];
    $data['post_modified_gmt'] = get_gmt_from_date( $data['post_date'] );
  }
  if (isset($_POST['update_mode'])) {
  }
  return $data;
}
add_filter( 'wp_insert_post_data', 'my_insert_post_data', 99, 2 );