Как добавить произвольное поле в быстрое редактирование
Как я могу добавить новую колонку в быстрое редактирование и поместить в неё произвольное поле? Я прочитал этот вопрос, но одна из ссылок ведёт на ошибку 404. Мне нужна новая колонка для моего произвольного мета-ключа "summary
" и типов записей "post
" и "episode
".
Вот несколько шагов для создания пользовательского блока быстрого редактирования и пользовательской колонки:
- Создать пользовательский мета-ключ (предполагается, что у вас уже есть один)
- Добавить заголовок и данные пользовательской колонки в админке (предполагается, что вы хотите отображать пользовательский мета-ключ в колонке, если нет, вы можете немного изменить логику для достижения того же эффекта, так как принцип остается тем же)
- Добавить блок быстрого редактирования
- Добавить логику сохранения
- Загрузить скрипт для модификации оригинальной функции inline-edit-post, чтобы поддерживать пользовательские мета-значения
- Подготовить файл скрипта
В этом примере мы добавим поле быстрого редактирования для типа записи Страница
. Следующий код проверен на работоспособность при размещении в functions.php.
1. Создать пользовательский мета-ключ (предполагается, что у вас уже есть один)
Я подготовил пользовательский мета-ключ remark
для типа записи page
2. Добавить заголовок и данные пользовательской колонки в админке
Для пользовательских типов записей можно использовать:
Добавить заголовок колонки
- Для конкретного Screen ID manage_{$screen->id}_columns
- Конкретно для Страниц manage_pages_columns
- Для общих случаев (обычные записи или пользовательские типы записей) manage_posts_custom_column или для конкретного типа записи manage_{$post->post_type}_posts_custom_column для добавления данных в колонку
Добавить содержимое колонки
- Общий случай (обычные записи или пользовательские типы записей) manage_posts_columns с проверкой типа записи через аргумент post_type или для конкретного типа записи manage_{$post_type}_posts_columns для добавления заголовка колонки
Ниже приведен пример для типа записи Страница, а также пользовательского типа записи + стандартного Поста
в качестве примера настройки для нескольких типов записей.
// добавить заголовок пользовательской колонки для пользовательского мета-значения
add_filter('manage_edit-page_columns', 'ws365150_add_custom_columns_title' );
function ws365150_add_custom_columns_title( $columns ) {
$columns['page_remark'] = 'Примечание'; // позже можно использовать __() для поддержки перевода
return $columns;
}
// добавить данные в пользовательскую колонку с пользовательским мета-значением
add_action('manage_pages_custom_column', 'ws365150_add_custom_column_data', 10, 2 );
function ws365150_add_custom_column_data( $column_name, $post_id ) {
switch ( $column_name ) {
case 'page_remark':
echo get_post_meta( $post_id, 'remark', true );
break;
default:
break;
}
}
Ниже приведен пример для пользовательского типа записи episode
+ стандартного Поста
с мета-ключом summary
// добавить заголовок пользовательской колонки для пользовательского мета-значения
// 'manage_pages_columns' или 'manage_edit-post_columns' - оба варианта работают
add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt', 10, 2 );
function ws365150_add_custom_columns_title_pt( $columns, $post_type ) {
switch ( $post_type ) {
case 'post':
case 'episode':
$columns['ws365150_summary'] = 'Краткое описание'; // позже можно использовать __() для поддержки перевода
break;
default:
break;
}
return $columns;
}
// добавить данные в пользовательскую колонку с пользовательским мета-значением для пользовательских типов записей
add_action('manage_posts_custom_column', 'ws365150_add_custom_column_data_pt', 10, 2 );
function ws365150_add_custom_column_data_pt( $column_name, $post_id ) {
switch ( $column_name ) {
case 'ws365150_summary': // указано для этой колонки, назначенной в заголовке
echo get_post_meta( $post_id, 'summary', true );
break;
default:
break;
}
}
3. Добавить блок быстрого редактирования
// для страниц
add_action( 'quick_edit_custom_box', 'ws365150_custom_edit_box', 10, 3 );
function ws365150_custom_edit_box( $column_name, $post_type, $taxonomy ) {
global $post;
switch ( $post_type ) {
case 'page':
if( $column_name === 'page_remark' ): // тот же заголовок колонки, что и в предыдущем шаге
?>
<?php // echo get_post_meta( $post->ID, 'remark', true ); ?>
<fieldset class="inline-edit-col-right" id="#edit-">
<div class="inline-edit-col">
<label>
<span class="title">Примечание</span>
<span class="input-text-wrap"><input type="text" name="remark" class="inline-edit-menu-order-input" value=""></span>
</label>
</div>
</fieldset>
<?php
endif;
// echo 'custom page field';
break;
default:
break;
}
}
// для Постов + пользовательских типов записей
add_action( 'quick_edit_custom_box', 'ws365150_custom_edit_box_pt', 10, 3 );
function ws365150_custom_edit_box_pt( $column_name, $post_type, $taxonomy ) {
global $post;
switch ( $post_type ) {
case 'post':
case 'episode':
if( $column_name === 'ws365150_summary' ): // тот же заголовок колонки, что и в предыдущем шаге
?>
<?php // echo get_post_meta( $post->ID, 'remark', true ); ?>
<fieldset class="inline-edit-col-right" id="#edit-">
<div class="inline-edit-col">
<label>
<span class="title">Краткое описание</span>
<span class="input-text-wrap"><input type="text" name="summary" class="inline-edit-menu-order-input" value=""></span>
</label>
</div>
</fieldset>
<?php
endif;
// echo 'custom page field';
break;
default:
break;
}
}
4. Добавить логику сохранения
add_action( 'save_post', 'ws365150_update_custom_quickedit_box' );
function ws365150_update_custom_quickedit_box() {
// здесь может быть любая логика проверки, опущена для простоты иллюстрации (nonce, наличие $_POST['remark'], сохранение через ajax и т.д.)
// примечание для Страницы
if( isset( $_POST ) && isset( $_POST['remark'] ) ) { // где remark определен в <input name="remark">
update_post_meta($_POST['post_ID'], 'remark', $_POST['remark']);
}
// краткое описание для Поста, пользовательского типа записи
if( isset( $_POST ) && isset( $_POST['summary'] ) ) { // где summary определен в <input name="summary">
update_post_meta($_POST['post_ID'], 'summary', $_POST['summary']);
}
// для отладки в инспекторе, не обязательно, включение этого прервет сохранение, но можно увидеть ответ ajax
// wp_send_json_success( array(
// 'message' => 'Save test!',
// 'post_data' => $_POST,
// ) );
return; // завершить вызов функции
}
5. Загрузить скрипт для модификации оригинальной функции inline-edit-post
add_action( 'admin_enqueue_scripts', function( $page ) {
// добавить логику проверки страницы, это простой пример, можно проверять тип записи и т.д...
if ( 'edit.php' != $page ) {
return;
}
wp_enqueue_script( 'custom-quickedit-box', get_stylesheet_directory_uri() . '/ws365150_custom_quickedit_box.js', array( 'jquery', 'inline-edit-post' ) );
});
6. Подготовить файл скрипта (ws365150_custom_quickedit_box.js, при тестировании вышеуказанного кода разместите в папке темы)
( function( $, wp ) {
// копия оригинальной функции из inline-post-edit.js для переопределения
// на самом деле не обязательно создавать псевдоним объекта, однако его создание может служить заметкой и маркером переопределения для последующего обслуживания
window.customInlineEditPost = window.inlineEditPost;
// переопределение функции: добавление пользовательского мета-значения, основа взята из исходного кода
customInlineEditPost.edit = function(id) {
// console.log( 'custom edit' );
var t = this, fields, editRow, rowData, status, pageOpt, pageLevel, nextPage, pageLoop = true, nextLevel, f, val, pw;
t.revert();
if ( typeof(id) === 'object' ) {
id = t.getId(id);
}
fields = ['post_title', 'post_name', 'post_author', '_status', 'jj', 'mm', 'aa', 'hh', 'mn', 'ss', 'post_password', 'post_format', 'menu_order', 'page_template'];
if ( t.type === 'page' ) {
fields.push('post_parent');
}
// Добавляем новую строку редактирования с дополнительной пустой строкой под ней для сохранения зебровой раскраски.
editRow = $('#inline-edit').clone(true);
$( 'td', editRow ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length );
$(t.what+id).removeClass('is-expanded').hide().after(editRow).after('<tr class="hidden"></tr>');
// Заполняем поля в окне быстрого редактирования.
rowData = $('#inline_'+id);
if ( !$(':input[name="post_author"] option[value="' + $('.post_author', rowData).text() + '"]', editRow).val() ) {
// Автор записи больше не имеет прав на редактирование, поэтому нужно добавить его в список авторов.
$(':input[name="post_author"]', editRow).prepend('<option value="' + $('.post_author', rowData).text() + '">' + $('#' + t.type + '-' + id + ' .author').text() + '</option>');
}
if ( $( ':input[name="post_author"] option', editRow ).length === 1 ) {
$('label.inline-edit-author', editRow).hide();
}
// заполняем пользовательское мета-значение
if ( $( ':input[name="remark"]', editRow ).length === 1 ) {
$( ':input[name="remark"]', editRow ).val( $('#post-' + id + ' .page_remark').text() );
}
if ( $( ':input[name="summary"]', editRow ).length === 1 ) {
$( ':input[name="summary"]', editRow ).val( $('#post-' + id + ' .ws365150_summary').text() );
}
for ( f = 0; f < fields.length; f++ ) {
val = $('.'+fields[f], rowData);
/**
* Заменяет изображение для Twemoji (Twitter emoji) на его альтернативный текст.
*
* @returns Альтернативный текст из изображения.
*/
val.find( 'img' ).replaceWith( function() { return this.alt; } );
val = val.text();
$(':input[name="' + fields[f] + '"]', editRow).val( val );
}
if ( $( '.comment_status', rowData ).text() === 'open' ) {
$( 'input[name="comment_status"]', editRow ).prop( 'checked', true );
}
if ( $( '.ping_status', rowData ).text() === 'open' ) {
$( 'input[name="ping_status"]', editRow ).prop( 'checked', true );
}
if ( $( '.sticky', rowData ).text() === 'sticky' ) {
$( 'input[name="sticky"]', editRow ).prop( 'checked', true );
}
/**
* Создает выпадающие списки для категорий.
*/
$('.post_category', rowData).each(function(){
var taxname,
term_ids = $(this).text();
if ( term_ids ) {
taxname = $(this).attr('id').replace('_'+id, '');
$('ul.'+taxname+'-checklist :checkbox', editRow).val(term_ids.split(','));
}
});
/**
* Получает все таксономии для live auto-fill подсказок при вводе имени тега.
*/
$('.tags_input', rowData).each(function(){
var terms = $(this),
taxname = $(this).attr('id').replace('_' + id, ''),
textarea = $('textarea.tax_input_' + taxname, editRow),
comma = inlineEditL10n.comma;
terms.find( 'img' ).replaceWith( function() { return this.alt; } );
terms = terms.text();
if ( terms ) {
if ( ',' !== comma ) {
terms = terms.replace(/,/g, comma);
}
textarea.val(terms);
}
textarea.wpTagsSuggest();
});
// Обрабатываем статус записи.
status = $('._status', rowData).text();
if ( 'future' !== status ) {
$('select[name="_status"] option[value="future"]', editRow).remove();
}
pw = $( '.inline-edit-password-input' ).prop( 'disabled', false );
if ( 'private' === status ) {
$('input[name="keep_private"]', editRow).prop('checked', true);
pw.val( '' ).prop( 'disabled', true );
}
// Удаляем текущую страницу и дочерние элементы из выпадающего списка родителя.
pageOpt = $('select[name="post_parent"] option[value="' + id + '"]', editRow);
if ( pageOpt.length > 0 ) {
pageLevel = pageOpt[0].className.split('-')[1];
nextPage = pageOpt;
while ( pageLoop ) {
nextPage = nextPage.next('option');
if ( nextPage.length === 0 ) {
break;
}
nextLevel = nextPage[0].className.split('-')[1];
if ( nextLevel <= pageLevel ) {
pageLoop = false;
} else {
nextPage.remove();
nextPage = pageOpt;
}
}
pageOpt.remove();
}
$(editRow).attr('id', 'edit-'+id).addClass('inline-editor').show();
$('.ptitle', editRow).focus();
return false;
};
})( jQuery, window.wp );
Вышеприведенный пример предназначен для быстрого редактирования с использованием хука quick_edit_custom_box
, и вам также может потребоваться учесть массовое редактирование. Вы можете изучить больше, используя хук bulk_edit_custom_box
.

