プラグインなしでメディアファイルを置き換える方法

Cocoonカスタマイズ集
この記事は約9分で読めます。
記事内に広告が含まれています。

はじめに

WordPressには登録済みの画像を直接差し替える機能がなく、従来は「再アップロードして投稿内の画像も貼り直す」必要がありました。

そこで、プラグイン「Enable Media Replace」などを使わずに、メディアの画像を差し替えます。

エディターで本文中の画像を個別に置き換えなくても、メディア→ライブラリ→「さらに詳細を編集 」から簡単にメディアファイルを差し替えることができます。

この記事では、この方法について説明します。

完成イメージ

実装手順

以下のコードをfunctions.phpに追加します。

// メタボックスを追加
add_action('add_meta_boxes_attachment', function () {
  add_meta_box(
    'replace_media',
    'ファイルを置き換える',
    'render_replace_media',
    'attachment',
    'side',
    'high'
  );
});


/**
 * メタボックスの入力フォームを出力
 *
 * @param WP_Post $post 添付ファイルオブジェクト
 * @return void
 */
if (!function_exists('render_replace_media')):
function render_replace_media($post) {
  $nonce = wp_create_nonce('replace_media_action');

  // 現在のファイルURLを取得
  $current_url = wp_get_attachment_url($post->ID);

  ?>
  <div class="replace-media">
    <!-- プレビュー画像 -->
    <p><img id="replace-media-preview" class="replace-media__preview" src="<?= esc_url($current_url); ?>" style="max-width:100%; height:auto; display:block; margin-bottom:10px;"></p>

    <!-- ファイル選択 -->
    <p><input type="file" id="replace-media-input" class="replace-media__input" name="file"></p>

    <!-- 上書きボタン -->
    <p><button type="button" class="button button-primary replace-media__button" id="replace-media-button" data-attachment-id="<?= esc_attr($post->ID); ?>" data-nonce="<?= esc_attr($nonce); ?>">上書き</button></p>
  </div>
  <?php
}
endif;


// 管理画面用JS・CSSを追加
add_action('admin_enqueue_scripts', function ($hook_suffix) {
  if ($hook_suffix !== 'post.php') {
    return;
  }

  // 現在の画面情報から添付ファイル編集画面であることを確認
  $screen = get_current_screen();

  if (!$screen || $screen->post_type !== 'attachment') {
    return;
  }

  ob_start();
  ?>
  jQuery(function ($) {
    // ファイル選択時にプレビューを更新
    $('#replace-media-input').on('change', function () {
      var input = this;

      if (input.files && input.files[0]) {
        var reader = new FileReader();

        reader.onload = function (e) {
          $('#replace-media-preview').attr('src', e.target.result);
        };

        reader.readAsDataURL(input.files[0]);
      }
    });

    // 「上書き」ボタンクリック時に処理
    $('#replace-media-button').on('click', function () {
      var button = $(this);
      var fileInput = $('#replace-media-input')[0];

      // ファイル未選択は処理対象外
      if (!fileInput.files.length) {
        alert('ファイルを選択してください');
        return;
      }

      // 送信用のフォームデータを設定
      var formData = new FormData();
      formData.append('action', 'replace_media');
      formData.append('attachment_id', button.data('attachment-id'));
      formData.append('replace_media_nonce', button.data('nonce'));
      formData.append('file', fileInput.files[0]);

      // Ajaxリクエストを実行
      $.ajax({
        url: ajaxurl,
        type: 'POST',
        data: formData,
        processData: false,
        contentType: false,
        success: function (response) {
          // 処理結果に応じた通知URLを設定
          var status = response.success ? 'success' : 'error';
          var msg = encodeURIComponent(response.success ? response.data.message : response.data);

          window.location.href = window.location.href.split('?')[0]
            + '?post=' + button.data('attachment-id')
            + '&action=edit&file_replace_status=' + status
            + '&file_replace_msg=' + msg;
        },
        error: function () {
          // 通信エラー時の通知URLを設定
          window.location.href = window.location.href.split('?')[0]
            + '?post=' + button.data('attachment-id')
            + '&action=edit&file_replace_status=error'
            + '&file_replace_msg=' + encodeURIComponent('通信エラー');
        }
      });
    });
  });
  <?php
  wp_add_inline_script('jquery', ob_get_clean());
});


// Ajaxアクションを追加
add_action('wp_ajax_replace_media', function () {
  if (empty($_POST['attachment_id']) || empty($_FILES['file'])) {
    wp_send_json_error('ファイルが指定されていません');
  }

  $attachment_id = intval($_POST['attachment_id']);
  $nonce = $_POST['replace_media_nonce'] ?? '';

  // noncecチェック
  if (!wp_verify_nonce($nonce, 'replace_media_action')) {
    wp_send_json_error('不正なアクセスです');
  }

  $file = $_FILES['file'];
  $file_path = get_attached_file($attachment_id);

  // 元ファイルなしは処理対象外
  if (!$file_path) {
    wp_send_json_error('元ファイルが見つかりません');
  }

  // WordPressのファイル操作・画像処理用関数を読み込み
  require_once(ABSPATH . 'wp-admin/includes/file.php');
  require_once(ABSPATH . 'wp-admin/includes/image.php');

  // アップロードファイルを一時保存
  $uploaded = wp_handle_sideload($file, ['test_form' => false]);

  if (isset($uploaded['error'])) {
    wp_send_json_error('アップロード失敗: ' . $uploaded['error']);
  }

  // 既存ファイルを削除
  if (file_exists($file_path)) {
    unlink($file_path);
  }

  // 新しいファイルに置換
  if (!rename($uploaded['file'], $file_path)) {
    wp_send_json_error('ファイル置き換えに失敗しました');
  }

  // 添付ファイルのメタデータを再生成
  $attach_data = wp_generate_attachment_metadata($attachment_id, $file_path);
  wp_update_attachment_metadata($attachment_id, $attach_data);

  // 更新結果を返却
  wp_send_json_success(['message' => 'ファイルを置き換えました']);
});


// 通知メッセージを出力
add_action('admin_notices', function () {
  if (isset($_GET['file_replace_status']) && isset($_GET['file_replace_msg'])) {
    $status = $_GET['file_replace_status'];
    $msg    = urldecode($_GET['file_replace_msg']);
    $class  = $status === 'success' ? 'notice-success' : 'notice-error';

    echo '<div class="notice ' . esc_attr($class) . ' is-dismissible"><p>' . esc_html($msg) . '</p></div>';
  }
});

さいごに

今回紹介した方法を使えば、プラグインを使わずに、メディアファイルを置き換えることができます。
管理画面でのプレビュー表示やAjaxによる非同期アップロードにより、サイト運営中でも安全に画像やその他のファイルを更新可能です。

タイトルとURLをコピーしました