<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Juanan Ruiz</title>
    <description>The latest articles on Forem by Juanan Ruiz (@juananruiz).</description>
    <link>https://forem.com/juananruiz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F83992%2Fca65af6d-cc39-4339-a6dc-02cd7c5444f0.jpeg</url>
      <title>Forem: Juanan Ruiz</title>
      <link>https://forem.com/juananruiz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/juananruiz"/>
    <language>en</language>
    <item>
      <title>Usar taxonomías en el campo select de un formulario</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Sun, 26 Jan 2020 00:40:21 +0000</pubDate>
      <link>https://forem.com/juananruiz/usar-taxonomias-en-el-campo-select-de-un-formulario-2hjj</link>
      <guid>https://forem.com/juananruiz/usar-taxonomias-en-el-campo-select-de-un-formulario-2hjj</guid>
      <description>&lt;p&gt;La entrada &lt;a href="https://kungfupress.com/usar-taxonomias-desde-el-campo-select-de-un-formulario/" rel="noopener noreferrer"&gt;Usar taxonomías en el campo select de un formulario&lt;/a&gt; se publicó primero en &lt;a href="https://kungfupress.com" rel="noopener noreferrer"&gt;🈴KungFuPress&lt;/a&gt; por &lt;a href="https://kungfupress.com/author/kungfupress/" rel="noopener noreferrer"&gt;Kung Fu Press&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando desarrolles un formulario en WordPress te vas a encontrar casi siempre con la necesidad de mostrar al usuario una lista de opciones para seleccionar una de ellas, el típico campo select. A veces, por acabar antes, pones las opciones grabadas a fuego en tu código, pero cuando más adelante necesites introducir o quitar opciones, tendrás que editar el código. La cosa se complica si cuando surja la necesidad no estás tú ahí para hacerlo.&lt;/p&gt;

&lt;p&gt;Por ello una buena práctica es sacar siempre estas opciones del código y llevarlas a la base de datos. Podrías crear una tabla para ello, pero luego tendrías que crear un listado y un formulario para revisar, agregar o modificar elementos.&lt;/p&gt;

&lt;p&gt;Si quieres ahorrarte este trabajo te recomiendo usar las taxonomías de WordPress. En este tutorial aprenderás a usar taxonomías en el campo select de un formulario y a guardarla con el resto de datos. Cada envío del formulario lo almacenarás en un Custom Post Type (CPT).&lt;/p&gt;

&lt;h2&gt;
  
  
  Creando la base del plugin y el CPT
&lt;/h2&gt;

&lt;p&gt;Lo primero que vas a hacer es crear la estructura básica del plugin y agregar un CPT de la forma más breve posible.&lt;/p&gt;

&lt;p&gt;Crea una carpeta con el nombre del plugin y un fichero PHP dentro con el mismo nombre de la carpeta. Contendrá un par de constantes con información del plugin y la llamada al fichero &lt;strong&gt;plugin-init.php&lt;/strong&gt; donde luego definirás el tipo de entrada personalizada (CPT) y la taxonomía personalizada.&lt;/p&gt;

&lt;p&gt;Como siempre, tienes el &lt;a href="https://github.com/kungfupress/kfp-formtaxon/tree/v0.1.0" rel="noopener noreferrer"&gt;código completo del plugin en GitHub&lt;/a&gt;, pero recuerda que si estás siguiendo este tutorial con fines de aprendizaje te recomiendo teclear todo el código de tu puño y letra.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/** 
 * Plugin Name: KFP FormTaxon 
 * Plugin URI: https://github.com/kungfupress/kfp-formtaxon
 * Description: Ejemplo de utilización de una categoría personalizada desde un formulario 
 * Version: 0.1.0 
 * Author: Juanan Ruiz 
 * Author URI: https://kungfupress.com/ 
 * PHP Version: 5.6 
 *  
 * @package kfp_ftx 
 */

defined( 'ABSPATH' ) || die();
// Constantes que afectan a todos los ficheros del plugin.
define( 'KFP_FTX_DIR', plugin_dir_path( __FILE__ ) );
define( 'KFP_FTX_URL', plugin_dir_url( __FILE__ ) );
define( 'KFP_FTX_VERSION', '0.1.0' );
// Crea CPT y taxonomia.
require_once KFP_FTX_DIR . 'include/plugin-init.php';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creación del CPT
&lt;/h2&gt;

&lt;p&gt;Crea una carpeta &lt;strong&gt;include&lt;/strong&gt; y dentro de ella el archivo &lt;strong&gt;plugin-init.php&lt;/strong&gt;. En este archivo vas a crear el CPT y la taxonomía.&lt;/p&gt;

&lt;p&gt;Vas a utilizar el &lt;strong&gt;hook&lt;/strong&gt; &lt;code&gt;add_action('init')&lt;/code&gt; de manera que el CPT esté definido antes de ejecutar otras acciones del plugin. Para definir dicho CPT usarás la función &lt;code&gt;register_post_type()&lt;/code&gt; que recibe como parámetros el slug o nombre interno del CPT y un array de argumentos donde se pueden personalizar muchos aspectos del mismo, aunque en este ejemplo será lo mínimo para ir al grano. Tienes otro artículo más extenso sobre &lt;a href="https://kungfupress.com/como-crear-tipos-y-campos-personalizados-en-wordpress/" rel="noopener noreferrer"&gt;cómo crear tipos y campos personalizados&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
** 
 * File: kfp-formtaxon/include plugin-init.php 
 * @package kfp_formtaxon 
 *

defined( 'ABSPATH' ) || die();

add_action( 'init', 'kfp_cpt_taller', 10 );
/** 
 * Crea el CPT Taller con lo mínimo que se despacha en CPTs
 * @return void 
 */
function kfp_cpt_taller() {
    $args = array('public' =&amp;gt; true,'label' =&amp;gt; 'Taller',);
    register_post_type( 'kfp-taller', $args );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cómo crear una taxonomía personalizada
&lt;/h2&gt;

&lt;p&gt;Agrega el código para definir la taxonomía y algunos ejemplos predeterminados de esta. Para ello vas a usar de nuevo el &lt;strong&gt;hook&lt;/strong&gt; &lt;code&gt;add_action('init')&lt;/code&gt; y la función &lt;code&gt;register_taxonomy()&lt;/code&gt; que recibe tres parámetros: el slug de la taxonomía, un array con los tipos de entrada que pueden usar esta categoría y otro array con el resto de propiedades, de nuevo al mínimo.&lt;/p&gt;

&lt;p&gt;En este ejemplo el CPT representa una actividad formativa ( &lt;strong&gt;taller&lt;/strong&gt; ) y la taxonomía el &lt;strong&gt;lugar&lt;/strong&gt; donde se imparte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kfp_taxonomy_lugares'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cd"&gt;/** 
 * Registra la taxonomía con lo mínimo indispensable 
 * @return void 
 */&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;kfp_taxonomy_lugares&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'label'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Lugar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'hierarchical'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'show_admin_column'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,);&lt;/span&gt;
    &lt;span class="nf"&gt;register_taxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'kfp-lugar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'kfp-taller'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'init'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kfp_lugares_add'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cd"&gt;/** 
 * Agrega algunos lugares de ejemplo por defecto 
 * @return void 
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;kfp_lugares_add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$lugares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Escuela de Ingenieros Informáticos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Facultad de Derecho'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Facultad de Bellas Artes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Facultad de Medicina'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Rectorado'&lt;/span&gt;&lt;span class="p"&gt;,);&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$lugares&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$lugar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;wp_insert_term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$lugar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kfp-lugar'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creación del formulario
&lt;/h2&gt;

&lt;p&gt;Ahora vas a crear el formulario que te permitirá crear un nuevo taller desde el frontend. Para mostrarlo en la parte pública o frontend de tu web, podrías crear una plantilla específica o aprovechar algún elemento de la plantilla como la cabecera, el pié o incluso un widget. Para este ejemplo vas a asociar el formulario a un shortcode, esto te permitirá incluirlo en cualquier página o entrada que desees, incluso en un widget en el que podrías colocar el shortcode.&lt;/p&gt;

&lt;p&gt;Si prefieres tener el formulario en el backend podrías crear un panel de administración allí y reutilizar el código que verás aquí con algunas adaptaciones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F12%2Fformulario_lugares.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F12%2Fformulario_lugares.gif" alt="Formulario con taxonomía en campo select"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así que crea un nuevo fichero llamado &lt;strong&gt;form_taller_shortcode.php&lt;/strong&gt; dentro de la carpeta &lt;strong&gt;include&lt;/strong&gt;. Recuerda que necesitas mostrar la lista de lugares en el formulario, así que lo primero que harás será recuperar todos los elementos de la taxonomía utilizando la función &lt;code&gt;get_terms()&lt;/code&gt; de WordPress.&lt;/p&gt;

&lt;p&gt;Luego «pintas» el formulario, que se va a procesar con el fichero &lt;strong&gt;admin-post.php&lt;/strong&gt; de WordPress, tal como se indica en el &lt;strong&gt;action&lt;/strong&gt; del formulario. Un campo oculto del formulario llamado también &lt;strong&gt;action&lt;/strong&gt; permitirá a WordPress saber que formulario es el que estás grabando por lo que debes usar un identificador único, yo he escogido &lt;strong&gt;kfp-ftx-taller&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El formulario se completa con un campo nonce de seguridad, el nombre y la descripción del nuevo taller y por supuesto la lista desplegable para seleccionar el lugar donde se va a celebrar el taller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/**
* File: kfp-formtaxon/include/form-taller-shortcode.php
*
* @package kfp_ftx
*/

defined( 'ABSPATH' ) || die();
add_shortcode( 'kfp_ftx_crear_taller', 'kfp_ftx_crear_taller' );


/**
 * Implementa formulario para crear un nuevo taller.
 *
 * @return string
 */
function kfp_ftx_crear_taller() {
    // Trae los lugares existentes en la base de datos a la vatiable $lugares.
    // Esta variable recibirá un array de objetos de tipo taxonomy.
    $lugares = get_terms('kfp-lugar',array('orderby' =&amp;gt; 'term_id','hide_empty' =&amp;gt; 0,));
    ob_start();
    ?&amp;gt;
    &amp;lt;form action="&amp;lt;?php echo esc_url( admin_url( 'admin-post.php' ) ); ?&amp;gt;" method="post"&amp;gt;
        &amp;lt;?php wp_nonce_field( 'kfp-ftx-taller', 'kfp-ftx-taller-nonce' ); ?&amp;gt;
        &amp;lt;input type="hidden" name="action" value="kfp-ftx-taller"&amp;gt;
        &amp;lt;div class="form-input"&amp;gt;
            &amp;lt;label for="nombre"&amp;gt;Taller&amp;lt;/label&amp;gt;
            &amp;lt;input type="text" name="nombre" id="nombre" required&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="form-input"&amp;gt;
            &amp;lt;label for="id_lugar"&amp;gt;Lugar&amp;lt;/label&amp;gt;
            &amp;lt;select name="id_lugar" required&amp;gt;&amp;lt;option value=""&amp;gt;Selecciona el lugar&amp;lt;/option&amp;gt;
            &amp;lt;?php 
                foreach ( $lugares as $lugar ) {
                    echo('&amp;lt;option value="' . esc_attr( $lugar-&amp;gt;term_id ) . '"&amp;gt;'. esc_attr( $lugar-&amp;gt;name ) . '&amp;lt;/option&amp;gt;');
                }
            ?&amp;gt;
            &amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="form-input"&amp;gt;
            &amp;lt;label for="descripcion"&amp;gt;Descripción&amp;lt;/label&amp;gt;
            &amp;lt;textarea name="descripcion" id="descripcion"&amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="form-input"&amp;gt;
            &amp;lt;input type="submit" value="Enviar"&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;?php
    return ob_get_clean();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crea en tu web una página o entrada donde colocarás el shortcode &lt;code&gt;[kfp_ftx_crear_taller]&lt;/code&gt;. Comprueba que el formulario aparece dentro de la entrada y que el campo &lt;strong&gt;Lugar&lt;/strong&gt; contiene los elementos de ejemplo que pusiste al definir la taxonomía.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grabar el formulario y la taxonomía en la base de datos
&lt;/h2&gt;

&lt;p&gt;Para grabar los datos del formulario usarás un hook dinámico (parte del nombre lo inventas tú) que será procesado por el fichero &lt;strong&gt;wp-admin/admin-post.php&lt;/strong&gt;. De momento el script procesará tanto el envío de usuarios autenticados como anónimos, por lo que utilizarás dos hooks: &lt;strong&gt;admin_post_{$action}&lt;/strong&gt; y &lt;strong&gt;admin_post_nopriv_{$action}&lt;/strong&gt;. Recuerda que el valor de &lt;strong&gt;{$action}&lt;/strong&gt; lo has definido en un campo oculto del formulario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F12%2Fexplica-llamada-form-1024x966.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F12%2Fexplica-llamada-form-1024x966.png" alt="Esquema de grabación de los datos de un formulario utilizando admin-post.php"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crea un nuevo fichero include/form-talller-grabar.php Agrega en él los hooks que acabo de explicar.&lt;/p&gt;

&lt;p&gt;Define la función &lt;code&gt;kfp_ftx_graba_taller()&lt;/code&gt; donde una vez comprobados que los campos requeridos vienen rellenos y que el nonce es correcto podrás grabar el CPT de tipo &lt;strong&gt;taller&lt;/strong&gt; utilizando la función &lt;strong&gt;wp_insert_post()&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/**
 * File: kfp-formtaxon/include/form-taller-grabar.php
 *
 * @package kfp_ftx
 */
defined( 'ABSPATH' ) || die();
// Agrega los action hooks para grabar el formulario (el primero para usuarios
// logeados y el otro para el resto)
// Lo que viene tras admin_post_ y admin_post_nopriv_ tiene que coincidir con
// el value del campo input con name "action" del formulario enviado.
add_action( 'admin_post_kfp-ftx-taller', 'kfp_ftx_graba_taller' );
add_action( 'admin_post_nopriv_kfp-ftx-taller', 'kfp_ftx_graba_taller' );
/**
 * Graba los datos enviados por el formulario como un nuevo CPT kfp-taller
 *
 * @return void
 */
function kfp_ftx_graba_taller() {
    // Comprueba campos requeridos y nonce.
    if ( isset( $_POST['nombre'] )
    &amp;amp;amp;&amp;amp;amp; isset( $_POST['id_lugar'] )
    &amp;amp;amp;&amp;amp;amp; isset( $_POST['kfp-ftx-taller-nonce'] )
    &amp;amp;amp;&amp;amp;amp; wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['kfp-ftx-taller-nonce'] ) ), 'kfp-ftx-taller' )
    ) {
        $nombre      = sanitize_text_field( wp_unslash( $_POST['nombre'] ) );
        $descripcion = sanitize_text_field( wp_unslash( $_POST['descripcion'] ) );
        $id_lugar    = (int) $_POST['id_lugar'];
        $args = array(
            'post_title' =&amp;gt; $nombre,
            'post_content' =&amp;gt; $descripcion,
            'post_type' =&amp;gt; 'kfp-taller',
            'post_status' =&amp;gt; 'draft',
            'comment_status' =&amp;gt; 'closed',
            'ping_status' =&amp;gt; 'closed'
        );
        // Esta variable $post_id contiene el ID del nuevo registro
        // Viene de perlas para grabar los metadatos
        $post_id = wp_insert_post($args);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comprueba que todo funciona y el nuevo CPT se graba. Al enviar el formulario obtendrás una pantalla en blanco, pero puedes comprobar que se ha creado un nuevo taller desde el escritorio. Luego arreglarás lo de la pantalla en blanca, primero tienes que asociar la taxonomía lugar con el taller .&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo grabar una taxonomía en un post
&lt;/h2&gt;

&lt;p&gt;En el paso anterior, al grabar el nuevo post de tipo taller, capturabas su identificador (ID) en la variable &lt;code&gt;$post_id&lt;/code&gt;, que era lo que te devolvía la función &lt;code&gt;wp_insert_post()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ahora vas a usar la función &lt;code&gt;wp_set_object_terms()&lt;/code&gt;que recibe el ID del post como primer parámetro. Luego necesitas el ID de la taxonomía que has asignado desde el formulario (lo has capturado en la variable &lt;code&gt;$id_lugar&lt;/code&gt;) y por último el tipo de taxonomía, en este caso &lt;code&gt;'kfp-lugar'&lt;/code&gt; Quedando el final del código así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_insert_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$term_taxonomy_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_set_object_terms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id_lugar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kfp-lugar'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sólo tendrías, por tanto, que añadir la última línea, volver a grabar un nuevo taller desde el formulario y comprobar desde el escritorio que la taxonomía se ha grabado con el taller.&lt;/p&gt;

&lt;p&gt;Para terminar tendrás que salir de esa pantalla en blanco que aparece tras enviar el formulario. No es un error, es normal, precisamente estás usando admin-post.php porque es mucho más rápido que cargar el sitio completo, el tema y los contenidos actuales. Con admin-post.php procesas el formulario con un WordPress que carga lo mínimo y luego rediriges a donde quieras. En este caso al mismo formulario con un mensaje de éxito o error, pero podría enviar a una página con un listado con todos los talleres propuestos hasta el momento o de todos los talleres propuestos por este usuario.&lt;/p&gt;

&lt;p&gt;Aquí tienes el código completo, incluyendo redirecciones y mensajes, del fichero &lt;strong&gt;include/form-taller-grabar.php&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/**
 * File: kfp-formtaxon/include/form-taller-grabar.php
 *
 * @package kfp_ftx
 */

defined( 'ABSPATH' ) || die();
// Agrega los action hooks para grabar el formulario (el primero para usuarios
// logeados y el otro para el resto)
// Lo que viene tras admin_post_ y admin_post_nopriv_ tiene que coincidir con
// el valor del campo input con nombre "action" del formulario enviado.
add_action( 'admin_post_kfp-ftx-taller', 'kfp_ftx_graba_taller' );
add_action( 'admin_post_nopriv_kfp-ftx-taller', 'kfp_ftx_graba_taller' );

/**
 * Graba los datos enviados por el formulario como un nuevo CPT kfp-taller
 *
 * @return void
 */
function kfp_ftx_graba_taller() {
    // Si viene en $_POST aprovecha uno de los campos que crea wp_nonce para volver al form.
    $url_origen = home_url( '/' );
    if ( ! empty( $_POST['_wp_http_referer'] ) ) {
        $url_origen = esc_url_raw( wp_unslash( $_POST['_wp_http_referer'] ) );
    }
    // Comprueba campos requeridos y nonce.
    if ( isset( $_POST['nombre'] )
    &amp;amp;amp;&amp;amp;amp; isset( $_POST['id_lugar'] )
    &amp;amp;amp;&amp;amp;amp; isset( $_POST['kfp-ftx-taller-nonce'] )
    &amp;amp;amp;&amp;amp;amp; wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['kfp-ftx-taller-nonce'] ) ), 'kfp-ftx-taller' )
    ) {
        $nombre      = sanitize_text_field( wp_unslash( $_POST['nombre'] ) );
        $descripcion = sanitize_text_field( wp_unslash( $_POST['descripcion'] ) );
        $id_lugar    = (int) $_POST['id_lugar'];
        $args = array(
            'post_title' =&amp;gt; $nombre,
            'post_content' =&amp;gt; $descripcion,
            'post_type' =&amp;gt; 'kfp-taller',
            'post_status' =&amp;gt; 'draft',
            'comment_status' =&amp;gt; 'closed',
            'ping_status' =&amp;gt; 'closed'
        );
        // Esta variable $post_id contiene el ID del nuevo registro
        // Viene de perlas para grabar la taxonomía
        $post_id = wp_insert_post($args);
        $term_taxonomy_ids = wp_set_object_terms( $post_id, $id_lugar, 'kfp-lugar' );
        $query_arg = array( 'kfp-ftx-resultado' =&amp;gt; 'error' );
        wp_redirect( esc_url_raw( add_query_arg( $query_arg , $url_origen ) ) );
        exit();
    }
    $query_arg = array( 'kfp-ftx-resultado' =&amp;gt; 'error' );
    wp_redirect( esc_url_raw( add_query_arg( $query_arg , $url_origen ) ) );
    exit();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verás que ahora el formulario redirige a la propia página o entrada del formulario y en la barra de direcciones de tu navegador aparece el resultado (‘success’ o ‘error’) pero tienes que hacer que aparezca dentro de la página.&lt;/p&gt;