Отлично! Не могли бы вы отредактировать пример для типов записей post
и episode
с пользовательским мета-ключом summary
? Извините, но мне не нравится возиться с файлом functions.php, я могу случайно сломать WordPress. Мне нужно быть уверенным на 100%, что код будет работать.

Я добавил дополнительный пример кода для post
, episode
с мета-полем summary
, включая заметки о заголовке колонки и проверочный пример при сохранении. Вы можете модифицировать и добавить дополнительные проверки в зависимости от ваших потребностей.

Подождите секунду, этот код не работает и просто делает мою страницу редактирования "Все страницы" пустой.

Код обновлен, пожалуйста, измените add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt' );
на add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt', 10, 2 );
. Пожалуйста, не торопитесь отмечать ответ как правильный, пока не протестируете его.

Хм, при тестировании вышеуказанного кода, новое поле summary добавляется в меню быстрого редактирования для типов записей post
и episode
,
но после нажатия "обновить", параметр summary
остается пустым.
Даже если параметр summary
уже был заполнен (без использования быстрого редактирования), поле быстрого редактирования все равно делает параметр пустым.

Я создал тестовый пример для имитации вашего случая и провел тестирование. Обновил javascript и фильтр save_post. Пожалуйста, также убедитесь, что javascript загружается правильно, так как предполагается, что скрипт находится в папке темы. Теперь все должно работать корректно согласно проведенному тестированию.

