Как добавить произвольное поле в быстрое редактирование

27 апр. 2020 г., 03:06:45
Просмотры: 16.9K
Голосов: 4

Как я могу добавить новую колонку в быстрое редактирование и поместить в неё произвольное поле? Я прочитал этот вопрос, но одна из ссылок ведёт на ошибку 404. Мне нужна новая колонка для моего произвольного мета-ключа "summary" и типов записей "post" и "episode".

0
Все ответы на вопрос 3
12
14

Вот несколько шагов для создания пользовательского блока быстрого редактирования и пользовательской колонки:

  1. Создать пользовательский мета-ключ (предполагается, что у вас уже есть один)
  2. Добавить заголовок и данные пользовательской колонки в админке (предполагается, что вы хотите отображать пользовательский мета-ключ в колонке, если нет, вы можете немного изменить логику для достижения того же эффекта, так как принцип остается тем же)
  3. Добавить блок быстрого редактирования
  4. Добавить логику сохранения
  5. Загрузить скрипт для модификации оригинальной функции inline-edit-post, чтобы поддерживать пользовательские мета-значения
  6. Подготовить файл скрипта

В этом примере мы добавим поле быстрого редактирования для типа записи Страница. Следующий код проверен на работоспособность при размещении в functions.php.

1. Создать пользовательский мета-ключ (предполагается, что у вас уже есть один)

Я подготовил пользовательский мета-ключ remark для типа записи page

2. Добавить заголовок и данные пользовательской колонки в админке

Для пользовательских типов записей можно использовать:

Добавить заголовок колонки

Добавить содержимое колонки

  • Общий случай (обычные записи или пользовательские типы записей) 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.

27 апр. 2020 г. 10:10:30
Комментарии

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

Gusto Gusto
5 мая 2020 г. 01:12:17

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

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
10 мая 2020 г. 13:06:50

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

Gusto Gusto
12 мая 2020 г. 23:47:14

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

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
13 мая 2020 г. 02:15:54

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

но после нажатия "обновить", параметр summary остается пустым.

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

Gusto Gusto
14 мая 2020 г. 00:17:21

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

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
15 мая 2020 г. 16:48:54

Я проверил это с обоими типами записей. Работает. Спасибо.

Gusto Gusto
15 мая 2020 г. 18:22:17

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

Toskan Toskan
8 янв. 2021 г. 00:34:46

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

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
9 янв. 2021 г. 11:45:07

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

Toskan Toskan
11 янв. 2021 г. 21:39:15

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

Toskan Toskan
12 янв. 2021 г. 04:11:09

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

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
12 янв. 2021 г. 06:50:14
Показать остальные 7 комментариев
0

Я не переписал весь 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);
28 янв. 2021 г. 21:15:48
2

Вот что я сделал в 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.

17 янв. 2024 г. 01:20:19
Комментарии

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

Bence Szalai Bence Szalai
14 окт. 2024 г. 16:02:03

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

AuRise AuRise
17 окт. 2024 г. 17:39:17