Cómo añadir un campo personalizado a la edición rápida

27 abr 2020, 03:06:45
Vistas: 16.9K
Votos: 4

¿Cómo puedo añadir una nueva columna a la edición rápida e incluir un campo personalizado en ella? He leído esta pregunta pero uno de los enlaces lleva a un error 404. Necesito una nueva columna para mi metaclave personalizada "summary" y para los tipos de contenido "post" y "episode".

0
Todas las respuestas a la pregunta 3
12
14

Hay algunos pasos para crear la caja de edición rápida personalizada y la columna personalizada

  1. crear una meta clave personalizada (se asume que ya tienes una)
  2. agregar título y datos de columna personalizados en el administrador (se asume que quieres mostrar la meta clave personalizada en la columna, si no, también puedes modificar un poco la lógica para lograr el mismo efecto porque el principio es el mismo)
  3. agregar caja de edición rápida personalizada
  4. agregar lógica de guardado
  5. cargar script para modificar la función original inline-edit-post para soportar valores meta personalizados
  6. preparar el archivo de script

Este ejemplo va a agregar una caja de entrada de edición rápida en el tipo de post Page. El siguiente código está probado y funciona al colocarlo en functions.php.

1. crear una meta clave personalizada (se asume que ya tienes una)

He preparado una meta clave personalizada remark para el tipo de post page

2. agregar título y datos de columna personalizados en el administrador

para tipos de post personalizados, puedes usar

Agregar título de columna

Agregar contenido de columna

El siguiente es un ejemplo para el tipo de post Page así como tipo de post personalizado + Post por defecto como ejemplo para configuración de múltiples tipos de post.

// agregar título de columna personalizada para valor meta personalizado
add_filter('manage_edit-page_columns', 'ws365150_add_custom_columns_title' );
function ws365150_add_custom_columns_title( $columns ) {
    $columns['page_remark'] = 'Nota'; // puedes usar __() después para soporte de traducción
    
    return $columns;
}
    
// agregar datos de columna personalizada con valor meta personalizado
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;
    }
}

El siguiente es un ejemplo para tipo de post personalizado episode + Post por defecto con meta clave summary

// agregar título de columna personalizada para valor meta personalizado
// 'manage_pages_columns' o 'manage_edit-post_columns' ambos funcionan
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'] = 'Resumen'; // puedes usar __() después para soporte de traducción
            break;
        
        default:
            
            break;
    }
    
    return $columns;
}
    
// agregar datos de columna personalizada con valor meta personalizado para tipos de post personalizados
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': // especificado para esta columna asignada en el título de columna
            echo get_post_meta( $post_id, 'summary', true );
            break;
        
        default:
            break;
    }
}

3. agregar caja de edición rápida personalizada

// para page
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' ): // mismo título de columna definido en paso anterior
        ?>
                <?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">Nota</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 'campo de página personalizado';
            break;
        
        default:
            break;
    }
}

// para Post + tipo de post personalizado
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' ): // mismo título de columna definido en paso anterior
        ?>
                <?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">Resumen</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 'campo de página personalizado';
            break;
        
        default:
            break;
    }
}

4. agregar lógica de guardado

add_action( 'save_post', 'ws365150_update_custom_quickedit_box' );
function ws365150_update_custom_quickedit_box() {
    // cualquier lógica de verificación aquí, omitir y mantener simple para propósito de ilustración simple (nonce, existencia de $_POST['remark'], guardado ajax y así
    
    // nota en Page
    if( isset( $_POST ) && isset( $_POST['remark'] ) ) { // donde remark está definido en <input name="remark">
        update_post_meta($_POST['post_ID'], 'remark', $_POST['remark']);
    }

    // resumen en Post, tipo de post personalizado
    if( isset( $_POST ) && isset( $_POST['summary'] ) ) { // donde summary está definido en <input name="summary">
        update_post_meta($_POST['post_ID'], 'summary', $_POST['summary']);
    }
    
    // para propósito de depuración en inspector, no es necesario, habilitar esto romperá el guardado pero permitirá ver el retorno ajax
    // wp_send_json_success( array(
    //  'message' => '¡Prueba de guardado!',
    //  'post_data' => $_POST,
    // ) );
    return; // finalizar la llamada de función
}

5. cargar script para modificar la función original inline-edit-post

