Работа с медиабиблиотекой WordPress
WordPress уже давно вырос из штанов «блогового движка», и теперь осваивает просторы тру-кул-продакшн системы. Скажем прямо, справляется с переменным успехом, все-таки CMS была создана в те времена, когда только рождался Php 5, и до сих пор пытается сохранять обратную совместимость.
Если «правильно готовить», то на выходе можно получить годный продукт, в плане пользовательского интерфейса в админке все достаточно интуитивно, и за пару дней можно понять принципы работы.
Вступление
Для одного коммерческого проекта, потребовалось создать плагин с выбором файлов из медиабиблиотеки. Подобная задача решается несколькими вариантами: можно сделать обычное текстовое поле, куда писать ссылку на файл, можно в это текстовое поле писать id файла из медиабиблиотеки — но мне захотелось сделать красиво. В WP (WordPress) есть уже готовый функционал работы с файлами, добавление их в библиотеку и выбор. Нужно только подцепиться к этому АПИ.
Задача
Чтобы было проще понять, поставлю конкретную проблему, которую героически преодолею: нужно в каждую публикацию типа post добавить кнопку прикрепления файла из библиотеки. Границы четкие и ясные. Поехали!
Код
Технически, ничто не мешает все нижеописанные куски php писать напрямую в файл functions.php в тему, но мне кажется, что то, что решаю, несколько больше чем функция темы, а скорее даже наоборот, вообще к функциям темы не имеет отношения, поэтому, я рекомендую создать отдельный плагин.
Допустим, входной файл плагина создан, и пока он чистый, кроме служебных строк, которые расскажут движку, что это плагин и его можно включать и выключать. Для начала, нужно подключить АПИ
function my_plugin_enqueue_media() { wp_enqueue_media(); } add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_media' );
Эти строки подключают все стили и скрипты связанные с работой медиабиблиотеки.
А теперь начинается магия. Следи за руками и постарайся не отставать за ходом мысли.
В папку с новый плагином (конечно, папку можно не создавать, но тогда магии не произойдет) нужно положить некоторый js-файлик, о содержимом которого поговорим чуть позже. Будет неплохо, если он будет правильно подключен. Для этого немного модифицируем вышеописанный код.
function my_plugin_enqueue_media() { // Подключение АПИ для работы с медиабиблиотекой wp_enqueue_media(); // Скрипт для выбора файла wp_enqueue_script('add-one-media.js', plugins_url('/js/add-one-media.js', __FILE__), array('jquery')); } add_action( 'admin_enqueue_scripts', 'my_plugin_enqueue_media' );
В функции wp_enqueue_script третьим параметром передается массив зависимостей. Да, я до сих пор использую уже не мэйнстримовый jquery, не нравится, ничто не мешает использовать что-то другое. Сам движок уже содержит jquery, поэтому незачем тянуть свою версию. Обожаю логически структурировать расположение файлов, поэтому js положу в отдельную папку для js, а вдруг потом это все обрастет чем-то большим, пусть уж сразу лежит там где положено.
Добавим саму кнопку для прикрепления файла к записи. Реализуем это через механизм метаполей. Надеюсь что такое хук в WP не надо объяснять? В противном случае, тебе еще рано так глубоко погружаться в кишки WP. Или просто копируй, и пусть оно работает как истинная магия.
/** * Создание блока метаполей для постов */ function add_one_meta() { add_meta_box('add_one_meta', 'Прикрепленный файл', 'add_one_meta_view', 'post'); } add_action('add_meta_boxes', 'add_one_meta'); /** * HTML код блока */ function add_one_meta_view() { global $post; // Если это пост отличный от необходимого, уйдем отсюда, и ничего не отобразим if ($post->post_type != 'post') { return; } // Используем nonce для верификации wp_nonce_field(plugin_basename(__FILE__), 'add_one_nonce'); // Заберем значение прикрепленного файла $add_file_id = get_post_meta($post->ID, 'add_file_id', true); // Ссылка на добавление файлов, если js отколючен $upload_link = esc_url(get_upload_iframe_src('null', $post->ID)); // Поле для выбора файла echo ' <div class="custom_field_itm"> <div class="js-add-wrap">'; if ($add_file_id) : $file_info = get_post($add_file_id); $file_icon = wp_get_attachment_image($add_file_id, 'thumbnail', true); echo '<div class="add_file js-add_file_itm"> <input type="hidden" name="add_file_id" value="' . $add_file_id . '" /> <div class="add_file_icon">' . $file_icon . '</div> <p class="add_file_name">' . $file_info->post_title . '</p> <a href="#" class="button button-primary button-large js-add-file-remove">Открепить файл</a> </div>'; endif; echo '</div><br/> <a href="' . $upload_link . '" class="button button-primary button-large js-add-file">Добавить файл</a> </div>'; }
На выходе получается такое
Как не трудно догадаться, в метаполе планируется хранить только id вложения, а зачем что-то большее? Имея id можно получить хоть черта лысого, было бы желание. Сохраняется все это вот так:
/** * Сохраняем данные, когда пост сохраняется * * @param $post_id * @param $post * * @return mixed */ function omsu_doc_save_postdata($post_id, $post) { // Если это пост отличный от документа, уйдем отсюда if ($post->post_type != 'post') { return $post_id; } // проверяем nonce нашей страницы, потому что save_post может быть вызван с другого места. if ( ! isset($_POST['add_one_nonce']) || ! wp_verify_nonce($_POST['add_one_nonce'], plugin_basename(__FILE__)) ) { return $post_id; } // проверяем, если это автосохранение ничего не делаем с данными нашей формы. if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $post_id; } // проверяем разрешено ли пользователю указывать эти данные if ('page' == $_POST['post_type'] && ! current_user_can('edit_page', $post_id)) { return $post_id; } elseif ( ! current_user_can('edit_post', $post_id)) { return $post_id; } // Сохраним прикрепленный файл $add_file_id = (int)$_POST['add_file_id']; update_post_meta($post_id, 'add_file_id', $add_file_id); return $post_id; } add_action('save_post', 'omsu_doc_save_postdata', 10, 2);
Куча проверок и защиты и 2 строки непосредственно сохранения. Кажется, что уже много написано, но к сути пока не подошел. А вся сила, брат, в JavaScript. Так-то! Вот мы и пришли к нему. Блин! Когда ты уже знаешь как это делать, все кажется таким очевидным, но на момент изучения потратил полдня на формулирование запроса и еще полдня на поиск ответа. А сейчас вот так просто я раскрыл все таинства этого действа, которые по сути уместились в 27 строчек кода. И отчего знания не приходят в голову сразу как только становятся необходимы?
jQuery(function ( $ ) { var frame; // Прикрепить файл к редакции $(document).on('click', '.js-add-file', function ( event ) { event.preventDefault(); // Если окно загрузки уже доступно, просто откроем его if( frame ) { frame.open(); return; } // В противном случае, создадим новое frame = wp.media({ title : 'Выберите файл', button : { text: 'Использовать этот файл' }, multiple: false // Если нужна возможность крепить одним махом несколько файлов }); // показать инфу о прикрепленном файле frame.on('select', function () { // Получим объект со всей информацией о выбранном файле var attachment = frame.state().get('selection').first().toJSON(); $('.js-add-wrap').html('<div class="add_file js-add_file_itm">' + '<input type="hidden" name="add_file_id" value="' + attachment.id + '" />' + '<div class="add_file_icon"><img src="' + attachment.icon + '" alt="" /></div>' + '<p class="add_file_name">' + attachment.title + '</p>' + '<a href="#" class="button button-primary button-large js-add-file-remove">Открепить файл</a>' + '</div>'); }); // Откроем файл frame.open(); }); });
Вот как бы и всё. Теперь нет проблем, чтобы крепить файлы к посту. Можно запуливать в продакшен и радоваться ровно до того момента, пока не потребуется открепить файл =) К счастью это совсем не проблема.
Внутрь замыкания добавляем 4 строчки кода, и теперь плагин умеет откреплять файлы.
// отцепить приложенный файл от редакции $(document).on('click', '.js-add-file-remove', function ( event ) { event.preventDefault(); $(this).closest('.js-add_file_itm').remove(); });
Чтобы добавить немного красивостей (хотя я бы не рекомендовал вам в области «красивостей» консультироваться с человеком взращенным ComicSans’ом) добавим немного css. В самую первую функцию, где подключаются скрипты, допишем еще строку с подключением стилей.
wp_enqueue_style('add-one-media', plugins_url('/css/add-one-media.css', __FILE__));
А в css, например, такие стили:
.add_file { position: relative; overflow: hidden; } .add_file_icon { position: relative; float: left; margin: 0 10px 10px 0; } .add_file_name { position: relative; margin: 0 0 10px; }
Теперь точно всё!
Итог
Показал как работать с медиабиблиотекой, попутно расписал как создавать свои метаполя, как правильно подключать свои скрипты и стили в админку. Кстати, рабочий плагин, созданный по мотивам этого поста, можно забрать на гитхабе. Туда же свои предложения по улучшению и исправлению.
Всем рок!