хороший ответ, хотя... в целом просто ужасно, что всё должно быть так сложно

@Toskan, это можно упростить. Просто данный ответ адаптирован под вопрос автора с мета-ключом из вопроса. Лично я в большинстве случаев предпочитаю универсальные решения, так как у разных людей разные потребности. Если убрать примеры, останется всего несколько шагов. Вкратце, есть 3 части: (1)создание пользовательского мета-ключа (можно сделать вручную или через плагины), (2)добавление кастомных колонок для отображения и (3)создание логики редактирования/сохранения для быстрого редактирования. Поскольку быстрое редактирование основано на Javascript ajax для обеспечения "мгновенного" UX, эта часть выглядит немного избыточной.

Ах да, нет, я не использовал твою javascript часть. Где-то есть гораздо более простое решение для js части...

Попробуй вот это: https://pastebin.com/FDQPsau4

@Toskan Спасибо, что поделился. Но стоит отметить, что javascript, который я использую здесь, копирует/основан на оригинальном javascript из WordPress Core с проверками, такими как тип и другие. Без должных проверок, хотя решение может быть проще и работать, но в некоторых случаях может привести к ошибкам.

Я не переписал весь js В шаге 6 я добавил только
(function($){
lesson_orders={};
if($('.post_type_page').length && $('.post_type_page').val()=='resource'){
$('#the-list tr').each(function(){
id=$(this).find('.check-column input').val();
val=$(this).find('.lesson_order').text();
lesson_orders['edit-'+id]=val;
});
}
$(document).on('focus','.ptitle',function(){
id =$(this).closest('.inline-edit-row').attr('id');
$('#'+id+' input[name="lesson_order"]').val(lesson_orders[''+id]);
});
})(jQuery);