&lt;p&gt;Para ello coloca estos dos «if» justo antes de «pintar» el formulario en el fichero &lt;strong&gt;include/form-taller-shortcode.php&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;ob_start();
    if ( filter_input( INPUT_GET, 'kfp-ftx-resultado', FILTER_SANITIZE_STRING ) === 'success' ) {
        echo '&lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Se ha grabado el taller correctamente&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;';
    }
    if ( filter_input( INPUT_GET, 'kfp-ftx-resultado', FILTER_SANITIZE_STRING ) === 'error' ) {
        echo '&lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Se ha producido un error al grabar el taller&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;';
    }
    ?&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nf"&gt;esc_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;admin_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'admin-post.php'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para terminar te comento las posibles formas de administrar tus taxonomías personalizadas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo administrar tu taxonomía personalizada
&lt;/h2&gt;

&lt;p&gt;Si estás trabajando con entradas, páginas o entradas personalizadas (CPTs) el menú de administración de la taxonomía lo encontrarás asociado al menú de administración de los tipos de entrada a los que la taxonomía está asociada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F12%2Ftaxonomia-asociada-a-cpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F12%2Ftaxonomia-asociada-a-cpt.png" alt="Panel para administrar taxonomía personalizada desde el escritorio de WordPress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En la imagen de arriba ves la taxonomía «Lugar» asociada al Custom Post Type «Taller», desde el menú de este CPT puedes acceder al listado, creación, edición y borrado de elementos de esta taxonomía.&lt;/p&gt;

&lt;p&gt;Si estás trabajando con una tabla propia (algo que siempre debe quedar como última opción) podrías asociar la taxonomía a las entradas (a los posts vamos) y desde allí podrás administrarla igualmente. Pero quizás podría quedar más elegante (y más usable) si desde el propio formulario pones un enlace o un botón al panel de edición de la taxonomía. En el caso de este ejemplo sería algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"wp-admin/edit-tags.php?taxonomy=provincias"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Editar provincias&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Realmente esta última opción también la puedes utilizar aunque estés usando un Custom Post Type, siempre que el formulario esté en el escritorio o backend (para el frontend sería otro cantar y quizás no tendría sentido que cualquier usuario pudiera editar una taxonomía).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;Utilizar taxonomías es la manera óptima de ofrecer a tus usuarios una serie de opciones cerradas a la hora de rellenar un formulario, en algunos casos incluso podrías permitir a los propios usuarios agregar nuevas opciones si fuera necesario. En cualquier caso el uso de taxonomías proporciona a los administradores del sitio las herramientas necesarias para administrarlas, herramientas que ya vienen incluidas en WordPress. Cuando implementes lo aquí aprendido en un par de ocasiones te preguntarás cómo habías podido vivir hasta ahora sin utilizar taxonomías personalizadas.&lt;/p&gt;

&lt;p&gt;Espero que te haya gustado el tutorial y le puedas sacar provecho, te recuerdo que tienes el &lt;a href="https://github.com/kungfupress/kfp-formtaxon/tree/v0.1.0" rel="noopener noreferrer"&gt;código completo del plugin en GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/files/wp-admin/admin-post.php/" rel="noopener noreferrer"&gt;https://developer.wordpress.org/reference/files/wp-admin/admin-post.php/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/wp_set_object_terms/" rel="noopener noreferrer"&gt;https://developer.wordpress.org/reference/functions/wp_set_object_terms/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Imagen de cabecera por &lt;a href="https://unsplash.com/@pirye?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Amelie &amp;amp; Niklas Ohlrogge&lt;/a&gt; en &lt;a href="https://unsplash.com/s/photos/library?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La entrada &lt;a href="https://kungfupress.com/usar-taxonomias-desde-el-campo-select-de-un-formulario/" rel="noopener noreferrer"&gt;Usar taxonomías en el campo select de un formulario&lt;/a&gt; se publicó primero en &lt;a href="https://kungfupress.com" rel="noopener noreferrer"&gt;🈴KungFuPress&lt;/a&gt; por &lt;a href="https://kungfupress.com/author/kungfupress/" rel="noopener noreferrer"&gt;Kung Fu Press&lt;/a&gt;&lt;/p&gt;

</description>
      <category>customposttype</category>
      <category>plugins</category>
      <category>taxonomías</category>
    </item>
    <item>
      <title>Diferencia entre wp_register_script y wp_enqueue_script</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Thu, 05 Sep 2019 15:54:38 +0000</pubDate>
      <link>https://forem.com/juananruiz/diferencia-entre-wpregisterscript-y-wpenqueuescript-4o4</link>
      <guid>https://forem.com/juananruiz/diferencia-entre-wpregisterscript-y-wpenqueuescript-4o4</guid>
      <description>&lt;p&gt;La entrada &lt;a href="https://kungfupress.com/diferencia-entre-wp_register_script-y-wp_enqueue_script/"&gt;Diferencia entre wp_register_script y wp_enqueue_script&lt;/a&gt; se publicó primero en &lt;a href="https://kungfupress.com"&gt;KungFuPress&lt;/a&gt; por &lt;a href="https://kungfupress.com/author/kungfupress/"&gt;Kung Fu Press&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Muchas veces me he preguntado cuándo debo usar &lt;code&gt;wp_register_script()&lt;/code&gt; antes de &lt;code&gt;wp_enqueue_script()&lt;/code&gt; y si realmente la primera sirve para algo.&lt;/p&gt;

&lt;p&gt;Si necesitas cargar un fichero javascript en todas las ocasiones en que se cargue tu página puedes ahorrarte la llamada a &lt;code&gt;wp_register_script()&lt;/code&gt; y utilizar sólo &lt;code&gt;wp_enqueue_script()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Si sólo usaras &lt;code&gt;wp_register_script()&lt;/code&gt; el script no se cargaría. Como su propio nombre indica, esta función lo único que hace es registrar el script, decir en qué orden quieres cargarlo y si tiene alguna dependencia, pero necesita &lt;code&gt;wp_enqueue_script()&lt;/code&gt; para que el script realmente se cargue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entonces ¿cuándo tengo que usar wp_register_script?
&lt;/h2&gt;

&lt;p&gt;Bien, imagina que estás usando un shortcode para mostrar un formulario, una tabla dinámica o cualquier otra cosa que necesite llamar un fichero JavaScript.&lt;/p&gt;

&lt;p&gt;Sólo deberías cargar este script si el shortcode se está utilizando en la página o entrada actual. El problema es que cuando se carga el shortcode ya es tarde para hacer la llamada al script porque la cabecera ya se ha cargado.&lt;/p&gt;

&lt;p&gt;Al final lo que hacen muchos desarrolladores es cargar los scripts junto con el plugin, pero entonces se cargan siempre y no sólo cuando se utilice el shortcode. Para solucionar este problema existe la función &lt;code&gt;wp_register_script()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Cuando uses esta función todos los parámetros se los puedes pasar directamente a ella, luego cuando llames a &lt;code&gt;wp_enqueue_scripts()&lt;/code&gt; sólo necesitas pasarle el “handler” del script.&lt;/p&gt;

&lt;p&gt;A continuación te pongo un ejemplo de como deberías cargar el script correctamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
// Cuando WP cargue la cabecera llama a este hook y tu script quedará registrado

add_action( 'wp_enqueue_scripts', 'kfp_Idea_List_scripts' );

function Kfp_Idea_List_scripts(){ 
  wp_register_script(
    'kfp-vote-link', 
    plugins_url('../js/vote-link.js', __FILE__), 
    array('jquery')
  );
}
add_shortcode('kfp_idea_list', 'Kfp_Idea_list');