add_action( 'admin_enqueue_scripts', function( $page ) {

    // agregar lógica de verificación de página, esta es simple, puedes probar tipo de post y así...
    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. preparar el archivo de script (ws365150_custom_quickedit_box.js, al probar el código anterior, colocar en la carpeta del tema)

( function( $, wp ) {
    // clonar de la función original en inline-post-edit.js para sobrescribir
    // en realidad no es necesario crear un objeto alias, sin embargo, crear un alias podría ser una nota y marca de sobrescritura sin olvidar para mantenimiento posterior
    window.customInlineEditPost = window.inlineEditPost;

    // sobrescribir función: agregar valor meta personalizado, la base está copiada del fuente
    customInlineEditPost.edit = function(id) {
            // console.log( 'edición personalizada' );
            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');
            }

            // Agregar la nueva fila de edición con una fila en blanco extra debajo para mantener el rayado cebra.
            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>');

            // Poblar campos en la ventana de edición rápida.
            rowData = $('#inline_'+id);
            if ( !$(':input[name="post_author"] option[value="' + $('.post_author', rowData).text() + '"]', editRow).val() ) {

                // El autor del post ya no tiene capacidades de edición, así que necesitamos agregarlos a la lista de autores.
                $(':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();
            }

            // poblar valor meta personalizado
            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);

                /**
                 * Reemplaza la imagen para un Twemoji(emoji de Twitter) con su texto alternativo.
                 *
                 * @returns Texto alternativo de la imagen.
                 */
                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 );
            }

            /**
             * Crea las cajas de selección para las categorías.
             */
            $('.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(','));
                }
            });

            /**
             * Obtiene todas las taxonomías para sugerencias de auto-completado en vivo al escribir el nombre
             * de una etiqueta.
             */
            $('.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();
            });

            // Manejar el estado del post.
            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 );
            }

            // Remover la página actual e hijos del menú desplegable de padre.
            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 );

Lo anterior es para edición rápida usando el hook quick_edit_custom_box, y también podrías necesitar manejar la edición masiva. Puedes explorar más leyendo y usando bulk_edit_custom_box.

27 abr 2020 10:10:30
Comentarios

¡Genial! ¿Podrías editar el ejemplo para los tipos de post post y episode y la meta clave personalizada summary? Lo siento, no me gusta jugar con el archivo functions.php, podría accidentalmente dañar WordPress. Necesito saber que hay un 100% de probabilidad de que el código funcione.

Gusto Gusto
5 may 2020 01:12:17

He agregado código de ejemplo adicional para post, episode con el campo meta summary con notas adicionales sobre el título de la columna y un ejemplo de verificación al guardar. Puedes modificarlo y agregar verificaciones adicionales según el contenido que necesites.

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
10 may 2020 13:06:50

Espera un segundo, este código no funciona y solo está dejando mi página de edición "Todas las páginas" en blanco.

Gusto Gusto
12 may 2020 23:47:14

El código ha sido actualizado, por favor cambia add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt' ); por add_filter('manage_posts_columns', 'ws365150_add_custom_columns_title_pt', 10, 2 ); Por favor, no te preocupes por marcar la respuesta como correcta antes de probarla.

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
13 may 2020 02:15:54

Hmm, al probar el código anterior, el nuevo campo de resumen se añade al menú de edición rápida de los tipos de contenido post y episode,

pero después de hacer clic en "actualizar", el parámetro summary aparece vacío.

Incluso si el parámetro summary ya tenía un valor (sin usar la edición rápida), el campo de edición rápida sigue dejando el parámetro vacío.

Gusto Gusto
14 may 2020 00:17:21

He creado un caso de prueba para simular tu situación y he realizado una prueba. He actualizado el javascript y el filtro save_post. Por favor, asegúrate también de que el javascript se carga correctamente. Porque la ruta asume que el javascript está en la carpeta del tema. Debería funcionar correctamente ahora según la simulación.

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
15 may 2020 16:48:54

Lo probé con ambos tipos de entradas. Funciona. Gracias.

Gusto Gusto
15 may 2020 18:22:17

Buena respuesta, aunque... en general es horrible que tenga que ser tan complicado

Toskan Toskan
8 ene 2021 00:34:46

@Toskan, podría simplificarse. Es solo que esta respuesta está especificada para el que hizo la pregunta con la meta key de las preguntas. Personalmente prefiero respuestas genéricas en la mayoría de los casos ya que diferentes personas tienen diferentes necesidades. Si eliminamos los ejemplos, hay pocos pasos. En resumen, hay 3 partes: (1)crear una meta key personalizada (puede hacerse manualmente o usando plugins), (2)añadir columnas personalizadas para mostrar y (3)crear la lógica de edición/guardado para la edición rápida. Como la edición rápida se basa en Javascript ajax para proporcionar una UX "instantánea", esta parte parece un poco exagerada.

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
9 ene 2021 11:45:07

ah sí, no, no usé tu parte de javascript. Hay una solución mucho más simple para la parte js en algún lugar...

Toskan Toskan
11 ene 2021 21:39:15

mira este: https://pastebin.com/FDQPsau4

Toskan Toskan
12 ene 2021 04:11:09

@Toskan Gracias por compartir. Pero un punto a tener en cuenta, el javascript que uso aquí está copiado/basado en el javascript original de WordPress Core con verificaciones como tipo y demás. Sin las verificaciones adecuadas, aunque podría ser más simple y funcionar, podría resultar en errores en algunos casos.

西門 正 Code Guy - JingCodeGuy 西門 正 Code Guy - JingCodeGuy
12 ene 2021 06:50:14
Mostrar los 7 comentarios restantes
0

no reescribí todo el js en el paso 6 solo agregué