Вот что я сделал в 2024 году, чтобы добавить метаполе summary
к типам записей post
и episode
в виде колонок, редактируемых через быстрое редактирование.
Добавление колонки в админке
Сначала определяем колонки с помощью фильтра manage_posts_columns.
/**
* Добавляем колонку Summary для записей и эпизодов
*
* @param array $columns Ассоциативный массив заголовков колонок.
* @param string $post_type Слаг типа записи.
*
* @return array Возможно измененный массив колонок.
*/
function prefix_add_summary_column($columns, $post_type)
{
if (in_array($post_type, array('post','episode')) {
return array_merge($columns, array(
'summary' => __('Краткое описание')
));
}
return $columns;
}
add_filter('manage_posts_columns', 'prefix_add_summary_column', 10, 2);
Альтернативно можно использовать add_filter('manage_post_posts_columns', 'prefix_add_summary_column');
и add_filter('manage_episode_posts_columns', 'prefix_add_summary_column');
для целевых типов записей и убрать проверку типа записи внутри функции.
Далее определяем содержимое колонки summary
с помощью действия manage_posts_custom_column. В этом примере просто выводим значение метаполя summary
.
/**
* Отображаем содержимое колонки Summary
*
* @param string $column_name Название колонки для отображения.
* @param int $post_id ID текущей записи.
*
* @return void
*/
function prefix_display_summary_column($column_name, $post_id)
{
if ($column_name === 'summary') {
echo esc_html(get_post_meta($post_id,'summary',true));
}
}
add_action('manage_posts_custom_column', 'prefix_display_summary_column', 10, 2);
Опять же, можно использовать add_action('manage_post_posts_custom_column', 'prefix_display_summary_column', 10, 2);
и add_action('manage_episode_posts_custom_column', 'prefix_display_summary_column', 10, 2);
для конкретных типов записей.
Добавление поля быстрого редактирования
Теперь достаточно для отображения колонки summary
и её данных. Далее добавляем функциональность быстрого редактирования. Сначала добавляем скрытые inline-данные для хранения на фронтенде с помощью действия add_inline_data:
/**
* Добавляем Summary в данные inline-редактора
*
* @param WP_Post $post Объект текущей записи.
*
* @return void
*/
function prefix_add_summary_inline($post)
{
if (in_array($post->post_type, array('post','episode')) {
printf(
'<div class="summary">%s</div>',
esc_html(get_post_meta($post->ID,'summary',true))
);
}
}
add_action('add_inline_data', 'prefix_add_summary_inline');
На момент написания нет действия для конкретного типа записи, поэтому нужна проверка. Используем esc_html() или другую подходящую функцию экранирования.
Теперь отображаем текстовое поле в окне быстрого редактирования с помощью действия quick_edit_custom_box:
/**
* Отображаем поле ввода Summary в редакторе
*
* @param string $column_name Название колонки для редактирования.
* @param string $post_type Слаг типа записи
*
* @return void
*/
function prefix_display_summary_field($column_name, $post_type)
{
if ($column_name == 'summary' && in_array($post_type, array('post', 'episode'))) {
printf(
'<fieldset class="inline-edit-col-right">
<div class="inline-edit-col">
<div class="inline-edit-group wp-clearfix">
<label class="inline-edit-au-websites alignleft">
<span class="title">%s</span>
<span class="input-text-wrap">
<input type="text" name="%s" autocomplete="off">
</span>
</label>
</div>
</div>
</fieldset>',
esc_html__('Краткое описание'),
esc_attr($column_name)
);
}
}
add_action('quick_edit_custom_box', 'prefix_display_summary_field', 10, 2);
Здесь мы проверяем нашу колонку и тип записи, выводя HTML с разметкой, аналогичной другим полям быстрого редактирования.
И последний шаг - предзаполнение поля ввода данными! Вместо клонирования скрипта редактора, мы просто слушаем триггер focus, который он генерирует при открытии, затем берем значение из скрытого поля и устанавливаем его (только если есть данные и поле пустое).
Для этого подключаемся к действию admin_enqueue_scripts и используем функцию wp_add_inline_script, добавляя наш JS к скрипту inline-редактора (inline-edit-post
), который уже загружен на странице. Таким образом наш скрипт будет загружаться после inline-edit-post.min.js
, но только для типов записей post
и episode
.
/**
* Предзаполняем поле Summary значением записи
*
* @param string $hook Суффикс хука текущей админ-страницы
*
* @return void
*/
function prefix_prefill_summary_field($hook) {
if ($hook == 'edit.php' && in_array(get_post_type(), array('post', 'episode'))) {
wp_add_inline_script(
'inline-edit-post', // Хэндл
'(function($) {
$(".ptitle").on("focus",function(e){
let id = parseInt($(e.target).closest(".quick-edit-row").attr("id").replace("edit-","")),
value = $("#inline_"+id+" .summary").text(),
$input = $("#edit-"+id+".quick-edit-row input[name=summary]");
if(value && !$input.val()) {
$input.val(value);
}
});
})(jQuery);'
);
}
}
add_action('admin_enqueue_scripts', 'prefix_prefill_summary_field');
Всё работает, потому что это расширение нативной функциональности WordPress по сохранению метаданных и данных записей. Поэтому не нужно писать кастомные обработчики для save_post
.

Что обеспечивает сохранение данных? Как WordPress узнаёт имя мета-поля, в которое нужно сохранить?

@bence-szalai WordPress узнаёт имя мета-ключа через атрибут name
элемента управления формы в режиме быстрого редактирования записи. В приведённом примере это значение $column_name
в функции prefix_display_summary_field()
, например summary
. Лично я рекомендую добавлять префиксы ко всем вашим мета-полям, чтобы избежать случайных перезаписей. WordPress понимает, что нужно сохранить значение, потому что оно будет присутствовать в массиве $_POST
при отправке формы, который WP динамически считывает и сохраняет в метаданных записи.