function Kfp_Idea_list(){ 
  // El script sólo se carga si se utiliza el shortcode 
  wp_enqueue_script('kfp-vote-link’); 
  /* El shortcode hace lo que tenga que hacer */
} 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Si quieres ver este código en su contexto visita &lt;a href="https://github.com/kungfupress/kfp-vota-ideas"&gt;https://github.com/kungfupress/kfp-vota-ideas&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Y si tengo que usar wp_localize_script?
&lt;/h2&gt;

&lt;p&gt;Se aplica lo mismo. En los casos mas simples puedes usar &lt;code&gt;wp_enqueue_script()&lt;/code&gt; y luego &lt;code&gt;wp_localize_script()&lt;/code&gt; para pasar parámetros al script. Pero siempre en ese orden porque el «handler» o identificador del script que utilizas en &lt;code&gt;wp_localize_script()&lt;/code&gt; lo defines este caso en &lt;code&gt;wp_enqueue_script()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Si utilizas &lt;code&gt;wp_register_script()&lt;/code&gt; puedes llamar a &lt;code&gt;wp_localize_script()&lt;/code&gt; inmediatamente después y ya lo tienes preparado para cuando llames a &lt;code&gt;wp_enqueue_script()&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cuál es la diferencia entre wp_register_style y wp_enqueue_style?
&lt;/h2&gt;

&lt;p&gt;Pues la misma que hay entre &lt;code&gt;wp_register_script()&lt;/code&gt; y &lt;code&gt;wp_enqueue_script()&lt;/code&gt;. Todo lo que te he contado arriba aplica para estas dos.&lt;/p&gt;

&lt;p&gt;Por cierto, recuerda que el «hook» para ambos es siempre el mismo: &lt;code&gt;'wp_enqueue_scripts'&lt;/code&gt;. No existe  &lt;code&gt;'wp_enqueue_styles'&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Porqué meterme en todo este lío y no usar simplemente wp_enqueue?
&lt;/h2&gt;

&lt;p&gt;Por rendimiento y optimización de recursos. Debes intentar cargar los mínimos scripts y hojas de estilo que necesites, pero debido al orden de carga de WordPress, hay veces que no sabrá que tiene que cargar un script u hoja de estilo hasta muy avanzado el proceso de carga de la página.&lt;/p&gt;

&lt;p&gt;Por eso es importante, en esos casos, registrarlos antes con &lt;code&gt;wp_register_script()&lt;/code&gt; o &lt;code&gt;wp_register_style()&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cómo puedo forzar que mi script se cargue en el footer en lugar del head?
&lt;/h2&gt;

&lt;p&gt;Esta es una de las opciones de las funciones &lt;code&gt;wp_register_script()&lt;/code&gt; y &lt;code&gt;wp_enqueue_script()&lt;/code&gt;. Hay que poner a true el último parámetro que recibe la función.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wp_register_script( string $handle, string|bool $src, array $deps = array(), string|bool|null $ver = false, bool $in_footer = false )&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Por cierto, que en el Codex de WordPress tienes una &lt;a href="https://developer.wordpress.org/themes/basics/including-css-javascript/#default-scripts-included-and-registered-by-wordpress"&gt;lista completa de todos los scripts que se cargan por defecto con WordPress&lt;/a&gt;. Merece la pena darle un vistazo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jhtechservices.com/wordpress-wp_enqueue_script-vs-wp_register_script/"&gt;WordPress wp_enqueue_script() vs. wp_register_script()&lt;/a&gt; – JH Tech Services &lt;/li&gt;
&lt;li&gt;WordPress Theme Handbook: Including CSS &amp;amp; JavaScript – &lt;a href="https://developer.wordpress.org/themes/basics/including-css-javascript/"&gt;https://developer.wordpress.org/themes/basics/including-css-javascript/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/wp_register_script/"&gt;Codex: function wp_register_script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/wp_enqueue_script/"&gt;Codex: function wp_enqueue_script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/wp_localize_script/"&gt;Codex: functions wp_localize_script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Entradas relacionadas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/juananruiz/cual-es-la-diferencia-entre-issingle-e-issingular-5gjk"&gt;¿Cuál es la diferencia entre is_single() e is_singular()?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La entrada &lt;a href="https://kungfupress.com/diferencia-entre-wp_register_script-y-wp_enqueue_script/"&gt;Diferencia entre wp_register_script y wp_enqueue_script&lt;/a&gt; se publicó primero en &lt;a href="https://kungfupress.com"&gt;KungFuPress&lt;/a&gt; por &lt;a href="https://kungfupress.com/author/kungfupress/"&gt;Kung Fu Press&lt;/a&gt;&lt;/p&gt;

</description>
      <category>funciones</category>
      <category>css</category>
      <category>javascript</category>
      <category>rendimiento</category>
    </item>
    <item>
      <title>Sumar y restar cantidades con SQL en WordPress</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Sat, 27 Jul 2019 15:58:11 +0000</pubDate>
      <link>https://forem.com/juananruiz/sumar-y-restar-cantidades-con-sql-en-wordpress-53kb</link>
      <guid>https://forem.com/juananruiz/sumar-y-restar-cantidades-con-sql-en-wordpress-53kb</guid>
      <description>&lt;p&gt;La entrada &lt;a href="https://kungfupress.com/sumar-y-restar-cantidades-con-sql-en-wordpress/" rel="noopener noreferrer"&gt;Sumar y restar cantidades con SQL en WordPress&lt;/a&gt; se publicó primero en &lt;a href="https://kungfupress.com" rel="noopener noreferrer"&gt;KungFuPress&lt;/a&gt; por &lt;a href="https://kungfupress.com/author/kungfupress/" rel="noopener noreferrer"&gt;KungFuPress&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hace unos días un usuario preguntaba en un comentario del blog como sumar y restar cantidades a una tabla de productos en WordPress usando SQL.&lt;/p&gt;

&lt;p&gt;Parecía una pregunta simple con una respuesta simple pero después de darle un par de vueltas y hacer una pequeña prueba de concepto me ha parecido interesante mostrar aquí las dos aproximaciones que se me ocurren.&lt;/p&gt;

&lt;p&gt;El ejemplo es sencillo, tengo una tabla de productos que contiene el nombre del producto y la cantidad de producto en existencia. La idea es incrementar o disminuir estas cantidades a medida que se compren o se vendan productos.&lt;/p&gt;

&lt;p&gt;Para ello he creado un plugin que crea las tablas necesarias y un par de formularios en el escritorio para comprar y vender productos. Si necesitas entender mejor como funciona todo eso puedes consultar el artículo «&lt;a href="https://dev.to/juananruiz/como-programar-un-formulario-en-wordpress-sin-utilizar-plugins-3977"&gt;Cómo programar un formulario sin utilizar plugins&lt;/a&gt;» en este blog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F07%2Fcontrol-de-stock.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F07%2Fcontrol-de-stock.gif" alt="Gif animado con una demo del plugin en funcionamiento sumando y restando cantidades de la tabla de la base de datos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Update directo&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;La primera solución que he adoptado se basa en realizar un &lt;strong&gt;UPDATE&lt;/strong&gt;, sumando o restando al campo cantidad el incremento o decremento.&lt;/p&gt;

&lt;p&gt;Sería algo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$cantidad_adquirida = (int)$_POST['cantidad'];
$id_producto = (int)$_POST['id_producto'];
$wpdb-&amp;gt;query( $wpdb-&amp;gt;prepare( "UPDATE `$tabla_producto` SET cantidad = cantidad + %d WHERE id = %d", $cantidad_adqurida, $id_producto ));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta aproximación es muy directa y creo que bastante fácil de entender. Vamos con la segunda.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Select más $wpdb-&amp;gt;update&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Aproximación en tres pasos: uno para averiguar la cantidad actual con un &lt;strong&gt;SELECT&lt;/strong&gt; , luego hago la suma (o la resta) y finalmente una actualización a la nueva cantidad utilizando &lt;strong&gt;$wpdb-&amp;gt;update&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$cantidad_vendida = (int)$_POST['cantidad'];
$id_producto = (int)$_POST['id_producto'];
$consulta_cantidad_actual = "SELECT cantidad FROM $tabla_producto WHERE id = $id_producto";
$cantidad_actual = $wpdb-&amp;gt;get_var($consulta_cantidad_actual);
$cantidad_final = $cantidad_actual - $cantidad_vendida;
$wpdb-&amp;gt;update( $tabla_producto, array( 'cantidad' =&amp;gt; $cantidad_final ), array( 'id' =&amp;gt; $id_producto ));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creo que esta opción, aunque un poco menos directa, es más versátil y me permitiría implementar más operaciones encapsulándola en una sola función, ya es cuestión de contexto.&lt;/p&gt;

&lt;p&gt;En cualquier caso tienes el &lt;strong&gt;&lt;a href="https://github.com/kungfupress/kfp-stock" rel="noopener noreferrer"&gt;código completo en GitHub&lt;/a&gt;&lt;/strong&gt; en forma de plugin para que puedas instalarlo y probarlo en local. Ten cuidado porque aunque está todo hecho dentro del escritorio no tomo todas las medidas de seguridad a la hora de procesar el formulario por simplificar la explicación.&lt;/p&gt;

&lt;p&gt;Por cierto que el código incluye algunas otras cositas interesantes que espero poder comentar en próximos artículos.&lt;/p&gt;

&lt;p&gt;Y, por último, me encantaría que si se te ocurre otra manera de encararlo me dejaras un comentario para ampliar el artículo y ofrecer una solución más completa.&lt;/p&gt;

&lt;p&gt;La entrada &lt;a href="https://kungfupress.com/sumar-y-restar-cantidades-con-sql-en-wordpress/" rel="noopener noreferrer"&gt;Sumar y restar cantidades con SQL en WordPress&lt;/a&gt; se publicó primero en &lt;a href="https://kungfupress.com" rel="noopener noreferrer"&gt;KungFuPress&lt;/a&gt; por &lt;a href="https://kungfupress.com/author/kungfupress/" rel="noopener noreferrer"&gt;Kung Fu Press&lt;/a&gt;&lt;/p&gt;

</description>
      <category>basededatos</category>
      <category>mysql</category>
      <category>sql</category>
      <category>update</category>
    </item>
    <item>
      <title>Configura la seguridad de tu WordPress de una manera sencilla con el plugin iThemes Security</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Wed, 10 Jul 2019 17:35:20 +0000</pubDate>
      <link>https://forem.com/juananruiz/configura-la-seguridad-de-tu-wordpress-de-una-manera-sencilla-con-el-plugin-ithemes-security-3ika</link>
      <guid>https://forem.com/juananruiz/configura-la-seguridad-de-tu-wordpress-de-una-manera-sencilla-con-el-plugin-ithemes-security-3ika</guid>
      <description>&lt;p&gt;Aunque en este blog normalmente te hablo sobre temas relacionados con el desarrollo de WordPress me parece que es muy importante adoptar unas medidas mínimas de seguridad para que en las webs que administres no se pierda o desluzca tu trabajo por esta expuesto a riesgos innecesarios. Así que he hecho esta excepción para contarte que plugin utilizo habitualmente para aumentar la seguridad de mis sitios WordPress. &lt;/p&gt;

&lt;p&gt;Las medidas de seguridad, a la hora de desarrollar un plugin, ya os las pongo de serie en cualquier artículo que escribo. Pero recuerda que una cadena siempre se parte por el eslabón más débil, así que te recomiendo vigilar todos los frentes. &lt;/p&gt;

&lt;p&gt;iThemes Security es mi plugin favorito de seguridad en WordPress. Fácil de configurar para un usuario medio te protege de una gran cantidad de amenazas incluso en su versión gratuita. Por ello recomiendo instalarlo, activarlo y configurarlo en todos tus sitios WordPress. A no ser que ya uses otro claro.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9YGVBFsG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-1024x725.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9YGVBFsG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-1024x725.png" alt="Ficha de descarga e instalación del plugin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez instalado y activado, accede al panel de configuración y allí encontrarás un cuadro de diálogo que te recomienda activar una serie de módulos básicos para asegurar tu sitio. Pulsa sobre &lt;strong&gt;Secure Site&lt;/strong&gt; para activar estas medidas de seguridad básicas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nbDCXZNz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-1-1024x509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nbDCXZNz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-1-1024x509.png" alt="Configuración básica del plugin tras instalarlo, configura las medidas básicas de seguridad"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una vez hecho esto verás una serie de fichas básicas, algunas activadas otras no, que configuran distintos aspectos de seguridad. Cada una de estas fichas es un pequeño plugin en sí mismo y de vez en cuando añaden alguna ficha nueva.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_NDkPN6e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-3-1024x760.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_NDkPN6e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-3-1024x760.png" alt="Fichas con los diferente módulos de iThemes Security"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Además de la seguridad que obtienes “de serie” al ejecutar la opción &lt;strong&gt;Secure Site&lt;/strong&gt; que te acabo de comentar, hay otras medidas interesantes que puedes activar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Detección 404:&lt;/strong&gt; si un usuario o un bot intenta buscar rendijas de seguridad en tu web irá accediendo de forma insistente a páginas al azar. Cuando estas no existen tu web genera un error 404. Si activas este filtro en iThemes el plugin rechazará nuevas conexiones desde cualquier dirección IP que haga más de 20 intentos de conexión a páginas inexistentes. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modo de reposo:&lt;/strong&gt; los ataques más peligrosos a tu red pueden producirse mientras duermes. Seguro que hay un periodo del día en el que casi nunca vas a acceder a tu escritorio de WordPress. Durante esas horas puedes prevenir ataques deshabilitando el acceso a la zona de administración. Con este módulo puedes configurar ese bloqueo horario a tu gusto. Si un día concreto sabes que vas a necesitar acceder a una hora bloqueada basta que lo deshabilites de manera temporal cuando todavía estás en periodo de acceso autorizado. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detección de cambios de archivos:&lt;/strong&gt; este es un poco latoso por la cantidad de falsos positivos que genera y que llegarán a tu correo. Pero te lo recomiendo si quieres enterarte cuando se realicen cambios de archivos en tu instalación. Hay que saber discernir cuándo estos cambios son autorizados, debidos a actualizaciones, o son obra de algún usuario malicioso que se nos haya colado. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permisos de archivos&lt;/strong&gt; : este es un poco más avanzado porque hay que tocar los permisos en el servidor o hosting. Comprueba la configuración actual de permisos de las carpetas y archivos más sensibles de WordPress y te señala cuales están correcta o incorrectamente configurados con una sugerencia sobre la estructura ideal de permisos. No es muy complicado, te puede evitar algún susto pero tienes que saber modificar permisos en el servidor.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL&lt;/strong&gt; : si tienes un certificado SSL para que tus visitantes accedan de manera segura (algo muy recomendable por cierto), es importante que nadie entre por http pues estaría menos protegidos. Con este módulo fuerzas que cualquier intento de acceder por http se redirija a https. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requisitos de contraseñas:&lt;/strong&gt; cualquier usuario con cuenta en tu sitio debería utilizar una contraseña segura (recuerda lo del eslabón más débil). Con la configuración por defecto de iThemes el requisito de contraseña segura se establece sólo para los administradores, pero te recomiendo que lo apliques al perfil más bajo que aparezca en tu lista, en mi caso &lt;strong&gt;Suscriptor&lt;/strong&gt; , para que afecte a todos los perfiles de usuario.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Algún efectos colateral:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bloqueo de REST API:&lt;/strong&gt; Este es uno de los bloqueos que iThemes Security activa por defecto cuando aseguras tu sitio con las opciones por defecto. En algunos casos es recomendable dejarlo así porque la REST API  puede dar acceso público a información de tu sitio que piensas que es privada. El problema es que algunos plugins y servicios necesitan que esté activada. Así que si la activas y ves que no te perjudica en nada, déjala así y si ves que te quita algo “imprescindible” para ti puedes desbloquearla accediendo desde la ficha &lt;strong&gt;Ajustes de WordPress&lt;/strong&gt; y en &lt;strong&gt;REST API&lt;/strong&gt; escoges la opción &lt;strong&gt;Acceso por defecto&lt;/strong&gt;. En uno de mis blogs esta opción hacía que el panel que me mostraba los resultados de Google Analytics en el escritorio me dejara de funcionar. Pero cómo puedo consultar estos datos desde otras herramientas he preferido dejar la API REST bloqueada de momento. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lpS4ajnp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-2-1024x511.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lpS4ajnp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/07/image-2-1024x511.png" alt="Módulo REST API, ofrece dos opciones: acceso restringido y acceso por defecto"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avisos de bloqueo e intentos de acceso:&lt;/strong&gt; de vez en cuando recibirás un correo de que cierta IP se ha bloqueado o que un usuario ha intentado entrar como “admin”. Esto es normal, son cosas que pasaban en tu sitio pero no lo sabías, ahora serás un poco más consciente y tendrás cuidado de no tener una cuenta que se llame “admin”, de usar buenas contraseñas y de estar un poco más pendiente de la seguridad de tu sitio pero sin volverte paranoico tampoco.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recuerda que los sitios web reciben ataques casi a diario, aunque no seamos conscientes de ello y toma estas precauciones de seguridad nos puede ahorrar muchos dolores de cabeza con muy poco esfuerzo. &lt;/p&gt;

&lt;p&gt;Si tienes alguna duda sobre este artículo, alguna aportación para mejorarlo o simplemente sientes un deseo refrenable de compartirlo con otros. No lo dudes, déjame un comentario o comparte un enlace en tu red social favorita. Recuerda que el karma exista y que las buenas acciones se nos devuelven multiplicadas &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CDd67yc0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CDd67yc0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/12.0.0-1/72x72/1f609.png" alt="😉"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>seguridad</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>¿Cuál es la diferencia entre is_single() e is_singular()?</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Wed, 26 Jun 2019 19:54:30 +0000</pubDate>
      <link>https://forem.com/juananruiz/cual-es-la-diferencia-entre-issingle-e-issingular-5gjk</link>
      <guid>https://forem.com/juananruiz/cual-es-la-diferencia-entre-issingle-e-issingular-5gjk</guid>
      <description>&lt;p&gt;La función &lt;strong&gt;is_single()&lt;/strong&gt; devuelve &lt;strong&gt;true&lt;/strong&gt;  si el contenido actual es una entrada única, un &lt;strong&gt;post&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;La función &lt;strong&gt;is_singular()&lt;/strong&gt; devuelve &lt;strong&gt;true&lt;/strong&gt;  si el contenido actual es una entrada, página, custom post type, etc. Cualquier tipo de contenido, pero que sea único. Devuelve &lt;strong&gt;false&lt;/strong&gt; si el contenido actual es una página de categoría, etiqueta o un listado cronológico de entradas.&lt;/p&gt;

&lt;p&gt;Ambas tienen en común que se utilizan para variar el comportamiento o el aspecto de la página actual en función del tipo de contenido que se está cargando.&lt;/p&gt;

&lt;p&gt;Ten en cuenta que, si no hay contenido, también devuelve &lt;strong&gt;false,&lt;/strong&gt; así que no te confíes pensando que tienes una pila de artículos por delante cuando en realidad no hay nada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F06%2Fkeep_calm_iamsingle.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F06%2Fkeep_calm_iamsingle.jpg" alt="Keep Calm. I am single."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un ejemplo para que lo veas más claro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if ( is_singular() ) { 
  //carga un script y una hoja de estilos 
  wp_enqueue_script( … ); 
  wp_enqueue_style( … );
} else { 
  // carga un paginador
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La función is_single() tiene dos funciones hermanas de utilidad similar: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is_page(): si el contenido actual es una &lt;strong&gt;página&lt;/strong&gt; única.&lt;/li&gt;
&lt;li&gt;is_attachment(): si el contenido actual es un &lt;strong&gt;enlace&lt;/strong&gt; único (no creo que nadie use esto a estas alturas) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La función &lt;strong&gt;is_singular()&lt;/strong&gt; no tiene hermanas, pero si tiene varias “contrarias”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is_category()&lt;/li&gt;
&lt;li&gt;is_tag()&lt;/li&gt;
&lt;li&gt;is_archive()&lt;/li&gt;
&lt;li&gt;is_author()&lt;/li&gt;
&lt;li&gt;is_search()&lt;/li&gt;
&lt;li&gt;is_tax()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A toda esta “familia» tan numerosa se les llama &lt;strong&gt;Conditional Tags&lt;/strong&gt; y todas aceptan un parámetro que puede ser de tipo entero, cadena o array para poder ser más específicos en la búsqueda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;En el caso de is_single() y sus hermanas se le puede pasar el ID concreto del contenido, el slug o un array de identificadores o slugs. &lt;/li&gt;
&lt;li&gt;En el caso de is_singular(), y sus contrarias, se puede especificar el tipo de contenido concreto o un array de tipos de contenido.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por otro lado, no sé si has caído en la cuenta de que estas funciones tal como las ves tienen utilidad en las plantillas de los temas, pero no en un plugin.&lt;/p&gt;

&lt;p&gt;Desde un plugin tienes la misma funcionalidad a través de la clase &lt;strong&gt;WP_Query&lt;/strong&gt; que incorpora todos estos métodos: is_single, is_page, is_archive, is_preview, is_author, is_category, is_tag, is_tax, is_search, is_feed, is_comment_feed, is_404, is_attachment, is_singular.&lt;/p&gt;

&lt;h3&gt;
  
  
  Referencias
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.proyectoalfa.es/diferencia-is-page-single-singular-wordpress/" rel="noopener noreferrer"&gt;Diferencia entre is_page(), is_single(), is_singular() y posibles usos.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Conditional_Tags" rel="noopener noreferrer"&gt;WordPress Codex: Conditional Tags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Class_Reference/WP_Query" rel="noopener noreferrer"&gt;WordPress Codex: Class Reference / WP_Query&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>funciones</category>
    </item>
    <item>
      <title>Cómo obtener la versión de WordPress desde un plugin</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Tue, 14 May 2019 06:01:41 +0000</pubDate>
      <link>https://forem.com/juananruiz/como-obtener-la-version-de-wordpress-desde-un-plugin-2o5h</link>
      <guid>https://forem.com/juananruiz/como-obtener-la-version-de-wordpress-desde-un-plugin-2o5h</guid>
      <description>&lt;p&gt;Para obtener la versión de WordPress puedes usar la función &lt;code&gt;get_bloginfo('version')&lt;/code&gt; que te devuelve el número de versión, ejemplo: &lt;strong&gt;5.2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Evidentemente esta función es útil, simplemente, para mostrar esta información a nuestros usuarios en un momento dado, sobre todo a editores o administradores del sitio.&lt;/p&gt;

&lt;p&gt;Otro uso puede ser el de variar el comportamiento del plugin que estás desarrollando en función de la versión de WordPress actual. Para ello puedes usar la función &lt;code&gt;version_compare&lt;/code&gt; que tiene precisamente esta misión. Esta función admite tres parámetros: los dos números de versiones que necesitas comparar y el operador de comparación que quieres usar.&lt;/p&gt;

&lt;p&gt;En el siguiente ejemplo puedes ver estas dos funciones en acción en un pequeño plugin de usar y tirar.&lt;/p&gt;

&lt;p&gt;El plugin muestra el número de versión actual como un elemento del menú de administración de WordPress, al pulsar sobre este nos mostrará una pequeña frase que varía en función de que la versión actual sea menor a la 5.0.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="sd"&gt;/**
 * Plugin Name: KFP Version WP
 * Author: Tonio Ruiz
 */&lt;/span&gt;

&lt;span class="nx"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"admin_menu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Kfp_Vwp_Admin_menu"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Kfp_Verwp_Admin_menu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;add_menu_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'Versión WordPress '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Wordpress '&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;get_bloginfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="s1"&gt;'manage_options'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kfp_vwp_admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kfp_Vwp_admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'dashicons-wordpress'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Kfp_Vwp_admin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;version_compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;get_bloginfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'5.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;h3&amp;gt;Soy un WordPress "&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;get_bloginfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" pregutemberiano&amp;lt;/h3&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;h3&amp;gt;Soy un WordPress "&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;get_bloginfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" con todo su Gutemberg&amp;lt;/h3&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Por cierto la función &lt;code&gt;get_blog_info( )&lt;/code&gt; tiene muchas más utilidades: nombre del sitio, descripción, correo del administrador, url del sitio, etc. Puedes &lt;a href="https://developer.wordpress.org/reference/functions/get_bloginfo/"&gt;verlas todas en la referencia de WordPress&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>plugin</category>
      <category>versión</category>
    </item>
    <item>
      <title>¿Cómo programar un formulario en WordPress sin utilizar plugins?</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Fri, 19 Apr 2019 14:53:40 +0000</pubDate>
      <link>https://forem.com/juananruiz/como-programar-un-formulario-en-wordpress-sin-utilizar-plugins-3977</link>
      <guid>https://forem.com/juananruiz/como-programar-un-formulario-en-wordpress-sin-utilizar-plugins-3977</guid>
      <description>

&lt;h3&gt;
  
  
  Porqué desarrollar un formulario en vez de usar un plugin
&lt;/h3&gt;

&lt;p&gt;Hay multitud de plugins para crear formularios, pero si eres programador de WordPress y lo único que necesitas es crear un par de formularios concretos puedes ofrecer una solución más personalizada creando un pequeño plugin de desarrollo propio, además ahorras la carga de un plugin mucho más grande, lo que redundará en la velocidad del sitio&lt;br&gt;&lt;br&gt;
Este plugin es muy mejorable pero creo que es un buen ejercicio para alguien que esté empezando. Si es tu caso y estás leyendo este artículo para aprender te recomiendo no ir cortando y pegando el código, sino irlo tecleando para asimilarlo mejor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por donde empiezo ¿cuál es el mejor sitio para el código de mi formulario?
&lt;/h3&gt;

&lt;p&gt;En WordPress hay tres sitios diferenciados para desarrollar tu código:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;En el tema&lt;/strong&gt; (bueno en el tema hijo): recomendable si lo que estás haciendo es un cambio en la presentación, o agregando una información a tus páginas en la que vayas a usar fundamentalmente funciones de WordPress, con poco código propio, algo de HTML, algo de CSS, quizás JS, poco PHP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En el archivo functions.php&lt;/strong&gt; del tema (del tema hijo): igual que el anterior pero vas a usar esos cambios en varias plantillas o el tamaño del código añadido justifica una función.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En un plugin&lt;/strong&gt; : si vas a necesitar algo más de lógica, tienes que escribir bastante PHP y no es tanto una cuestión de estética o presentación como de mecánica o comportamiento, la cosa está pidiendo un plugin a gritos. Además un plugin lo vas a poder usar independientemente del tema que tengas activo. En esta caso, que estás desarrollando un formulario claramente necesitas un plugin. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creación del plugin
&lt;/h3&gt;

&lt;p&gt;Crear un plugin es tan sencillo como crear un directorio nuevo en la carpeta de plugins y en su interior un archivo con el mismo nombre que la carpeta y la extensión &lt;strong&gt;.php&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
El nombre de carpeta y archivo debe ser descriptivo pero corto, no usar espacios sino guiones bajos y para evitar colisiones con otros plugins le puedes colocar delante unas siglas propias o un nombre corto: tu alias, tu marca, etc.&lt;br&gt;&lt;br&gt;
En mi caso la carpeta será &lt;strong&gt;kfp_form_autoevaluacion&lt;/strong&gt;  y el archivo principal &lt;strong&gt;kfp_form_autoevaluacion.php&lt;/strong&gt;. Pero te animo a poner tu sufijo propio, y si vas a usar el formulario para otra cosa también debes cambiar el resto del nombre.&lt;br&gt;&lt;br&gt;
La primera versión del archivo debe contener una cabecera mínima para que WordPress lo considere un plugin. Ese mínimo funcional es el &lt;strong&gt;Plugin Name&lt;/strong&gt; que debe reflejar el nombre de la carpeta y del archivo principal del plugin. No olvides la etiqueta inicial &lt;strong&gt;&amp;lt;?php&lt;/strong&gt;&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; 
&lt;span class="sd"&gt;/**
 * Plugin Name: KFP Form Autoevaluacion
 */&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Si ahora vas al menú &lt;strong&gt;Plugins&lt;/strong&gt; del panel de administración de WordPress debes ver allí tu plugin &lt;strong&gt;KFP Form Autoevaluacion&lt;/strong&gt; , puedes activarlo y comprobar que no explota nada en tu web. Si quieres que tu plugin quede mejor documentado puedes añadir otros campos a la cabecera como &lt;strong&gt;Description&lt;/strong&gt; , &lt;strong&gt;Author&lt;/strong&gt; o &lt;strong&gt;Version&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bml9Z_6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/kfp_form_aspirante_activacion_plugin-1024x398.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bml9Z_6O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/kfp_form_aspirante_activacion_plugin-1024x398.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Empezando a pintar el formulario
&lt;/h3&gt;

&lt;p&gt;Ya tienes claro que quieres desarrollar un formulario para recoger una serie de datos y grabarlos en una tabla, pero todavía no sabes dónde lo vas a colocar. La mejor forma de insertar un formulario en una página o un post de WordPress es hacerlo mediante un shortcode.&lt;/p&gt;

&lt;p&gt;Un shortcode es una pequeña etiqueta de texto que va entre corchetes []. Para insertarlo en una página tendrás que hacerlo desde el modo HTML del editor clásico o usando un bloque especial de Gutemberg que se llama shortcode&lt;/p&gt;

&lt;p&gt;Para programar un shortcode utilizas una función especial de WordPress a la que tienes que pasarle una función definida por ti que hará todo el trabajo. De momento el shortcode sólo va a pintar el formulario en la página o entrada donde lo coloques, sin capacidad de procesar el envío, ya llegarás a eso.&lt;/p&gt;

&lt;p&gt;Para que sea más didáctico te iré poniendo el fragmento de código en el que irás trabajando, pero, si te pierdes en algún momento o quieres ver el resultado final antes de empezar, tienes todo el código en el &lt;a href="https://github.com/kungfupress/kfp_formulario_aspirantes"&gt;repositorio de GitHub de KungFuPress&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Casi todo el código va comentado, así que espero que lo vayas entendiendo a medida que lo vas escribiendo, si no entiendes porqué se hace algo concreto te ruego me lo preguntes usando los comentarios para mejorar este tutorial.&lt;/p&gt;

&lt;p&gt;Lo único que no he comentado es el parámetro &lt;strong&gt;action&lt;/strong&gt; del formulario, que llama a la función &lt;strong&gt;get_the_permalink&lt;/strong&gt; de WordPress, esta función devuelve la url (o permalink) de la entrada actual, con lo que cuando un usuario envíe el formulario se volverá a llamar a la entrada actual, por lo que aparecerá de nuevo el formulario. Más adelante pondrás un aviso para informar al usuario del resultado de su envío.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="sd"&gt;/**
* Plugin Name: KFP Form Autoevaluacion
*/&lt;/span&gt;

&lt;span class="c1"&gt;// Define el shortcode y lo asocia a una función&lt;/span&gt;
&lt;span class="nx"&gt;add_shortcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'kfp_aspirante_form'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kfp_Aspirante_form'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="sd"&gt;/** 
 * Define la función que ejecutará el shortcode
 * De momento sólo pinta un formulario que no hace nada
 * 
 * @return string
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Kfp_Aspirante_form&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Esta función de PHP activa el almacenamiento en búfer de salida (output buffer)&lt;/span&gt;
    &lt;span class="c1"&gt;// Cuando termine el formulario lo imprime con la función ob_get_clean&lt;/span&gt;
    &lt;span class="nb"&gt;ob_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="nx"&gt;get_the_permalink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"form_aspirante"&lt;/span&gt;
&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cuestionario"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"nombre"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Nombre&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nombre"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"nombre"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;'correo'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Correo&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"correo"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"correo"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"nivel_html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;¿Cuál es tu nivel de HTML?&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_html"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Nada
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_html"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Estoy 
                aprendiendo
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_html"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Tengo 
                experiencia
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_html"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Lo 
                domino al dedillo
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"nivel_css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;¿Cuál es tu nivel de CSS?&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_css"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Nada
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_css"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Estoy 
                aprendiendo
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_css"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Tengo 
                experiencia
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_css"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Lo 
                domino al dedillo
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"nivel_js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;¿Cuál es tu nivel de JavaScript?&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_js"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Nada
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_js"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Estoy 
                aprendiendo
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_js"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Tengo 
                experiencia
            &lt;span class="nt"&gt;&amp;lt;br&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"nivel_js"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Lo domino 
al dedillo
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"aceptacion"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;La información facilitada se tratará 
            con respeto y admiración.&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"aceptacion"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"aceptacion"&lt;/span&gt;
&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Entiendo y acepto las condiciones
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Enviar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

    &lt;span class="c1"&gt;// Devuelve el contenido del buffer de salida&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;ob_get_clean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Comprueba que lo estás haciendo correctamente poniendo en una entrada de tu blog el shortcode &lt;strong&gt;[kfp_aspirante_form]&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5aQrzUTE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/kfp_form_aspirante_insertando_shortcode-1024x287.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5aQrzUTE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/kfp_form_aspirante_insertando_shortcode-1024x287.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando vayas a tu entrada debes ver un formulario como el de la imagen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GnJtgR8P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/kfp_form_aspirante_formulario_sin_formato-1024x928.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GnJtgR8P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/kfp_form_aspirante_formulario_sin_formato-1024x928.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bueno, no es lo más estético que habrás visto en formularios, pero… ¡es el tuyo! &lt;/p&gt;

&lt;h4&gt;
  
  
  Un poco de estilo
&lt;/h4&gt;

&lt;p&gt;Si te duelen los ojos de ver este formulario puedes insertar una pequeña hoja de estilo para adecentarlo desde el propio plugin. Crea un fichero dentro de la carpeta del plugin con el nombre &lt;strong&gt;style.css&lt;/strong&gt; y el siguiente contenido:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form.cuestionario {
    width: 600px;
}
form.cuestionario label {
    font-weight: bold;
    display: block;
    margin-bottom: 5px;
}
form.cuestionario .form-input {
    padding: 1em;
}
form.cuestionario .form-input input[type='text'],
form.cuestionario .form-input input[type='email'] {
    display: block;
    width: 100%;
}
p.exito {
    font-size: 1.5em;
    padding: 1em;
    border: 2px solid green;
    background: lightgreen;
}
p.error {
    font-size: 1.5em;
    padding: 1em;
    border: 2px solid red;
    background: #ff8787;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Luegon inserta la función &lt;strong&gt;wp_enqueue_style&lt;/strong&gt; dentro de la función &lt;strong&gt;Kfp_Aspirante_form&lt;/strong&gt; antes de abrir el buffer de salida &lt;strong&gt;ob_start&lt;/strong&gt; que imprime el formulario.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Carga esta hoja de estilo para poner más bonito el formulario
wp_enqueue_style('css_aspirante', plugins_url('style.css', __FILE__));

// Esta función de PHP activa el almacenamiento en búfer de salida (output buffer)
// Cuando termine el formulario lo imprime con la función ob_get_clean
ob_start();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Crear la tabla para recoger los datos
&lt;/h3&gt;

&lt;p&gt;Casi todo el contenido de un blog de WordPress, ya sean entradas, páginas o incluso las imágenes; se graban en la misma tabla de la base de datos: la tabla &lt;strong&gt;wp_posts&lt;/strong&gt;. Esto tiene sus ventajas y sus incovenientes, y quizás para grabar los datos de un formulario lo más adecuado sería crear un &lt;strong&gt;&lt;a href="https://kungfupress.com/como-crear-tipos-y-campos-personalizados-en-wordpress/"&gt;custom post type o tipo personalizado&lt;/a&gt;&lt;/strong&gt; con sus correspondientes &lt;strong&gt;custom fields o campos personalizados&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En este caso he preferido crear una tabla aparte para recoger los datos. El momento idoneo para crear esta tabla o comprobar que ya existe es en el momento en que se activa el propio plugin. Para ello puedes usar la función &lt;strong&gt;register_activation_hook&lt;/strong&gt; y una sencilla consulta SQL de creación de tabla. Tienes un buen &lt;a href="https://es.wikibooks.org/wiki/MySQL"&gt;manual de MySQL en Wikilibros&lt;/a&gt; y si quieres algo más rápido mira el artículo sobre &lt;a href="https://es.wikipedia.org/wiki/SQL"&gt;SQL en Wikipedia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Puedes poner este código en cualquier lugar del fichero del plugin, yo he preferido ponerlo casi al principio, detrás de la cabecera y antes de definir el shortcode.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Cuando el plugin se active se crea la tabla para recoger los datos si no existe
register_activation_hook(__FILE__, 'Kfp_Aspirante_init');

/**
 * Crea la tabla para recoger los datos del formulario
 *
 * @return void
 */
function Kfp_Aspirante_init() 
{
    global $wpdb; // Este objeto global permite acceder a la base de datos de WP
    // Crea la tabla sólo si no existe
    // Utiliza el mismo prefijo del resto de tablas
    $tabla_aspirantes = $wpdb-&amp;gt;prefix . 'aspirante';
    // Utiliza el mismo tipo de orden de la base de datos
    $charset_collate = $wpdb-&amp;gt;get_charset_collate();
    // Prepara la consulta
    $query = "CREATE TABLE IF NOT EXISTS $tabla_aspirantes (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        nombre varchar(40) NOT NULL,
        correo varchar(100) NOT NULL,
        nivel_html smallint(4) NOT NULL,
        nivel_css smallint(4) NOT NULL,
        nivel_js smallint(4) NOT NULL,
        aceptacion smallint(4) NOT NULL,
        created_at datetime NOT NULL,
        UNIQUE (id)
        ) $charset_collate;";
    // La función dbDelta permite crear tablas de manera segura se
    // define en el fichero upgrade.php que se incluye a continuación
    include_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($query); // Lanza la consulta para crear la tabla de manera segura
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Una vez tecleado este código (recuerda que siempre recomiendo teclear el código en lugar de copia y pegar) debes desactivar el plugin y volver a activarlo para que se ejecute la función &lt;strong&gt;register_activation_hook&lt;/strong&gt;. Si todo ha ido bien deberías poder ver en tu base de datos la nueva tabla (algo como &lt;strong&gt;wp_aspirante&lt;/strong&gt; ).&lt;/p&gt;

&lt;h3&gt;
  
  
  Grabar los datos
&lt;/h3&gt;

&lt;p&gt;Ya tienes definido el formulario y la tabla preparada, ¿no sería maravilloso qué cuando accedas al formulario y rellenes los datos estos se guarden en la tabla? Pues eso es lo que vas a hacer ahora.&lt;/p&gt;

&lt;p&gt;Debes escribir este código dentro de la función &lt;strong&gt;Kfp_Aspirante_form&lt;/strong&gt; que creaste al definir el shortcode. Puedes ponerlo hacia el principio o el final, pero dentro de la función, yo he preferido ponerlo al principio, delante del formulario que acabas de crear.&lt;/p&gt;

&lt;p&gt;Lo primero que deberías hacer es comprobar que vienen rellenos todos los datos que vengan marcados como &lt;strong&gt;required&lt;/strong&gt; en el formulario, se supone que sin esos no tendría sentido guardar la información en la base de datos. En este caso todos lo son, así que se comprueban todos. Esta comprobación la acabas de realizar, precisamente con el &lt;strong&gt;required&lt;/strong&gt; , en el lado del cliente, pero como no todos nuestros visitantes van a acceder con un navegador actualizado lo debes volver a comprobar aquí también. Esto te va a pasar en más ocasiones, todo lo que compruebes o sanees en el lado del cliente debes volver a comprobarlo en el lado del servidor&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_shortcode('kfp_aspirante_form', 'Kfp_Aspirante_form');

/** 
 * Define la función que ejecutará el shortcode
 * Comprueba si se han enviado los datos desde el formulario
 * y pinta el formulario
 *
 * @return string
 */
function Kfp_Aspirante_form() 
{
    global $wpdb; // Este objeto global permite acceder a la base de datos de WP
    // Si viene del formulario  graba en la base de datos
    // Cuidado con el último igual de la condición del if que es doble
    if ($_POST['nombre'] != ''
        AND is_email($_POST['correo'])
        AND $_POST['nivel_html'] != ''
        AND $_POST['nivel_css'] != ''
        AND $_POST['nivel_js'] != ''      
        AND $_POST['aceptacion'] == '1'
    ) {
        $tabla_aspirantes = $wpdb-&amp;gt;prefix . 'aspirante'; 
        $nombre = sanitize_text_field($_POST['nombre']);
        $correo = $_POST['correo'];
        $nivel_html = (int)$_POST['nivel_html'];
        $nivel_css = (int)$_POST['nivel_css'];
        $nivel_js = (int)$_POST['nivel_js'];
        $aceptacion = (int)$_POST['aceptacion'];
        $created_at = date('Y-m-d H:i:s');
        $wpdb-&amp;gt;insert(
            $tabla_aspirantes,
            array(
                'nombre' =&amp;gt; $nombre,
                'correo' =&amp;gt; $correo,
                'nivel_html' =&amp;gt; $nivel_html,
                'nivel_css' =&amp;gt; $nivel_css,
                'nivel_js' =&amp;gt; $nivel_js,
                'aceptacion' =&amp;gt; $aceptacion,
                'created_at' =&amp;gt; $created_at,
            )
        );
        echo "&amp;lt;p class='exito'&amp;gt;&amp;lt;b&amp;gt;Tus datos han sido registrados&amp;lt;/b&amp;gt;. Gracias 
            por tu interés. En breve contactaré contigo.&amp;lt;p&amp;gt;";
    }
// Esto que viene ya lo debes tener escrito del primer paso    
    ob_start();
    ?&amp;gt;
&amp;lt;form action="&amp;lt;?php get_the_permalink(); ?&amp;gt;" method="post" id="form_aspirante"
    class="cuestionario"&amp;gt;
// ...y continúa el formulario que escribiste 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Graba el código en tu editor y recarga la página donde estás mostrando el formulario, si todo ha ido bien, debería aparecer el mensaje de confirmación y en la base de datos deberías ver la información grabada.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validando y saneando datos
&lt;/h3&gt;

&lt;p&gt;Observa que en el código que acabas de teclear se hacen varias comprobaciones de los datos antes de grabarlos. Enumero las funciones utilizadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sanitize_text_field&lt;/strong&gt; : impide que puedan ir etiquetas o caractéres “peligrosos” dentro de un campo de texto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;is_email&lt;/strong&gt; : función de WordPress para validar que una cadena es un correo bien conformado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(int)&lt;/strong&gt;: es un “cast” de php que intenta convertir la variable que viene a continuación en una variable de tipo &lt;strong&gt;int&lt;/strong&gt;. Sería un saneado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por último, en todo formulario que desarrolles debes intentar evitar un ataque de CSRF (Cross-Site Request Forgery) . Para ello vas a insertar un campo &lt;strong&gt;nonce&lt;/strong&gt; que te ayude a comprobar que el formulario que se está procesando viene de tu propia web utilizando las funciones &lt;strong&gt;wp_nonce&lt;/strong&gt; de WordPress. Tienes una buena explicación de lo que es un ataque de este tipo en el artículo &lt;a href="https://igmoweb.com/2017/11/29/cross-site-request-forgery-dos-ejemplos-para-entenderlo/"&gt;“Cross-site Request Forgery: Dos ejemplos para entenderlo”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Así que tienes que introducir este código (o uno parecido) justo después de la etiqueta &lt;strong&gt;form&lt;/strong&gt; y antes del primer &lt;strong&gt;input&lt;/strong&gt;.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form action="&amp;lt;?php get_the_permalink(); ?&amp;gt;" method="post" id="form_aspirante"
      class="cuestionario"&amp;gt;
    &amp;lt;?php wp_nonce_field('graba_aspirante', 'aspirante_nonce'); ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Por último tienes que incluir una condición más cuando estás comprobando si vienen todos los datos requeridos del formulario&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AND $_POST['aceptacion'] == '1'
AND wp_verify_nonce($_POST['aspirante_nonce'], 'graba_aspirante')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Sobre el tema de validación y saneado estoy preparando un artículo que espero publicar próximamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bola extra: cumplir con RGPD en tú formulario
&lt;/h3&gt;

&lt;p&gt;Una cosa que muchos plugins de formulario (al menos la versión gratuita) no hacen, es cumplir con los requisitos básicos de RGPD (Reglamento General de Protección de Datos) al solicitar la aceptación de “Política de Privacidad”: guardar el nombre de la persona que da su consentimiento, el acepto, la dirección IP, la fecha y la hora.&lt;/p&gt;

&lt;p&gt;Aunque lo incluyo en el código completo del plugin, que te puedes &lt;a href="https://github.com/kungfupress/kfp_formulario_aspirantes"&gt;descargar desde Github&lt;/a&gt;, te dejo como ejercicio el desarrollarlo por tu cuenta. Teniendo en cuenta que ya guarda el nombre, el “acepto” y la fecha con la hora, sólo faltaría guardar la IP.&lt;/p&gt;

&lt;p&gt;Que yo conozca no hay ninguna función en WordPress que te devuelva la IP del usuario así que te animo a buscar en la web alguna solución para implementar tu propia función dentro del plugin, a la que puedas llamar antes de grabar los datos, para que te devuelva la IP del usuario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crear un menú de administración para el plugin
&lt;/h3&gt;

&lt;p&gt;Por último necesitarás alguna forma de consultar los datos recogidos por el formulario que se han ido almacenando en la base de datos. Como estos datos solo deben estar accesibles a los administradores del sitio vas a crear un menú específico dentro de la zona de administración.&lt;/p&gt;

&lt;p&gt;Para ello usuarás el hook &lt;strong&gt;admin_menu&lt;/strong&gt; que llamará a la función &lt;strong&gt;Kfp_Aspirante_menu&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esta función a su vez utiliza la función &lt;strong&gt;add_menu_page&lt;/strong&gt; de WordPress, encargada de crear el menú si el usuario tiene privilegios suficientes. La función &lt;strong&gt;add_menu_page&lt;/strong&gt; recibe varios argumentos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El título de la página que se cargará al seleccionar el menú.&lt;/li&gt;
&lt;li&gt;El nómbre del elemento del menú.&lt;/li&gt;
&lt;li&gt;La capacidad (o privilegio) que tiene que tener el usuario para que se cargue el menú, en este caso &lt;strong&gt;manage_options.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;El &lt;strong&gt;slug&lt;/strong&gt; o alias que aparecererá en la URL de la página de administración.&lt;/li&gt;
&lt;li&gt;El nombre de la función que construirá la página que se cargará cuando se seleccione el menú.&lt;/li&gt;
&lt;li&gt;El icono que aparecerá asociado al menú. Hay varias formas de hacerlo, para simplificar vas a usar un &lt;a href="https://developer.wordpress.org/resource/dashicons/"&gt;dashicon&lt;/a&gt;, en este caso &lt;strong&gt;dashicon-feedback&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;La posisición del elemento en el menú, en este caso he puesto &lt;strong&gt;75&lt;/strong&gt;. Aunque esto depende de la versión de WordPress y de los plugins que tengas ya instalados, para no marearte lo mejor es no ponerlo y saldrá el último&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// El hook "admin_menu" permite agregar un nuevo item al menú de administración
add_action("admin_menu", "Kfp_Aspirante_menu");

/**
 * Agrega el menú del plugin al panel de administración
 *
 * @return void
 */
function Kfp_Aspirante_menu() 
{
    add_menu_page(
        'Formulario Aspirantes', 'Aspirantes', 'manage_options', 
        'kfp_aspirante_menu', 'Kfp_Aspirante_admin', 'dashicons-feedback', 75
    );
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Tras grabar el código ve al panel de administración de WordPress y allí debe aparecer un nuevo elemento de menú con el nombre &lt;strong&gt;Aspirantes&lt;/strong&gt; , al pulsar sobre él se carga un panel a la derecha en el que, de momento, no aparece nada. Ahora vas a mostrar ahí los datos que has capturado desde el formulario (espero que hayas rellenado el formulario unas cuantas veces para tener algo que mostrar).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJTVQNX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/menus_apirantes-1024x309.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJTVQNX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/menus_apirantes-1024x309.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Crear una tabla para mostrar los datos del plugin desde el panel de administración&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Uno de los argumentos de la función &lt;strong&gt;add_menu_page&lt;/strong&gt; es el nombre de una función que pinte el panel o página de administración que se va a cargar cuando pulses sobre tu nuevo menú. Esa función , que vas a definir ahora, es la encargada de generar el código HTML que aparecerá en la página.&lt;/p&gt;

&lt;p&gt;La función &lt;strong&gt;Kfp_Aspirante_admin&lt;/strong&gt; hará cuatro cosas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imprimir la cabecera de una tabla HTML para mostrar los datos&lt;/li&gt;
&lt;li&gt;Ejecutar una consulta SQL para obtener los datos desde la tabla de la base de datos generada por el propio plugin.&lt;/li&gt;
&lt;li&gt;Recorrer con un bucle los registros obtenidos para irlos pintando en la tabla HTML&lt;/li&gt;
&lt;li&gt;Imprimir el cierre de la tabla HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observa que cuando vas a mostrar los campos de la base de datos &lt;strong&gt;escapas&lt;/strong&gt; su contenido para que el usuario no reciba ninguna sorpresa. A pesar de todas las precauciones que has tomado en la fase de recogida de datos nunca puedes estar 100% seguro de que alguien haya &lt;strong&gt;inyectado&lt;/strong&gt; algo en tus tablas, así que debes &lt;strong&gt;escapar&lt;/strong&gt; en salida.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Crea el contenido del panel de administración para el plugin
 *
 * @return void
 */
function Kfp_Aspirante_admin()
{
    global $wpdb;
    $tabla_aspirantes = $wpdb-&amp;gt;prefix . 'aspirante';
    echo '&amp;lt;div class="wrap"&amp;gt;&amp;lt;h1&amp;gt;Lista de aspirantes&amp;lt;/h1&amp;gt;';
    echo '&amp;lt;table class="wp-list-table widefat fixed striped"&amp;gt;';
    echo '&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th width="30%"&amp;gt;Nombre&amp;lt;/th&amp;gt;&amp;lt;th width="20%"&amp;gt;Correo&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;HTML&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;CSS&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;JS&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;PHP&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;WP&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Total&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;';
    echo '&amp;lt;tbody id="the-list"&amp;gt;';
    $aspirantes = $wpdb-&amp;gt;get_results("SELECT * FROM $tabla_aspirantes");
    foreach ( $aspirantes as $aspirante ) {
        $nombre = esc_textarea($aspirante-&amp;gt;nombre);
        $correo = esc_textarea($aspirante-&amp;gt;correo);
        $motivacion = esc_textarea($aspirante-&amp;gt;motivacion);
        $nivel_html = (int)$aspirante-&amp;gt;nivel_html;
        $nivel_css = (int)$aspirante-&amp;gt;nivel_css;
        $nivel_js = (int)$aspirante-&amp;gt;nivel_js;
        $nivel_php = (int)$aspirante-&amp;gt;nivel_php;
        $nivel_wp = (int)$aspirante-&amp;gt;nivel_wp;
        $total = $nivel_html + $nivel_css + $nivel_js + $nivel_php + $nivel_wp;
        echo "&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;a href='#' title='$motivacion'&amp;gt;$nombre&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$correo&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$nivel_html&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$nivel_css&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$nivel_js&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$nivel_php&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;$nivel_wp&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$total&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;";
    }
    echo '&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/div&amp;gt;';
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Guarda el código del plugin y selecciona de nuevo el menú &lt;strong&gt;Aspirante&lt;/strong&gt; , si todo ha ido bien deberías poder ver los datos recogidos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WcX0BDX2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/panel_admin_aspirantes-1024x343.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WcX0BDX2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/04/panel_admin_aspirantes-1024x343.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Para nota
&lt;/h4&gt;

&lt;p&gt;Las tablas del panel de administración se podría usar la clase &lt;strong&gt;WP_List_Table&lt;/strong&gt; , aunque hay que tomar ciertas precauciones. Hay un buen ejemplo en &lt;a href="https://clearintelligence.mx/wp-list-table-una-guia-paso-a-paso/"&gt;https://clearintelligence.mx/wp-list-table-una-guia-paso-a-paso/&lt;/a&gt; y otro en &lt;a href="https://www.smashingmagazine.com/2011/11/native-admin-tables-wordpress/"&gt;https://www.smashingmagazine.com/2011/11/native-admin-tables-wordpress/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflexiones finales
&lt;/h2&gt;

&lt;p&gt;Espero que hayas podido seguir este tutorial sin mayor problema y que tu formulario esté funcionando a la perfección, ¡me encantaría verlo en acción si me pasas la URL!&lt;/p&gt;

&lt;p&gt;Te animo a explorar otros temas a partir de lo que has aprendido con esta práctica. Piensa en algo que necesites resolver con WordPress desarrollando un plugin sencillo de este estilo e intenta solucionarlo.&lt;/p&gt;

&lt;p&gt;Si te quedas atascado en algún punto no dudes en preguntarme tus dudas y juntos podemos intentarlo. Si observas algún error o se te ocurre algo que mejorar tampoco te lo guardes.&lt;/p&gt;

&lt;p&gt;¡Espero impaciente tus comentarios!&lt;/p&gt;

&lt;h3&gt;
  
  
  Referencias
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kungfupress/kfp_formulario_aspirantes"&gt;Código completo en el repositorio de GitHub de KungFuPress&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://es.wikibooks.org/wiki/MySQL"&gt;Manual de MySQL en Wikilibros&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://es.wikipedia.org/wiki/SQL"&gt;SQL en Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Artículo: &lt;a href="https://igmoweb.com/2017/11/29/cross-site-request-forgery-dos-ejemplos-para-entenderlo/"&gt;“Cross-site Request Forgery: Dos ejemplos para entenderlo”&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.arsys.es/blog/programacion/generar-menus-admin-wordpress/"&gt;https://www.arsys.es/blog/programacion/generar-menus-admin-wordpress/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ejemplos de funciones para obtener la IP del visitante:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mvkoen.com/mostrar-la-direccion-ip-de-usuarios-y-visitantes-en-wordpress/"&gt;https://mvkoen.com/mostrar-la-direccion-ip-de-usuarios-y-visitantes-en-wordpress/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/6717926/function-to-get-user-ip-address"&gt;https://stackoverflow.com/questions/6717926/function-to-get-user-ip-address&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.wpbeginner.com/wp-tutorials/how-to-display-a-users-ip-address-in-wordpress/"&gt;https://www.wpbeginner.com/wp-tutorials/how-to-display-a-users-ip-address-in-wordpress/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Hooks y funciónes de WordPress utilizados:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Function_Reference/add_shortcode"&gt;add_shortcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Function_Reference/the_permalink"&gt;the_permalink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Function_Reference/register_activation_hook"&gt;register_activation_hook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/add_action/"&gt;add_action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Plugin_API/Action_Reference/admin_menu"&gt;admin_menu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/add_menu_page/"&gt;add_menu_page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/sanitize_text_field/"&gt;sanitize_text_field&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Function_Reference/is_email"&gt;is_email&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/esc_textarea/"&gt;esc_textarea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/reference/functions/get_the_permalink/"&gt;get_the_permalink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codex.wordpress.org/Function_Reference/register_activation_hook"&gt;register_activation_hook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;


</description>
      <category>plugins</category>
      <category>basededatos</category>
      <category>creartabla</category>
      <category>formularios</category>
    </item>
    <item>
      <title>¿Deberías quitar o limitar las revisiones de artículos en WordPress?</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Tue, 05 Mar 2019 17:37:40 +0000</pubDate>
      <link>https://forem.com/juananruiz/deberas-quitar-o-limitar-las-revisiones-de-artculos-en-wordpress-4n6g</link>
      <guid>https://forem.com/juananruiz/deberas-quitar-o-limitar-las-revisiones-de-artculos-en-wordpress-4n6g</guid>
      <description>

&lt;p&gt;Una de las cosas que los usuarios no perdonan en una web es la lentitud. El contenido podrá ser de mayor o menor calidad y el diseño será mas o menos atractivo, si encuentran algo de valor seguirán contigo. Pero una página web que carga lenta es insufrible, si a eso le agregas la manía que tiene Google porque las webs sean rápidas tienes una  tormenta perfecta.&lt;/p&gt;

&lt;p&gt;En WordPress una de las dimensiones críticas que hay que controlar es el tamaño de la base de datos. Cuando creas un artículo o página y vas haciendo modificaciones el propio sistema guarda automáticamente las distintas versiones en la base de datos. Esto en algunos casos es de gran ayuda porque te permite revisar y recuperar versiones antiguas del contenido, pero con el tiempo las revisiones se van acumulando en la base de datos, el tamaño de esta va creciendo y tu sitio comienza a relentizarse (en webs con pocos recursos incluso he visto que impedía la creación de nuevas entradas).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DFRRlZmq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/03/revisiones-entrada-baja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DFRRlZmq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/03/revisiones-entrada-baja.png" alt="Captura de pantalla de una comparación de revisiones de un artículo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por tanto es necesario buscar una solución de compromiso ante el dilema de mantener o no mantener revisiones. Si tienes un buen  plan de copias de seguridad y optas por optimizar la velocidad ante todo, puedes prescindir totalmente del sistema de revisiones. Si quieres una solución intermedia puedes al menos limitar el número de versiones que se guardan. &lt;/p&gt;

&lt;p&gt;Otra solución bastante conservadora, aunque supone un poco más trabajo, es utilizar un plugin como WP-Optimize que te permite comprobar el total de revisiones de tus artículos y tomar distintas medidas al respecto, este plugin también es muy útil porque permite localizar y borrar registros huérfanos de tu base de datos.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cómo deshabilitar las revisiones de artículos desde código&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Para ello necesitas modificar el archivo &lt;strong&gt;wp-config.php&lt;/strong&gt;. Lo primero es acceder a los ficheros de tu sitio web y hacer una copia de seguridad de este fichero por si accidentalmente modificas algo que no debieras.&lt;br&gt;&lt;br&gt;
Luego editas el fichero y más o menos debajo de la línea&lt;br&gt;&lt;br&gt;
$table_prefix = ‘wp_’;&lt;br&gt;&lt;br&gt;
pero siempre por encima de las líneas donde se define la constante “ABSPATH” añades la siguiente línea:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;define('WP\_POST\_REVISIONS', false);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Es importante que esté bien escrito, si al volver a la web te aparece un error o la temida pantalla en blanca, vuelve a revisar lo que has escrito, mira que las comillas estén bien, el punto y coma, etc. También es importante la posición dentro del fichero como te he comentado antes. Pero tampoco te asustes demasiado, si quieres ser implementador o desarrollador de WordPress te tienes que acostumbrar a estas cosas. Para quedarte más tranquilo puedes practicar antes en un blog de pruebas o en uno local. Y recuerda siempre sacar a priori una copia de seguridad del archivo wp-config.php por si la lías parda.&lt;/p&gt;

&lt;p&gt;Con esto has quitado las revisiones, pero WordPress seguirá haciendo el autoguardado automático del archivo, esto no ocupa lugar en la base de datos, pero, si quieres que no se haga con tanta frecuencia puedes aumentar el tiempo que viene establecido por defecto, que es de un minuto, a 3 o 5 minutos. Para ello encima de la línea anterior agrega esta otra:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;define('AUTOSAVE\_INTERVAL', 300); 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Con lo cual el autoguardado se define a 300 segundos, o sea, 5 minutos. Recuerda es importante que todo esto vaya encima de la línea del “Absolute path”.&lt;/p&gt;

&lt;p&gt;Guarda finalmente el fichero wp-config.php y verás que en el panel de publicación de los artículos ya no aparece la opción de revisiones.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cómo (NO) deshabilitar las revisiones de artículos con un plugin. ¡Cuidado lee hasta el final!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Resulta que buscando un plugin que hiciera esto, casi el único que encuentro es &lt;strong&gt;Disable Post Revision&lt;/strong&gt;  (&lt;a href="https://wordpress.org/plugins/disable-post-revision/"&gt;https://wordpress.org/plugins/disable-post-revision/&lt;/a&gt;) es un plugin muy ligero que sólo sirve para estos. Además ofrece la opción de deshabilitar las revisiones en función del tipo de contenido: páginas, entradas, medios, etc.  &lt;/p&gt;

&lt;p&gt;Cuando analizo un plugin o publico un fragmento de código en esta web no me conformo con buscar el primer plugin que encuentro, probarlo y tirar para adelante. Me gusta ver el código del plugin para ver lo que hace internamente, ve que repercusiones tiene sobre el sitio, sobre la base de datos, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lRxYHYh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/03/disable_post_revision.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lRxYHYh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kungfupress.com/wp-content/uploads/2019/03/disable_post_revision.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuál ha sido mi sorpresa cuando tras instalar este plugin, configurarlo y ver que funciona, me voy a la base de datos y veo que las revisiones se siguen grabando, aunque no se tiene acceso a ellas desde el panel de control. O sea el plugin te quita la ventaja de poder acceder a las revisiones y te sigue dejando el marrón permitir su almacenamiento en la base de datos ¡Que mal! ¡Ni se te ocurra instalarlo!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cómo quitar revisiones antiguas en un sitio existente con un plugin&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Si te toca revisar un sitio existente que no tenía ningún plugin de este tipo instalado previamente puede que te encuentres con cientos de revisiones de algunos contenidos que están relentizando la base de datos, las soluciones vistas hasta ahora no valen&lt;/p&gt;

&lt;p&gt;Para quitar las revisiones antiguas el plugin que brilla con luz propia es  &lt;strong&gt;WP-Optimize.&lt;/strong&gt; Antes de empezar a utilizar este plugin o cualquiera que se dedique a borrar contenidos de la base debes hacer &lt;strong&gt;sí o sí&lt;/strong&gt; una copia de seguridad de la base de datos. Es más, las primeras pruebas deberías hacerla en una web local con contenido de prueba que no te importe perder. Si aún no tienes ningún plugin de copias de seguridad instalado te recomiendo Updraft Plus (&lt;a href="https://updraftplus.com/"&gt;https://updraftplus.com/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Una vez instalado el plugin dentro de su menú de administración ve a la opción &lt;strong&gt;Base de datos,&lt;/strong&gt; marca la opción &lt;strong&gt;Limpiar todas las revisiones de entradas&lt;/strong&gt; y dale al botón &lt;strong&gt;Ejecutar optimización&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Como verás WP-Optimize tiene muchas más opciones, pero en este artículo no me quiero salir del tema que nos tocaba.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Como quitar revisiones antiguas desde código.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Para quitar revisiones antiguas desde código necesitas tener acceso a la base de datos de tu sitio WordPress y en ella teclear la siguiente sentencia SQL:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM wp\_posts WHERE post\_type = "revision"; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Resumen
&lt;/h2&gt;

&lt;p&gt;Quería escribir una simple entrada sobre cómo añadir una línea a tu fichero wp-config.php por si necesitas deshabilitar el mecanismo de almacenamiento de revisiones de WordPress. No es algo que te recomiende hacer por defecto porque en muchas ocasiones viene muy bien poder deshacer los cambios que hemos realizado en un artículo o recuperar información de una versión antigua de una página que pueda sernos útil.&lt;/p&gt;

&lt;p&gt;Sólo quiero que seas consciente de que todas estas revisiones van quedando almacenadas en la base de datos y si tienes muchas entradas y haces muchas revisiones en ciertos alojamientos podrían suponer una carga para el sistema.&lt;/p&gt;

&lt;p&gt;Por el camino me he encontrado un plugin que no hacía lo que decía y me he quedado un poco a cuadros porque es un plugin del repositorio oficial. Por favor si alguien lo usa y le funciona que me deje un comentario de la versión que está usando o que me diga que he podido hacer mal.&lt;/p&gt;


</description>
      <category>optimizacin</category>
      <category>basededatos</category>
      <category>wpconfig</category>
      <category>wpoptimize</category>
    </item>
    <item>
      <title>Deconstruyendo un tema de WordPress para entender como funciona</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Fri, 15 Feb 2019 12:18:10 +0000</pubDate>
      <link>https://forem.com/juananruiz/deconstruyendo-un-tema-de-wordpress-para-entender-como-funciona-2b38</link>
      <guid>https://forem.com/juananruiz/deconstruyendo-un-tema-de-wordpress-para-entender-como-funciona-2b38</guid>
      <description>&lt;p&gt;Desde pequeñito me gustaba desmontar las cosas para ver cómo funcionaban, recuerdo que con 5 o 6 años a mi madre se le estropeó el despertador de la mesita de noche. Era un despertador muy moderno, de plástico amarillo y dos campanas que sonaban con un pequeño martillo que había en medio. El despertador hubo que tirarlo a la basura, pero yo quedé extasiado de  haber visto sus tripas y estuve toda la tarde manipulando los engranajes.&lt;/p&gt;

&lt;p&gt;Con los sistemas informáticos me pasa lo mismo: me gusta mirar dentro para ver como funcionan. Al principio, como no tengo ni idea me pongo a sacar y meter piezas para saber qué es lo importante y que no lo es tanto.&lt;/p&gt;

&lt;p&gt;Para esta práctica te recomiendo instalar el tema TwentyTwelve de WordPress. Vas a destrozar el tema usando el canto de tus dedos. &lt;strong&gt;¡Yiaaaah!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparando el tema de prueba
&lt;/h2&gt;

&lt;p&gt;Para empezar vas a descubrir como crear un tema en menos de 10 segundos. Instala el tema TwentyTwelve, desde un navegador de archivos, busca la carpeta del tema dentro de &lt;strong&gt;wp_content/themes&lt;/strong&gt; y hazle una copia con otro nombre en el mismo directorio. Abre el fichero &lt;strong&gt;style.css&lt;/strong&gt; que hay dentro de la carpeta que has copiado y renombrado, y cambia en la segunda línea el nombre de “TwentyTwelve” por otra cosa, en mi caso le voy a poner “TwentyKungFuPress”. Ve al panel de control, abre &lt;strong&gt;Apariencia&lt;/strong&gt; &amp;gt; &lt;strong&gt;Temas.&lt;/strong&gt; ¡Ya tienes un tema nuevo!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S2k54kDA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kungfupress.com/wp-content/uploads/2019/02/tema-duplicado-1024x472.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S2k54kDA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kungfupress.com/wp-content/uploads/2019/02/tema-duplicado-1024x472.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora vamos a hacer algo parecido a lo que hice con el reloj. Crea una carpeta vacía dentro de &lt;strong&gt;wp_content/themes&lt;/strong&gt; , puedes llamarla sala-de-espera. Ve a la carpeta donde están todos los archivos del tema TwentyKungFuPress o como lo hayas llamado y mueve todos los archivos y directorios que contiene a la carpeta que acabas de crear.  Ve a la pantalla de administración de temas a ver qué ha pasado. Seguramente tendrás un mensaje de que el tema actual está dañado y WP ha vuelto a un tema por defecto para no dejar a tus visitantes en blanco.&lt;/p&gt;

&lt;p&gt;Como lo mínimo que necesita un tema para funcionar son los ficheros &lt;strong&gt;index.php&lt;/strong&gt; y &lt;strong&gt;style.css&lt;/strong&gt; , traelos de nuevo desde la sala de espera y activa de nuevo el tema. Ahora el tema no dará errores pero cuando vayas a ver el sitio encontrarás un error parecido a este: &lt;em&gt;“Fatal error: Uncaught Error: Call to undefined function twentytwelve_content_nav() in /app/public/wp-content/themes…”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Esto es debido a la llamada de funciones incorporadas al tema, que no pueden cargarse, seguramente debido a que no hemos incluido el archivo &lt;strong&gt;functions.php&lt;/strong&gt; que es donde por convenio deberían estar, podríamos incluir este fichero o ir quitando del &lt;strong&gt;index.php&lt;/strong&gt; las llamadas a las funciones que vayan fallando. Como el tema que hemos escogido de ejemplo está bien hecho es bastante fácil identificarlas porque todas empiezan por &lt;strong&gt;twentytwelve.&lt;/strong&gt; De momento sólo encontraremos una o dos, así que podemos simplemente comentarlas. Vuelve a cargar tu sitio y ahora deberías poder ver la cabecera, el pie e incluso la barra lateral. ¡El tema está funcionando! &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DvXCaIjD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/11/72x72/1f642.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DvXCaIjD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/11/72x72/1f642.png" alt="🙂"&gt;&lt;/a&gt; Pero falta el contenido &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N109eHMv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/11/72x72/1f641.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N109eHMv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s.w.org/images/core/emoji/11/72x72/1f641.png" alt="🙁"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esto se debe con toda probabilidad a que falta un archivo llamado &lt;strong&gt;content.php&lt;/strong&gt; que es el encargado de mostrar el contenido de cada entrada o página, en TwentyTwelve lo vas a encontrar directamente en la carpeta raiz, en temas más modernos puede estar un poco más escondido, por ejemplo en &lt;strong&gt;template-parts/post/content.php&lt;/strong&gt; o en &lt;strong&gt;template-parts/content/content.php&lt;/strong&gt;. Bueno, ¿sabes ya lo que tienes que hacer? Copia el archivo a su carpeta correspondiente y ahora deberías poder ver el contenido.&lt;/p&gt;

&lt;p&gt;Puede que de nuevo aparezcan errores de llamadas a funciones, pues nada, ve a &lt;strong&gt;content.php y&lt;/strong&gt; comenta las llamadas a las mismas. Si todo ha ido bien ahora deberías poder ver el contenido, la barra lateral, etc.&lt;/p&gt;

&lt;p&gt;¡Pero es que todo se ve muy feo! Si, es verdad, así que sigue avanzando, copia ahora &lt;strong&gt;footer.php&lt;/strong&gt; , &lt;strong&gt;functions.php&lt;/strong&gt; y &lt;strong&gt;header.php&lt;/strong&gt; desde la sala de espera, abre de nuevo el navegador y todo tu esfuerzo se verá recompensado con un error que comienza con &lt;em&gt;“Warning: require(/app/public/wp-content/themes/twentykungfupress/inc/custom-header.php): failed to open stream …”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;¡Que ingrato!, quiere más ficheros, se queja de uno que está en la carpeta &lt;strong&gt;inc&lt;/strong&gt; así que para no escucharlo más copia la carpeta completa a tu tema (son dos o tres archivos solo).&lt;/p&gt;

&lt;p&gt;¡Ahora, ahora! Esto ya se parece a un tema, sólo lo afea el contenido de la barra lateral, marchando ese &lt;strong&gt;sidebar.php&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Y ya que hemos agregado &lt;strong&gt;functions.php&lt;/strong&gt; podemos quitar los comentarios que pusimos en &lt;strong&gt;index.php&lt;/strong&gt; y en &lt;strong&gt;content.php&lt;/strong&gt;. Verás que ahora se muestra la fecha de las entradas y alguna cosilla más que estaban a cargo de funciones definidas en &lt;strong&gt;function.php.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si entras en una página o en una entrada (post), verás que parece funcionar correctamente, pero si observas bien verás que no aparece la caja para hacer comentarios, así que agrega también el fichero &lt;strong&gt;comment.php.&lt;/strong&gt; Supongo que esto no ha arreglado nada ¿porqué?&lt;/p&gt;

&lt;p&gt;Porque en realidad hemos forzado a WP a mostrar todo el contenido del sitio (la portada, las entradas, las páginas, las búsquedas, las categorías, etc) utilizando una sola plantilla, que es &lt;strong&gt;index.php&lt;/strong&gt; pero la mayoría de los temas vienen con páginas dedicadas a cada uno de los tipos de contenido que hemos mencionado, así:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;las entradas completas se muestran con &lt;strong&gt;single.php&lt;/strong&gt; , &lt;/li&gt;
&lt;li&gt;las páginas con &lt;strong&gt;page.php&lt;/strong&gt; ,&lt;/li&gt;
&lt;li&gt;las búsquedas con &lt;strong&gt;search.php&lt;/strong&gt; ,&lt;/li&gt;
&lt;li&gt;las categorías con &lt;strong&gt;category.php&lt;/strong&gt; , etc &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aquí tienes un esquema simplificado de la jerarquía de plantillas de WP para que veas de una manera gráfica esto que te cuento, si quieres una versión completa y más actualizada visita &lt;a href="https://wphierarchy.com/"&gt;WP Hierarchy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M9wo0jDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kungfupress.com/wp-content/uploads/2019/02/jerarquia_plantillas_wordpress-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M9wo0jDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kungfupress.com/wp-content/uploads/2019/02/jerarquia_plantillas_wordpress-1.jpg" alt="Jerarquía de plantillas en WordPress donde se ve que al final todo se puede mostrar con index.php"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si ahora copias &lt;strong&gt;single.php&lt;/strong&gt; verás que los comentarios ya aparecen en las entradas, pero ¿cómo puedes realmente saber que plantilla utiliza WP en cada momento? Hay un simpático plugin que lo muestra en la barra superior que aparece cuando has iniciado sesión en el blog, es el plugin &lt;a href="https://es.wordpress.org/plugins/show-current-template/"&gt;Show Current Template&lt;/a&gt; que muestra la plantilla de la vista actual .Si pulsas sobre el nombre de la plantilla también da información sobre la ruta de la plantilla y las sub-plantillas que se están utilizando.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RZ-gksvS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kungfupress.com/wp-content/uploads/2019/02/show-current-template-plugin.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RZ-gksvS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kungfupress.com/wp-content/uploads/2019/02/show-current-template-plugin.jpg" alt="El plugin Show Current Template en acción mostrando la ruta de plantilla y los archivos de plantilla"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instálalo desde el panel de control y ve agregando el resto de plantillas que le hemos quitado al tema: &lt;strong&gt;page.php&lt;/strong&gt; , &lt;strong&gt;search.php&lt;/strong&gt; , &lt;strong&gt;category.php&lt;/strong&gt; , &lt;strong&gt;archive.php&lt;/strong&gt; , etc. Ve mirando ahora como se cargan las entradas, las páginas, las categorías, etc. Así podrás comprobar que aunque a WP le gusta tener muchas plantillas al final se conforma con una sola: *&lt;em&gt;index.php *&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cuál es la moraleja de todo este destrozo que hemos hecho?
&lt;/h2&gt;

&lt;p&gt;Como desarrollador WordPress (o al menos como aprendiz de Kung Fu Press) debes perder ese miedo a tocar y romper cosas que tienen los usuarios menos avezados. Es normal que durante el desarrollo de un tema te dejes abierta una llamada a PHP, olvides poner un punto y coma al final de una línea, llames incorrectamente a una función o hayas borrado por accidente algún fichero. Tienes que acostumbrarte a enfrentar estas situaciones y resolverlas, puede que incluso algún cliente o amigo te venga con una instalación dañada de WP que no se atreve a tocar para no hacer más destrozos. Te va a tocar a ti recoger los cachitos y pegarlos de nuevo para que todo funcione. Y si ya tienes práctica actuarás con más seguridad y sangre fría.&lt;/p&gt;

&lt;p&gt;Recuerda no hacer estas prácticas extremas en un sistema en producción, usa instalaciones locales de prueba donde no tengas nada que perder. Si te enfrentas a algo parecido en la vida real recuerda antes que nada hacer una copia de seguridad de todos los ficheros y de la base de datos. Si el sitio no está respondiendo no te valdrá ningún plugin así que tendrás que saber como hacer copias de seguridad de WP sin usar plugins.&lt;/p&gt;

</description>
      <category>temas</category>
      <category>twentytwelve</category>
    </item>
    <item>
      <title>Multiplica tu velocidad en Visual Studio Code utilizando Emmet</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Mon, 04 Feb 2019 20:10:49 +0000</pubDate>
      <link>https://forem.com/juananruiz/multiplica-tu-velocidad-en-visual-studio-code-utilizando-emmet-44h5</link>
      <guid>https://forem.com/juananruiz/multiplica-tu-velocidad-en-visual-studio-code-utilizando-emmet-44h5</guid>
      <description>&lt;h2&gt;
  
  
  ¿Qué es Emmet?
&lt;/h2&gt;

&lt;p&gt;Emmet es un pequeño pero potente lenguaje de autocompletado de código que está disponible en prácticamente todos los editores que existen. ¡Incluso hay un plugin para usarlo en WordPress!&lt;/p&gt;

&lt;p&gt;En Visual Studio Code, Emmet viene instalado por defecto, así que para usarlo sólo tenemos que aprender su sencilla sintáxis.&lt;/p&gt;

&lt;p&gt;Para mi uno de los usos más interesantes que tiene es el de autocompletado de HTML y puede usarse para autocompletar etiquetas a medida que las vamos escribiendo o para formatear texto ya existente. También vale para CSS .&lt;/p&gt;

&lt;p&gt;Imagina que un cliente te pasa todo el texto para colocar en su web en un fichero de Word, algo bastante típico ¿no?. Tienes dos opciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intentar copiar el texto con el formato enriquecido que trae Word&lt;/li&gt;
&lt;li&gt;Copiar el texto y pegarlo en un editor de texto plano&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En la primera opción puede que consigas traerte los títulos, las negritas y quizás alguna lista. Pero puede que los títulos no vengan con las etiquetas que tu necesitas y que el resto venga todo infectado con etiquetas inútiles tipo span, div, etc.&lt;/p&gt;

&lt;p&gt;La segunda opción parece que supone más trabajo: poner todas las etiquetas a mano, e irlas cerrando y abriendo correctamente es una lata. Pero vas a controlar mejor tu trabajo y además puedes acelerarlo de una manera brutal utilizando Emmet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emmet para generar nuevo contenido
&lt;/h2&gt;

&lt;p&gt;En muchas ocasiones tendrás que escribir código HTML a pelo, partiendo desde cero, es una lata tener que abrir etiquetas, escribir texto, cerrarlas, etc. Con Emmet escribir HTML se convierte en un placer.  &lt;/p&gt;

&lt;p&gt;Creo que es más fácil aprender Emmet empezando con un documento en blanco, así que vamos a empezar por ahí.&lt;/p&gt;

&lt;p&gt;Aunque puedes consultar toda la documentación sobre Emmet en su propia &lt;a href="https://docs.emmet.io/" rel="noopener noreferrer"&gt;página web&lt;/a&gt;, te cuento aquí lo más básico y algunos trucos interesantes para que te entre el gusanillo de probarlo.&lt;/p&gt;

&lt;p&gt;Emmet utiliza abreviaturas para ir generando elementos, en este tutorial veremos sólo HTML, aunque también se lleva muy bien con CSS. Las abreviaturas son muy sencillas, si escribes &lt;strong&gt;p&lt;/strong&gt; y pulsas tabulador te va a crear las etiquetas de apertura y cierre de párrafo. Esto por si sólo no tiene demasiada gracia, pero si escribes &lt;strong&gt;ul&amp;gt;li*5&lt;/strong&gt; y das a tabulador te genera una lista no numerada con cinco elementos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F02%2Femmet1-2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F02%2Femmet1-2.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prueba lo que te digo antes de seguir y ahora vemos cosas más interesantes.&lt;/p&gt;

&lt;p&gt;Ahora vas a montar un menú de navegación en un periquete sin tener que preocuparte de abrir y cerrar etiquetas, copiar y pegar líneas, etc. Prueba con esto: &lt;strong&gt;ul.navbar&amp;gt;(li.nav&amp;gt;a.menuitem)*5&lt;/strong&gt; y pulsas tabulador, verás que te genera un código en el que sólo tienes que ir escribiendo el texto que te falta e ir usando el tabulador para terminar de rellenar toda la información.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F02%2Femmet2-2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F02%2Femmet2-2.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El punto se utiliza para indicar una clase para la etiqueta que la precede, de la misma manera puedes usar la almohadilla &lt;strong&gt;#&lt;/strong&gt; para crear un &lt;strong&gt;id&lt;/strong&gt;. Cómo ves la sintáxis viene heredada de CSS así que te saldrá de manera muy natural.&lt;/p&gt;

&lt;p&gt;Por cierto cuando Visual Studio Code detecta que estás usando Emmet te sale un pequeño círculo con una i para que puedas ver como va a quedar tu código, es muy útil hasta que domines el lenguaje para no tener que estar borrando todo el tiempo código fallido.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teclea &lt;strong&gt;html:5&lt;/strong&gt; y dale al tabulador.&lt;/li&gt;
&lt;li&gt;Introduce &lt;strong&gt;form&amp;gt;(label&amp;gt;input:text)*4&lt;/strong&gt; y tendrás un formulario con 4 campos con etiquetas, clases, etc.&lt;/li&gt;
&lt;li&gt;Teclea &lt;strong&gt;p*5&amp;gt;lorem&lt;/strong&gt; y tendrás cinco párrafos de texto para rellenar tus diseños de prueba.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los ejemplos serían infinitos, te invito a probar por ti mismo e ir consultando la documentación de Emmet cuando tengas alguna duda. Te animo a intentar escribir una tabla de 10 filas por 5 columnas, o a escribir 3 parráfos cada uno con su correspondiente cabecera &lt;strong&gt;h2&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Emmet para contenido existente
&lt;/h2&gt;

&lt;p&gt;Tal como te comenté al principio de este artículo, cuando mas brilla Emmet es a la hora de formatear en HTML texto plano al que tienes que dar formato: encabezados, párrafos, listas, enlaces, negritas, etc.&lt;/p&gt;

&lt;p&gt;Lo primero que te recomiendo es activar dos atajos de teclado para poder llamar a Emmet desde Visual Studio Code sin tener que tirar de ratón. Para ello ve al menú &lt;strong&gt;Preferencias&lt;/strong&gt; y luego &lt;strong&gt;Métodos abreviados de teclado&lt;/strong&gt; , ahora teclea “Encapsular” en el buscador de comandos y te aparecen dos entradas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encapsular con abreviatura&lt;/li&gt;
&lt;li&gt;Encapsular las líneas individuales con abreviatura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Asigna una combinación de teclas abreviadas a cada una de ellas (yo he usado Comando+M y ALt+Comando+M en el Mac porque las combinaciones con “E” ya están cogidas para las búsquedas Ahora abre un texto plano que tengas que formatear, o mejor, para seguir bien el ejemplo, teclea primero:&lt;br&gt;&lt;br&gt;
 &lt;strong&gt;lorem6+lorem2+lorem5*5&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
 y luego pulsas el tabulador.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F02%2Femmet3-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F02%2Femmet3-1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y ahora vamos a aplicarle formato utilizando Emmet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selecciona la primera línea, usa tu atajo para “Encapsular con abreviatura” y escribe &lt;strong&gt;h2&lt;/strong&gt; y pulsas Intro.&lt;/li&gt;
&lt;li&gt;Selecciona los dos párrafos siguientes, usa el atajo de nuevo y escribe &lt;strong&gt;p*&lt;/strong&gt; (más Intro, claro). Esto asigna un párrafo por cada salto de línea que encuentra (si no pones el asterisco te lo coloca todo en un sólo párrafo).&lt;/li&gt;
&lt;li&gt;Ahora selecciona una pálabra cualquiera del texto, haciendo doble clic sobre ella y utiliza el otro atajo para poner una negrita (usando &lt;strong&gt;b&lt;/strong&gt; o &lt;strong&gt;strong&lt;/strong&gt; )&lt;/li&gt;
&lt;li&gt;Elige y selecciona dos o tres palabras seguidas y con el mismo atajo teclea &lt;strong&gt;a&lt;/strong&gt; para crear un enlace.&lt;/li&gt;
&lt;li&gt;Ahora ve a por las 5 líneas más cortas, selecciona con el primer atajo y escribe &lt;strong&gt;ul&amp;gt;li*&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Puede que te resulte un poquillo engorroso al principio, pero recuerda que puedes usar todas las etiquetas de HTML, definir clases o identificadores para las etiquetas que lo necesiten e incluso puedes definir tus propios &lt;strong&gt;snippets&lt;/strong&gt; personalizados para estructuras que uses con frecuencia, al estilo de html:5&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Si tienes alguna duda o descubres alguna combinación chula de Emmet me encantaría que dejaras un comentario más abajo.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;p&gt;Si quieres seguir aprendiendo sobre el tema te dejo algunas de las referencias en que me he basado para escribir este artículo o que me han parecido interesantes. La primera es la propia documentación oficial de Emmet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.emmet.io/" rel="noopener noreferrer"&gt;Documentación oficial de Emmet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://efficientuser.com/2017/11/12/emmet-in-vs-code/" rel="noopener noreferrer"&gt;“Emmet in VS Code” por Efficient User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sridharkatakam.com/set-wordpress-coding-standards-visual-studio-code/" rel="noopener noreferrer"&gt;How to Set Up WordPress Coding Standards in Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://deliciousbrains.com/vs-code-wordpress/" rel="noopener noreferrer"&gt;Using VS Code for WordPress Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tommcfarlin.com/vs-code-wordpress/" rel="noopener noreferrer"&gt;VS Code for WordPress Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.tenseg.net/blog/2017/02/24/docker-and-visual-studio-code-for-local-wordpress-development/" rel="noopener noreferrer"&gt;Docker and Visual Studio Code for Local WordPress Development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>herramientas</category>
      <category>emmet</category>
      <category>productividad</category>
      <category>visualstudiocode</category>
    </item>
    <item>
      <title>Cómo crear tipos  y campos personalizados en WordPress</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Thu, 31 Jan 2019 20:07:11 +0000</pubDate>
      <link>https://forem.com/juananruiz/cmo-crear-tipos--y-campos-personalizados-en-wordpress-1f5c</link>
      <guid>https://forem.com/juananruiz/cmo-crear-tipos--y-campos-personalizados-en-wordpress-1f5c</guid>
      <description>&lt;p&gt;Siguiendo este tutorial verás cómo crear un plugin de WordPress para convertir tu web en algo más que un blog. Vas a crear tipos de contenido personalizado, en inglés “Custom Posts Type” o “CPT” que te permitirán convertir tu página en un recetario de cocinas, un gestor de tu colección de libros, películas o sellos de correo.&lt;/p&gt;

&lt;p&gt;Y lo harás todo desde código, sin utilizar ningún plugin pues esta es la mejor manera de aprender como funciona realmente WordPress y como programar aplicaciones completas con él. Más adelante en algún proyecto quizás necesites por cuestiones de tiempo o presupuesto utilizar algún plugin para crear campos personalizados como &lt;strong&gt;Advanced Custom Fields&lt;/strong&gt; pero tu ya sabrás como funciona internamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requisitos previos
&lt;/h2&gt;

&lt;p&gt;Para enfrentar este tutorial te recomiendo un conocimiento medio o avanzado de WordPress y unos conocimientos mínimos de PHP.&lt;/p&gt;

&lt;p&gt;Empezarás por lo básico creando el esqueleto del plugin con lo mínimo para que funcione todo correctamente. En tutoriales más avanzados aprenderás a crear plugins utilizando un esqueleto algo más complejo pero más estandarizado. Si ya eres desarrollador de WordPress quizás eches en falta definir el plugin dentro de un objeto, separar en distintos archivos las distintas partes del plugin y otras buenas prácticas que no he utilizado aquí a propósito, porque me parece más didáctico este planteamiento. En sucesivos tutoriales iré incorporando todas esas cuestiones.&lt;/p&gt;

&lt;p&gt;Así que busca un sitio tranquilo para realizar tu práctica, con un tiempo delimitado durante el que nada te moleste, cíñete el cinturón, respira hondo y saluda a tu adversario antes de comenzar el combate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comienza a crear el plugin
&lt;/h2&gt;

&lt;p&gt;Parte de una instalación básica y limpia, con un tema cualquiera, aunque si quieres que tenga apariencia de recetario te recomiendo uno de estos dos que son gratuitos y del repositorio oficial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://wordpress.com/theme/baskerville-2" rel="noopener noreferrer"&gt;Baskerville 2&lt;/a&gt;, Anders Norén&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wordpress.com/theme/dyad-2" rel="noopener noreferrer"&gt;Dyad 2&lt;/a&gt;, Automattic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Fdyad-2-theme.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Fdyad-2-theme.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Con tu editor de código favorito crea una nueva carpeta dentro de la ruta &lt;code&gt;wp-content/plugins&lt;/code&gt;, la puedes llamar &lt;strong&gt;recetario&lt;/strong&gt; o algo más acorde a lo que quieras hacer.&lt;/p&gt;

&lt;p&gt;Dentro de esa carpeta crea un archivo y nómbralo &lt;strong&gt;recetario.php&lt;/strong&gt; , este será el punto de partida de tu plugin. Es muy importante escribir correctamente la cabecera de este artículo para que WP lo identifique como un plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;lt;?php
/\*
Plugin Name: Recetario
Author Name: Juanan Ruiz
Plugin URI: https://kungfupress.com/como-crear-tipos-y-campos-personalizados-en-wordpress
Description: Un plugin para aprender a programar WP practicando con un gestor de recetas.
\*/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La única línea realmente imprescindible es la del &lt;strong&gt;Plugin Name&lt;/strong&gt; , el resto es información adicional, útil pero no imprescindible a nivel de funcionamiento interno. Que no se te olvide tampoco la primera línea con la etiqueta &lt;strong&gt;&amp;lt;?php&lt;/strong&gt; , si olvidas ponerla el contenido del plugin o una parte de él se verá por toda tu web (si no pasa algo peor).&lt;/p&gt;

&lt;p&gt;Si ahora guardas el fichero y vas al listado de plugins desde el panel de administración de tu sitio WP verás tu nuevo plugin esperando y listo para ser activado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Factivacion_plugin_recetario-1024x228.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Factivacion_plugin_recetario-1024x228.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creando un Custom Post Type o Campo Personalizado
&lt;/h2&gt;

&lt;p&gt;Este pobre plugin aún no sabe hacer nada, así que vas a comenzar a definir el Custom Post Type (CPT). Para ello puedes copiarlo de otro que ya tengas hecho, del código que tienes más abajo o generarlo desde la web &lt;a href="https://generatewp.com/post-type/" rel="noopener noreferrer"&gt;GenerateWP&lt;/a&gt;. Para ello vas recorriendo las pestañas que aparecen en la página: General, Post Type, Labels, etc y rellenando los campos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Fherramienta_generate_wp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Fherramienta_generate_wp.jpg" alt="Herramienta GenerateWP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Empieza por la pestaña &lt;strong&gt;General&lt;/strong&gt; y rellena &lt;strong&gt;Function Name&lt;/strong&gt; con: &lt;code&gt;receta_post_type&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Este es el nombre de la función con la que vas a definir el CPT&lt;/p&gt;

&lt;p&gt;Luego ve a la pestaña &lt;strong&gt;Post Type&lt;/strong&gt; y rellena los siguientes campos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post Type Key: receta&lt;/li&gt;
&lt;li&gt;Name (Singular): Receta&lt;/li&gt;
&lt;li&gt;Description: Receta&lt;/li&gt;
&lt;li&gt;Name (Plural): Recetas&lt;/li&gt;
&lt;li&gt;Hierarchical: No&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En la pestaña &lt;strong&gt;Labels&lt;/strong&gt; se pueden configurar un montón de etiquetas, rellena sólo estas dos de momento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Menu name: Recetas&lt;/li&gt;
&lt;li&gt;Admin Bar Name: Receta&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por último en la pestaña &lt;strong&gt;Options&lt;/strong&gt; marca la casilla &lt;strong&gt;Custom Fields&lt;/strong&gt; y para terminar pulsa el botón &lt;strong&gt;Update Code&lt;/strong&gt; que hay un poco más abajo, copia el código generado en tu plugin y te quedará algo parecido a esto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;lt;?php
/\*
Plugin Name: Recetario
...
\*/
​
// Register Custom Post Type
function receta\_post\_type() {
 $labels = array(
 'name' =\&amp;gt; \_x( 'Recetas', 'Post Type General Name', 'text\_domain' ),
 'singular\_name' =\&amp;gt; \_x( 'Receta', 'Post Type Singular Name', 'text\_domain' ),
 'menu\_name' =\&amp;gt; \_\_( 'Recetas', 'text\_domain' ),
 'name\_admin\_bar' =\&amp;gt; \_\_( 'Receta', 'text\_domain' ),
 'archives' =\&amp;gt; \_\_( 'Item Archives', 'text\_domain' ),
 'attributes' =\&amp;gt; \_\_( 'Item Attributes', 'text\_domain' ),
 'parent\_item\_colon' =\&amp;gt; \_\_( 'Parent Item:', 'text\_domain' ),
 'all\_items' =\&amp;gt; \_\_( 'All Items', 'text\_domain' ),
 'add\_new\_item'          =\&amp;gt; \_\_( 'Add New Item', 'text\_domain' ),
 'add\_new' =\&amp;gt; \_\_( 'Add New', 'text\_domain' ),
 'new\_item' =\&amp;gt; \_\_( 'New Item', 'text\_domain' ),
 'edit\_item' =\&amp;gt; \_\_( 'Edit Item', 'text\_domain' ),
 'update\_item' =\&amp;gt; \_\_( 'Update Item', 'text\_domain' ),
 'view\_item' =\&amp;gt; \_\_( 'View Item', 'text\_domain' ),
 'view\_items' =\&amp;gt; \_\_( 'View Items', 'text\_domain' ),
 'search\_items' =\&amp;gt; \_\_( 'Search Item', 'text\_domain' ),
 'not\_found' =\&amp;gt; \_\_( 'Not found', 'text\_domain' ),
 'not\_found\_in\_trash' =\&amp;gt; \_\_( 'Not found in Trash', 'text\_domain' ),
 'featured\_image' =\&amp;gt; \_\_( 'Featured Image', 'text\_domain' ),
 'set\_featured\_image'=\&amp;gt; \_\_( 'Set featured image', 'text\_domain' ),
 'remove\_featured\_image' =\&amp;gt; \_\_( 'Remove featured image', 'text\_domain' ),
 'use\_featured\_image' =\&amp;gt; \_\_( 'Use as featured image', 'text\_domain' ),
 'insert\_into\_item' =\&amp;gt; \_\_( 'Insert into item', 'text\_domain' ),
 'uploaded\_to\_this\_item' =\&amp;gt; \_\_( 'Uploaded to this item', 'text\_domain' ),
 'items\_list' =\&amp;gt; \_\_( 'Items list', 'text\_domain' ),
 'items\_list\_navigation' =\&amp;gt; \_\_( 'Items list navigation', 'text\_domain' ),
 'filter\_items\_list' =\&amp;gt; \_\_( 'Filter items list', 'text\_domain' ),
 );
 $args = array(
 'label'                 =\&amp;gt; \_\_( 'Receta', 'text\_domain' ),
 'description'           =\&amp;gt; \_\_( 'Receta', 'text\_domain' ),
 'labels'                =\&amp;gt; $labels,
 'supports'              =\&amp;gt; array( 'title', 'editor', 'thumbnail', 'comments', 'revisions', 'custom-fields' ),
 'hierarchical'          =\&amp;gt; false,
 'public'                =\&amp;gt; true,
 'show\_ui'               =\&amp;gt; true,
 'show\_in\_menu'          =\&amp;gt; true,
 'menu\_position'         =\&amp;gt; 5,
 'show\_in\_admin\_bar'     =\&amp;gt; true,
 'show\_in\_nav\_menus'     =\&amp;gt; true,
 'can\_export'            =\&amp;gt; true,
 'has\_archive'           =\&amp;gt; true,
 'exclude\_from\_search'   =\&amp;gt; false,
 'publicly\_queryable'    =\&amp;gt; true,
 'capability\_type'       =\&amp;gt; 'page',
 );
 register\_post\_type( 'receta', $args );
}
add\_action( 'init', 'receta\_post\_type', 0 );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ¿Cómo se ha definido el CPT con este código?
&lt;/h2&gt;

&lt;p&gt;Primero se ha creado un array llamado &lt;strong&gt;$labels&lt;/strong&gt; que contiene las etiquetas para nombrar los diferentes elementos y acciones del nuevo CPT.&lt;/p&gt;

&lt;p&gt;Luego se ha definido otro array &lt;strong&gt;$args&lt;/strong&gt; , que a su vez ha incluido a &lt;strong&gt;$labels&lt;/strong&gt; , y en este segundo array se pasan todos los parametros para configurar el comportamiento del CPT. Cosas como si va a ser público, si va a aparecer en el menú de administración, si se va a comportar como una página o como una entrada, si los contenidos se van a incluir en las búsquedas del sitio, etc.&lt;/p&gt;

&lt;p&gt;Una vez definido este segundo array se llama a la función &lt;strong&gt;register_post_type&lt;/strong&gt; que requiere dos parámetros, uno con el nombre clave del CPT y otro con el nombre del array de argumentos.&lt;/p&gt;

&lt;p&gt;Por último, fuera ya de la función se engancha esta función que define al CPT al hook &lt;strong&gt;add_action(init)&lt;/strong&gt; que es el que se invoca cuando se carga el plugin&lt;/p&gt;

&lt;p&gt;Guarda ahora el archivo y ve de nuevo al listado de plugins en el panel de administración. Busca el plugin y ahora puedes darle a &lt;strong&gt;activar&lt;/strong&gt;. Si todo ha ido bien en el menú de la izquierda aparecerá un nuevo elemento con el nombre “Recetas”. Si haces clic en &lt;strong&gt;Add New&lt;/strong&gt; verás que aparece algo parecido a una nueva entrada de blog.&lt;/p&gt;

&lt;p&gt;¡Felicidades por haber llegado hasta aquí! Has creado un nuevo tipo de contenido para WordPress, no muchos lo han hecho.&lt;/p&gt;

&lt;p&gt;Verás que hay algunos títulos y nombres de menú sin actualizar, abre de nuevo el archivo del plugin y traduce los elementos que estimes necesarios, por ejemplo &lt;strong&gt;All Items&lt;/strong&gt; , &lt;strong&gt;Add New&lt;/strong&gt; , &lt;strong&gt;Add New Item&lt;/strong&gt; , etc. La próxima vez esto puedes hacerlo directamente editando las &lt;strong&gt;Labels&lt;/strong&gt; en el &lt;strong&gt;WordPress Type Generator&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los campos personalizados
&lt;/h2&gt;

&lt;p&gt;Llegamos a un punto crítico.&lt;/p&gt;

&lt;p&gt;Un tipo de contenido personalizado tiene poco sentido si no tiene también asociados algunos campos personalizados. En este ejemplo estaría bien contar con un campo para ingredientes, otro para instrucciones, tiempo de preparación, comensales, tipo de cocina, etc. A esto se le llama “Custom Fields”, se pueden crear a mano dentro de cada receta pero hay que crearlo cada vez y es muy engorroso.&lt;/p&gt;

&lt;p&gt;Existe un plugin fantástico para hacer esto &lt;a href="https://www.advancedcustomfields.com/" rel="noopener noreferrer"&gt;Advanced Custom Fields&lt;/a&gt; (ACF), está genial y te recomiendo tenerlo en tu caja de herramientas . Pero la idea de este tutorial es ir más alla y crear también los Campos Personalizados desde código, este es un sendero poco transitado pero que te recomiendo recorrer si quieres convertirte en un verdadero guerrero de WordPress.&lt;/p&gt;

&lt;p&gt;Así que a partir de aquí tienes dos opciones, utilizar la vía rápida del plugin ACF o seguir aprendiendo a programar como un auténtico practicante de la disciplina.&lt;/p&gt;

&lt;p&gt;¿Sigues conmigo? ¡Bravo! Este camino es duro pero te garantiza grandes satisfacciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agregando meta boxes.
&lt;/h2&gt;

&lt;p&gt;Los &lt;strong&gt;meta boxes&lt;/strong&gt; son todas esas cajas de formularios que rodean normalmente el contenido de un artículo o de una página cuando estás utilizando el editor de WP. Aquí los vas a utilizar para colocar en ellos los elementos de formulario donde recogerás tus campos personalizados.&lt;/p&gt;

&lt;p&gt;Como no podía ser menos WordPress tiene funciones para que puedas crear tus propias meta boxes. Dentro de ellas se puede colocar cualquier tipo de información: texto, imágenes, etc y también se pueden introducir campos de formulario para recoger valores.&lt;/p&gt;

&lt;p&gt;Empieza agregando al plugin una función parar registrar una meta box con un par de campos personalizados, luego podrás añadir más. Dentro de esa función vas a llamar a la función &lt;strong&gt;add_meta_box&lt;/strong&gt; de WordPress.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;add_meta_box($id, $title, $callback, $screen, $context, $priority, $callback_args);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$id: identificador o “slug” del nuevo “meta box”.&lt;/li&gt;
&lt;li&gt;$title: título que se mostrará en el “meta box”.&lt;/li&gt;
&lt;li&gt;$callback: función que imprime el contenido de la “meta box”, se utiliza &lt;strong&gt;echo&lt;/strong&gt; para generar la salida&lt;/li&gt;
&lt;li&gt;$screen: representa la pantalla de administración en la que se mostrará esta “meta box”. En este caso debes poner “receta” para que quede asociado a este “Custom Post Type”.&lt;/li&gt;
&lt;li&gt;$context y $priority: en este caso las configuras como “normal” y “high” respectivamente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verás que la función encargada de cargar el meta box (o los meta boxes si fueran varios) tiene un nombre un poco largo: &lt;strong&gt;kfp_receta_register_meta_boxes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Lo he puesto así porque con la estructura que estamos usando para construir el plugin los nombres de función podrían entrar en conflicto con otros plugins. Imagina que has instalado otro plugin de recetas y el autor llama a su función “receta_register_meta_boxes”, en este caso ambos plugins dejarían de funcionar. Esta forma de nombrar funciones y variables es un estándar y consiste en agregar al principio el nombre o las siglas del desarrollador (en mi caso son las siglas de Kung Fu Press), seguido del nombre o alias del plugin (en esta caso “receta”) y luego viene el resto del nombre que quieras dar a la función. Si hubiéramos encapsulado el plugin en un objeto esto no sería necesario, veremos esta forma de trabajar en algún tutorial más avanzado.&lt;/p&gt;

&lt;p&gt;Este es el código que tienes que añadir a tu plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/\*\*
 \* Agrega los meta boxes para el Custom Post Type Receta
 \* https://developer.wordpress.org/reference/functions/add\_meta\_box/
 \*/
function kfp\_receta\_register\_meta\_boxes()
{
    add\_meta\_box('receta-info', 'Información', 'kfp\_receta\_output\_meta\_box', 'receta', 'normal', 'high');
}
​
/\*\*
 \* Genera el contenido que hay que mostrar dentro del meta box
 \* @param WP\_Post $post WordPress Post object
 \*/
function kfp\_receta\_output\_meta\_box($post)
{
}
​
add\_action('add\_meta\_boxes', 'kfp\_receta\_register\_meta\_boxes');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora puedes ir a tu Custom Post Type en el panel de administración y verás que cuando creas o editas un contenido de este tipo ahora aparece una meta box con el título “Información”.&lt;/p&gt;

&lt;p&gt;A continuación tienes que rellenar el contenido de esa meta box para que sirva para algo. Para eso escribirás algo que genere una salida en pantalla dentro de la función “kfp_receta_output_meta_box”, utilizando la función &lt;strong&gt;echo&lt;/strong&gt; de PHP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/\*\*
 \* Genera el contenido que hay que mostrar dentro del meta box
 \* @param WP\_Post $post WordPress Post object
 \*/
function kfp\_receta\_output\_meta\_box($post)
{
 echo('\&amp;lt;label for="tiempo\_preparacion"\&amp;gt;' . \_\_('Tiempo de preparación', 'text\_domain') . '\&amp;lt;/label\&amp;gt;');
    echo('\&amp;lt;input type="text" name="tiempo\_preparacion" id="tiempo\_preparacion" value="' . esc\_attr($tiempo\_preparacion) . '"\&amp;gt;');
 echo('\&amp;lt;p\&amp;gt;\&amp;lt;label for="comensales"\&amp;gt;' . \_\_('Comensales', 'text\_domain') . '\&amp;lt;/label\&amp;gt;');
 echo('\&amp;lt;input type="date" name="comensales" id="fecha" value="' . esc\_attr($comensales) . '"\&amp;gt;');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De nuevo te animo a probar el resultado en tu panel de administración para que veas que aparecen dos campos de formulario dentro de la meta box. En esta punto si intentas grabar alguna información ahí verás que no se guarda, disculpa que vaya pasito a paso pero quiero asegurarme que lo entiendes bien.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Fmeta_box_informacion_receta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkungfupress.com%2Fwp-content%2Fuploads%2F2019%2F01%2Fmeta_box_informacion_receta.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora vas a añadir el código para que la información se grabe en la base de datos y para que se muestre en la meta box cada vez que abramos este registro.&lt;/p&gt;

&lt;p&gt;Es importante comprobar que el usuario actual tiene permisos para grabar esta información y añadir también una cláusula de seguridad para evitar que alguien intente grabar información en esos campos si no viene de este formulario. Para esto último vas a usar la función &lt;strong&gt;wp_nonce_field&lt;/strong&gt; que genera un campo oculto en el formulario, campo que luego comprobaremos antes de grabar la información.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/\*\*
 \* Genera el contenido que hay que mostrar dentro del meta box
 \* @param WP\_Post $post WordPress Post object
 \*/
function kfp\_receta\_output\_meta\_box($post)
{
    // Los campos se graban en la base de datos con un subrayado bajo como prefijo
    // WP indica así por defecto que son campos metas
 $tiempo\_preparacion = get\_post\_meta($post-\&amp;gt;ID, '\_tiempo\_preparacion', true);
    $comensales = get\_post\_meta($post-\&amp;gt;ID, '\_comensales', true);

 echo('\&amp;lt;label for="tiempo\_preparacion"\&amp;gt;' . \_\_('Tiempo de preparación', 'text\_domain') . '\&amp;lt;/label\&amp;gt;');
    echo('\&amp;lt;input type="text" name="tiempo\_preparacion" id="tiempo\_preparacion" value="' . esc\_attr($tiempo\_preparacion) . '"\&amp;gt;');
 echo('\&amp;lt;p\&amp;gt;\&amp;lt;label for="comensales"\&amp;gt;' . \_\_('Comensales', 'text\_domain') . '\&amp;lt;/label\&amp;gt;');
 echo('\&amp;lt;input type="number" name="comensales" id="comensales" value="' . esc\_attr($comensales) . '"\&amp;gt;');
}
​
/\*\*
 \* Graba los campos del formulario del meta box
 \* @param int $post\_id Post ID
 \* @return bool|int
 \*/
function kfp\_receta\_save\_meta\_boxes($post\_id)
{
    // Comprueba que el nonce es correcto para evitar ataques CSRF
    if ( !isset($\_POST['receta\_nonce']) || ! wp\_verify\_nonce( $\_POST['receta\_nonce'], 'graba\_receta') ) {
        return $post\_id;
   }
​
    // Comprueba que el tipo de post es receta
    if ('receta' != $\_POST['post\_type']) {
        return $post\_id;
   }
​
    // Comprueba que el usuario actual tiene permiso para editar esto
    if (!current\_user\_can('edit\_post', $post\_id)) {
        return $post\_id;
   }
​
    // Ahora puedes grabar los datos con tranquilidad
    // Observa que cuando vienen del formulario los campos meta no vienen con el prefijo subrayado bajo pero cuando los grabas en el post hay que poner el prefijo, igual que cuando los leías del post
    $tiempo\_preparacion = sanitize\_text\_field($\_POST['tiempo\_preparacion']);
    update\_post\_meta($post\_id, '\_tiempo\_preparacion', $tiempo\_preparacion);
 $comensales = sanitize\_text\_field($\_POST['comensales']);
 update\_post\_meta($post\_id, '\_comensales', $comensales);
    return true;
}
​
add\_action('save\_post', 'kfp\_receta\_save\_meta\_boxes');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto ya debe funcionar bien la grabación de los campos meta, prueba a rellenar los campos personalizados en tus recetas y al guardarla, si no hay ningún error, deben permanecer los valores que has introducido&lt;/p&gt;

&lt;p&gt;Y por último habrá que mostrar esos campos que tanto esfuerzo han costado en el front end, para que puedan consultarlo tus visitantes. Esta es una solución un poco burda, pero no quiero que se alargue más el artículo metiendo conceptos relacionados con plantillas, que pondrás ver con más detenimiento en otros tutoriales. Así que lo vas a hacer también con código y desde el propio plugin.&lt;/p&gt;

&lt;p&gt;Usarás el hook “the_content” que es el que se llama cada vez que se va a imprimir el contenido de cualquier entrada, página o post personalizado. Lo que vas a hacer es añadir al contenido principal del artículo los campos personalizados que te interesen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function kfp\_receta\_add\_custom\_fields\_to\_content( $content ) 
{
 $custom\_fields = get\_post\_custom();
 $content .= "\&amp;lt;ul\&amp;gt;";
 if( isset( $custom\_fields['\_tiempo\_preparacion'] ) ) {
 $content .= '\&amp;lt;li\&amp;gt;Tiempo de preparación: '. $custom\_fields['\_tiempo\_preparacion'][0] . '\&amp;lt;/li\&amp;gt;';
 }
 if( isset( $custom\_fields['\_comensales'] ) ) {
 $content .= '\&amp;lt;li\&amp;gt;Comensales: ' . $custom\_fields['\_comensales'][0] . '\&amp;lt;/li\&amp;gt;';
 }
 $content .= '\&amp;lt;/ul\&amp;gt;';
​
 return $content;
}
​
add\_filter( 'the\_content', 'kfp\_receta\_add\_custom\_fields\_to\_content' )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Como mostrar a los visitantes tus tipos personalizados
&lt;/h2&gt;

&lt;p&gt;Para esto hay mil métodos, creo que uno muy deseable es que las recetas aparezcan en la página de inicio, vas a decirle al plugin que cuando WP vaya a buscar los artículos para la página de inicio se traiga también las recetas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_/\*\*  
 \* Agrega el tipo personalizado Receta a la página inicial de WP  
 \*/_ function get\_posts\_y\_recetas( $query ) {
 if ( is\_home() &amp;amp;&amp;amp; $query-\&amp;gt;is\_main\_query() ) {
 $query-\&amp;gt;set( 'post\_type', array( 'post', 'receta' ) );
 }

 return $query;
}

add\_filter( 'pre\_get\_posts', 'get\_posts\_y\_recetas' )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Carga ahora tu página de inicio y verás como tus posts y recetas aparecen publicados en ellas.&lt;/p&gt;

&lt;h2&gt;
  
  
  No te lo quedes dentro
&lt;/h2&gt;

&lt;p&gt;¿Todo ha funcionado? Dímelo y te mando el cinturón amarillo desde ya.&lt;/p&gt;

&lt;p&gt;¿Algo no te funciona? Házmelo saber en los comentario e intento ayudarte. Si ves algún fallo también te agradeceré mucho que me lo digas.&lt;/p&gt;

&lt;p&gt;Si te ha gustado o te ha sido útil, te animo a compartirlo con otros a quien pueda interesarle, en las redes sociales, cantando el artículo mientras te duchas, gritándolo a los cuatro vientos o de cualquier otra manera que se te ocurra.&lt;/p&gt;

&lt;p&gt;Y si quieres seguir haciendo cosas divertidas y útiles con WordPress acepto cualquier sugerencia para seguir escribiendo artículos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enlaces de referencia
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cybmeta.com/custom-fields-y-meta-boxes-para-posts-de-wordpress" rel="noopener noreferrer"&gt;Añadir custom fields y meta boxes para posts de WordPress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.tutsplus.com/tutorials/create-a-simple-crm-in-wordpress-creating-a-custom-post-type--cms-20014" rel="noopener noreferrer"&gt;Create A Simple CRM in WordPress: Creating a Custom Post Type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://justintadlock.com/archives/2010/02/02/showing-custom-post-types-on-your-home-blog-page" rel="noopener noreferrer"&gt;Showing custom post types on your home/blog page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.wptasty.com/tasty-recipes" rel="noopener noreferrer"&gt;https://www.wptasty.com/tasty-recipes&lt;/a&gt; – Ver el vídeo para coger ideas para tu recetario&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>plugins</category>
      <category>customfield</category>
      <category>customposttype</category>
      <category>metaboxes</category>
    </item>
    <item>
      <title>Marcar y desmarcar checkbox de manera simultánea con jQuery</title>
      <dc:creator>Juanan Ruiz</dc:creator>
      <pubDate>Tue, 07 Aug 2018 15:31:39 +0000</pubDate>
      <link>https://forem.com/juananruiz/marcar-y-desmarcar-checkbox-de-manera-simultanea-con-jquery-523o</link>
      <guid>https://forem.com/juananruiz/marcar-y-desmarcar-checkbox-de-manera-simultanea-con-jquery-523o</guid>
      <description>&lt;p&gt;Hace unos días tuve que hacer un formulario para enviar un correo a una lista cerrada de personas, pero me pedían la posibilidad de enviar el correo a todos o a una parte de la lista cerrada.&lt;/p&gt;

&lt;p&gt;Algo parecido a lo que se muestra en la siguiente imagen&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fgalatar.com%2Fwp-content%2Fuploads%2F2018%2F08%2Fcaptura_formulario_correo_con_checks_multiples-1024x512.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fgalatar.com%2Fwp-content%2Fuploads%2F2018%2F08%2Fcaptura_formulario_correo_con_checks_multiples-1024x512.png" alt="Formulario con checks multiples"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tenía hecho algo similar en un proyecto más antiguo y me decidí por la técnica del corta/pega pero para mi sorpresa el script funcionaba la primera vez y luego se negaba a repetir la hazaña.&lt;/p&gt;

&lt;p&gt;Os pongo el fragmento de HTML de ejemplo y el script inicial para comentaros el problema.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;border=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"selectall"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Participantes&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"case"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"case[]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;Fulano de Tal y Cual&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"case"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"case[]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;Irene Sal de la Cueva&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"case"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"case[]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;Zutano Ruiz Ruiz&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#selectall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Como ves he definido un primer checkbox con el identificador "selectall" para seleccionar o deseleccionar al resto que he marcado con la clase "case" para poder referirme a ellos en conjunto.&lt;/p&gt;

&lt;p&gt;Cuando se hace "click" en el checkbox superior todos los demás cambian su estado. El fallo de este código, que yo juraría que funcionaba en versiones antiguas de jQuery, es que la función "attr" sólo modifica el atributo original del elemento, cuando la página se carga, pero no una vez que se ha modificado. Para que funcione en cualquier circunstancia hay que usar la función "prop".&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#selectall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Puestos a mejorar un poco más el script he agregado otro evento para que cuando se desmarquen uno o más elementos se desmarque también el checkbox superior. Y si todos están marcados pues que se marque el superior también.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#selectall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;  

&lt;span class="c1"&gt;// if all checkbox are selected, check the selectall checkbox and viceversa  &lt;/span&gt;
&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.case:checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#selectall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#selectall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Espero que os sea de utilidad en algún momento. En cualquier caso os dejo el &lt;a href="https://codepen.io/juananruiz/pen/rrKEqw" rel="noopener noreferrer"&gt;código en codepen&lt;/a&gt; para que podáis jugar con él.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
  </channel>
</rss>