(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 ene 2021 21:15:48
2

Esto es lo que he hecho en 2024 para agregar el campo meta summary a los tipos de entrada post y episode como columnas y editable mediante edición rápida.

Agregar una columna en el administrador

Primero, define las columnas con el filtro manage_posts_columns.

/**
 * Agregar Columna de Resumen a Posts y Episodios
 *
 * @param array $columns Un arreglo asociativo de encabezados de columnas.
 * @param string $post_type El slug del tipo de entrada.
 *
 * @return array Un arreglo posiblemente modificado de columnas.
 */
function prefix_add_summary_column($columns, $post_type)
{
    if (in_array($post_type, array('post','episode')) {
        return array_merge($columns, array(
            'summary' => __('Resumen')
        ));
    }
    return $columns;
}
add_filter('manage_posts_columns', 'prefix_add_summary_column', 10, 2);

Alternativamente, puedes usar add_filter('manage_post_posts_columns', 'prefix_add_summary_column'); y add_filter('manage_episode_posts_columns', 'prefix_add_summary_column'); para dirigirte a tipos de entrada específicos y eliminar la declaración if que verifica el tipo de entrada dentro de la función.

Luego, define lo que se muestra en el contenido para tu columna summary usando la acción manage_posts_custom_column. Para este ejemplo, simplemente muestra el valor del campo meta summary.

/**
 * Mostrar Contenido de la Columna Resumen en Posts y Episodios
 *
 * @param string $column_name El nombre de la columna a mostrar.
 * @param int $post_id El ID de la entrada actual que se está mostrando.
 *
 * @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);

Nuevamente, puedes usar add_action('manage_post_posts_custom_column', 'prefix_display_summary_column', 10, 2); y add_action('manage_episode_posts_custom_column', 'prefix_display_summary_column', 10, 2); para aplicar la acción a tipos de entrada específicos. La razón por la que no hay una declaración if verificando los tipos de entrada es porque esta columna no estará presente en otros tipos de entrada debido al paso anterior.

Agregar campo de edición rápida

Con eso es suficiente para renderizar la columna personalizada summary y sus datos. Lo siguiente es agregar la funcionalidad para la edición rápida. El primer paso aquí es agregarlo a los datos inline ocultos para almacenarlo en el frontend y recuperarlo más tarde usando la acción add_inline_data así:

/**
 * Agregar Resumen a los Datos del Editor Inline
 *
 * @param WP_Post $post El objeto de entrada actual.
 *
 * @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');

Al momento de escribir esto, no hay una acción específica para tipos de entrada, por lo que se necesita la declaración if que lo verifica. Estoy usando esc_html() aquí, pero usa la función de escape que sea apropiada para tus datos.

Ahora para mostrar el campo de texto en el cuadro de edición rápida usando la acción quick_edit_custom_box así:

/**
 * Mostrar Campo de Entrada del Editor de Resumen
 *
 * @param string $column_name Nombre de la columna a editar.
 * @param string $post_type El slug del tipo de entrada
 *
 * @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__('Resumen'),
            esc_attr($column_name)
        );
    }
}
add_action('quick_edit_custom_box', 'prefix_display_summary_field', 10, 2);

Aquí estamos verificando el nombre de nuestra columna personalizada y el tipo de entrada, y mostrando el HTML que usa un marcado similar a los otros campos de edición rápida. Siéntete libre de minimizar el HTML.

Y finalmente, la última pieza es precargar el campo de entrada con los datos ocultos. En lugar de manipular el script del editor, aquí simplemente escuchamos el evento focus que se dispara cuando se abre, luego tomamos nuestro valor del campo oculto y lo establecemos (¡pero solo si nuestro valor oculto tiene datos y el valor actual del campo del editor está vacío!).

Para esto, me engancho a la acción admin_enqueue_scripts y utilizo la función wp_add_inline_script y adjunto nuestro JS personalizado al script del editor inline (inline-edit-post) que ya está cargado en la página. De esta manera, nuestro script siempre aparecerá después de que se cargue inline-edit-post.min.js, pero solo en nuestros tipos de entrada especificados post y episode.

/**
 * Precargar Campo de Resumen con Valor de la Entrada
 *
 * @param string $hook Sufijo del hook para la página de administración actual
 *
 * @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', // Handle
            '(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');

Todo esto funciona porque extiende lo que WordPress hace de forma nativa, cómo guarda los datos meta y de entrada. Por eso no tengo que escribir nada personalizado para una acción save_post.

17 ene 2024 01:20:19
Comentarios

¿Qué hace que el guardado funcione? ¿Cómo sabe WP cuál es el nombre del campo meta para guardar?

Bence Szalai Bence Szalai
14 oct 2024 16:02:03

@bence-szalai WordPress conoce el nombre de la clave meta a través del atributo name del elemento de control del formulario en el formulario de edición rápida de la entrada. En el ejemplo anterior, es el valor de $column_name en la función prefix_display_summary_field(), por ejemplo summary. Personalmente, recomendaría prefijar todos tus campos meta para evitar sobrescrituras accidentales. Sabe que debe guardarlo porque existirá en el array $_POST al enviar el formulario, el cual WP leerá dinámicamente y guardará en los meta de la entrada.

AuRise AuRise
17 oct 2024 17:39:17