Seminarios de WordPress en Paradigma: Introducción y curso avanzado

Hace ya unos cuantos meses tuve la suerte de poder impartir dos seminarios sobre WordPress a mis compañeros de Paradigma Tecnológico.

Introducción a WordPress

Con este primer seminario podréis ver una breve introducción a lo que es WordPress, conociendo sus ventajas y sus riesgos. También aprovechamos para hacer un sencillo caso práctico, creando una web de ejemplo, y viendo como editar y administrar dicha web.

WordPress avanzado. Las tripas de WordPress y sus Plugins

Segundo seminario de esta serie, con el que entramos en WordPress más en profundidad. Con este curso podrás ver y conocer:

  • La estructura de ficheros de WordPress y de sus plantillas,
  • ¿Qué es el loop y como funciona?
  • Mi lista de plugins indispensables,
  • y algunas medidas de seguridad para proteger WordPress de ataques indeseados.

WordPress supervitaminado: PODs Framework

Tercer y último seminario de esta serie. Por desgracia todavía no he podido sacar tiempo para impartirlo, pero espero que pronto pueda impartirlo, y así dar por terminado esta trilogía del WordPress.

CSS transform: El movimiento de las órbitas

Hacer animaciones con CSS mola, y a día de hoy es algo que todavía no está muy extendido entre los maquetadores. El soporte de navegadores ya es mas que aceptable, y deberíamos de empezar a aplicarlos en nuestras maquetas.

En la última versión de la web de Paradigma optamos por hacer uso de las animaciones CSS en algunas de sus páginas. El caso más significativo es el de la página de contacto o la 404, donde quisimos simbolizar varios grupos de órbitas en movimiento.

Tras hablar con algunos de mis colegas, y darme cuenta de que pensaban que estaba hecho con JavaScript, pensé que podría ser interesante explicar como se hizo  (por lo menos no pensaron que estaba hecho en Flash).

orbits

leer mas

Pods WordPress > Mi guía

Como ya sabéis me encanta usar Pods Framework para WordPress, pero siempre gasto demasiado tiempo volviendo a buscar como hacer las queries, mostrar ciertos campos, … Por este motivo, voy a intentar crearme una guía con las porciones de código que más uso en mis proyectos, y así poder usarla como guía de consulta en el futuro. Con un poco de suerte, quizás también sea útil para ti.

Tipos de Pods

Yo suelo usar dos: Custom Post Type (ampliar los post habituales de Worpdress) o los Advanced Content Type (tablas nuevas que no extienden de ningún elemento de WordPress)

Según usemos uno cambiará ligeramente las queries de busqueda:

Custom Post Type

$params = array(
  'where' =>   't.post_status="Publish"',
  'orderby' => 'position.meta_value+0 ASC',
  'limit' =>   -1  // -1 = no limit in items per page
);

$mypod = pods( 'podsTypeName', $params );
$params = array(
  // show it if wordpress status is publish & it belongs to current type
  'where' => 't.post_status="Publish" AND podTypeName.field_id="'.$typeId.'"'
);

Advanced Content Type

$params = array(
  'where' => 'active=1',
  'orderby' => 'position+0 ASC',
  'limit' => -1  // -1 = no limit in items per page
);

$mypod = pods( 'podsTypeName', $params );

Loop

while ( $mypod->fetch() ) {
  echo $mypod->data->row['ID'];
  echo $mypod->display('title');

  echo wpautop($mypod->display('content'));
    // Changes double line-breaks in the text into HTML paragraphs (<p>...</p>)

  // multiselect field:
  echo $mypod->get_field('categories');
  $array = $mypod->field('categories.category_id');
     foreach ($array as &$item) {
       echo $item;
     }

  // Image
  $portfolio_image = $portfolio->get_field('image');
  echo $portfolio_image[0]['guid'];
}
// Total records in the loop
$openingData->total();

Single Records

$slug = pods_v( 'last', 'url' );
// antes se usaba: $slug = pods_url_variable('last');

$params = array( 'where' => 'post_name = "'.$slug.'"' );
$mypod = pods( 'podsTypeName', $params );

Poco a poco iré ampliando estas porciones de código.

Espero que os sea de ayuda

Campos de fecha

$datetime = explode(" ", $mypod->display( 'start_date' ));
$date = explode("-", $datetime [0]);
$time = explode(":", $datetime [1]);
echo date(get_option('date_format'), mktime($time[0], $time[1], 0, $date[1], $date[0], $date[2]));

Centrar texto verticalmente con CSS

Parece mentira, pero todavía tengo muchos problemas para centrar el texto verticalmente dentro de un div. Hoy he visto una solución que me ha gustado en Stack Overflow.

<div>
  <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
</div>
div {
  width: 250px;
  height: 100px;
  line-height: 100px;
  text-align: center;
}

span {
  display: inline-block;
  vertical-align: middle;
  line-height: normal;
}

De este modo conseguimos lo que vemos en esta imagen, que los botones siempre tengan la misma altura, y queden bien con una o dos líneas.

double-line-button

WordPress search: Customizar búsqueda para encontrar diferentes tipos de entradas

Hoy he tenido la necesidad de customizar la búsqueda por defecto de Worpdress, que usamos en la web corporativa de Paradigma, para poder mostrar en una misma página de resultados diferentes tipos de entradas/posts/artículos.

Aunque he visto que hay un par de plugins que hacen esta funcionalidad, he optado por adaptar la búsqueda por medio de funciones en el fichero functions.php de mi tema.

El primer problema es que para poder realizar estas búsquedas no podemos usar los campos por defecto de wordpress (por ejemplo el título o el contenido), así que necesitamos ejecutar una acción que añada un campo oculto por cada uno de los campos que necesitaremos para la búsqueda:

add_action( "save_post", "add_title_custom_field");

function add_title_custom_field($postid){
    // since we removed the "s" from the search query, we want to create
    // some custom fields for every post_title & post_content
    update_post_meta($postid, "_post_title", $_POST["post_title"]);
    update_post_meta($postid, "_post_content", $_POST["post_content"]);
}

Está acción se ejecutará cada vez que salvemos un post, y tendremos que tenerla siempre activa, para que todas las entradas que actualicemos tengan esta información. Pero ¿qué podemos hacer con todos las entradas que ya tenemos creadas en nuestro wordpress? ¿Es necesario salvar cada una por separado? Pues por suerte no. Con esta función que os pongo a continuación, y que basta con que sea ejecutada UNA ÚNICA VEZ, actualizaremos todo el contenido de nuestra base de datos, sin necesidad de tener que ir salvando uno a uno cada artículo.

// create new fields on every post of your wordpress
function addNewFieldsToPosts() {
    global $post;
    $args = array(
        'numberposts' => -1,
         //  set here each post type you want to search
        'post_type' => array( 'post', 'portfolio', 'openings', 'events' )
        );
    $the_query = get_posts( $args );
    if ($the_query) {
        foreach ($the_query as $post) {
            update_post_meta($post->ID, "_post_title", $post->post_title);
            update_post_meta($post->ID, "_post_content", $post->post_content);
        }
    }
}
wp_reset_query();
add_action('wp_head', 'addNewFieldsToPosts');

Si no ejecutamos estas dos funciones no encontraremos resultados en nuestras búsquedas.

Vale, si , muy bonito, pero… ¿Y la función necesaria para adaptar la búsqueda de WordPress? Pues aquí la teneis:

// CUSTOM SEARCH RESULTS
function custom_search_query( $query ) {
    $custom_fields = array(
        // put all the meta fields you want to search for here
        "_post_title", // we must add this fields on update_post_meta function
        "_post_content",
        "long_description"
    );
    $searchterm = $query->query_vars['s'];
    // we have to remove the "s" parameter from the query,
    // because it will prevent the posts from being found
    $query->query_vars['s'] = "";
    if ($searchterm != "") {
        //  set here each post type you want to search
        $query->set('post_type', array('post', 'pages', 'events', 'portfolio' ));
        $meta_query = array('relation' => 'OR');
        foreach($custom_fields as $cf) {
            array_push($meta_query, array(
                'key' => $cf,
                'value' => $searchterm,
                'compare' => 'LIKE'
            ));
        }
        $query->set("meta_query", $meta_query);
    };
}
add_filter( "pre_get_posts", "custom_search_query");

Diferenciar resultados por tipo de entrada

En nuestra página de resultados de búsqueda (search.php) podremos diferenciar cada resultado por su tipo de este modo:

$post_type = get_post_type( $post_id );

Espero que os sean de utilidad estas funciones. A mi me han salvado el día.

Mi combinación ganadora para Internet Explorer 8

Cuando empezamos un nuevo proyecto siempre queremos aprovechar todo el potencial de HTML5, pero por desgracia no podemos olvidarnos de nuestro fiel amigo Internet Explorer 8.

Por suerte, ya hace tiempo que desterramos de nuestras maquetas a sus predecesores: Internet Explorer 6 y 7, pero IE8 se quedará con nosotros a tomar otro par de copitas más antes de irse.

Gracias a frameworks como bootstrap, foundation o paraGridma, podemos tener un esqueleto de aplicación moderno, pero perfectamente funcionar en IE8. En el desarrollo de paraGridma, el mayor problema que me encontre para adaptarlo a IE8 fue el de que aceptara algunas de las novedades de HTML5 y sobretodo el uso de media-queries.

Finalmente, opte por la combinación de dos librerías: html5shiv y respond.js.

Podemos incluir estas librerías por el método condicional tradicional:

<!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
  <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->

O también podemos incluirlo de esta otra forma.

<?php
  // because new Internet Explorer develop tools don't proccess conditional comments
  $conditionalComments = '';
  if (strpos($_SERVER['HTTP_USER_AGENT'],'MSIE 8.0') !== false) {
    $conditionalComments .= '<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>';
    $conditionalComments .= '<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>';
  }

  echo $conditionalComments;
?>

Es recomendable usar esta segundo método si estamos testando la web con las develop tools de explorer 11, ya que incomprensiblemente este navegador no entiende los comentarios condicionales cuando emulamos una versión anterior del navegador.

Y hasta aquí mi consejo de hoy. Espero que os haya sido de utilidad.

Adaptar el alto y ancho al embeber un iframe de Youtube, Vimeo, …

Uno de los problemas que surgen al hacer una web responsible es el de querer mostrar un iframe de Youtube, Vimeo, etc… Los iframes que nos facilitan estos proveedores suelen tener asociado una anchura y altura fijas, por lo que difícilmente se van a adaptar a nuestro flamante diseño fluido. Lo bueno, es que ya hace tiempo alguien muy sesudo se encontró con este problema, y le dio una solución de lo más sencilla, para que al altura del iframe siempre sea relativa a su anchura.

video_responsive

<div class="responsiveContent">
    <iframe src="//www.youtube.com/embed/_veOIvMWazU" 
            allowfullscreen="" frameborder="0"></iframe>
</div>

 

.responsiveContent {
  position: relative;
  height: 0;
  overflow: hidden;
  padding-bottom: 56.2%;
  margin-bottom: 20px;
}
.responsiveContent iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

Como vemos en el código la solución pasa por:

  • Quitar toda referencia a width o height en la etiqueta del propio iframe
  • Crear un contenedor para el iframe con altura 0
  • La altura vendrá definida por el padding que le demos al contenedor. Al darle el padding en porcentage, la altura siempre sera el x% de la anchura del padre.

Podéis ver este ejemplo funcionando en mi querido framework paragridma.

Lo único que tendréis que tener en cuenta es el aspect ratio del vídeo que queráis mostrar. En caso de no coincidir se deberá de modificar el padding del contenedor.

Espero que este truqui os sea de utilidad.

Mi experiencia de wordpress Multiidioma con el plugin Polylang

En varias ocasiones he tenido que crear sitios wordpress en varios idiomas. Hasta ahora venía usando el plugin WPML, pero el hecho de que se haya convertido en un plugin de pago y que me surjieran algunos bugs, me hizo buscar alguna alternativa.

Al final he dado con el plugin Polylang, y de momento mi experiencia es muy buena. En la parte negativa puedo destacar la falta de documentación, que todavía se encuentra en una fase temprana de desarrollo (aunque su funcionamiento de momento es correcto), y que la integración con PODs no es todavía completa.

Algunos de mis problemas de momento:

Lo estoy usando junto con PODs, y al mostrar los registros de PODs muestra todos los que encuentran, sin importar el idioma:

En su día cree esta entrada en el foro de PODs


/*= GET CASES STUDIES */
$params = array(
'where' => 'active.meta_value = 1',
'orderby' => 'position ASC',
'limit' => 4
);

// Get the Pods object
$mypod = pods( 'case_studies', $params );

// Loop through the items returned
while ( $mypod->fetch() ) {
?>

<? echo $mypod->display( 'title' ); ?>
<? echo $mypod->display( 'short_description' ); ?>

Para solucionarlo he tenido que hacer esta modificación:


while ( $mypod->fetch() ) {
// it show all matched pods, from all language,
  // so we check if current pod is in current language
$post_id = $mypod->data->row['ID'];
$translated_post_id = pll_get_post($post_id, pll_current_language());

// if both id are equal, then it is in the right laguage
if ($post_id == $translated_post_id) {
?>

<? echo $mypod->display( 'title' ); ?>
<? echo $mypod->display( 'short_description' ); ?>

}
}

Link a la home page con el idioma correcto


<?= pll_home_url(); ?>

No se me muestran algunas páginas

Este es el error que más me preocupa, y estoy francamente preocupado por que se me pueda reproducir en producción

De repente (no se que desencadena el fallo) los links a las páginas de worpdress muestran el contenido del index.php en lugar de mostrar el page.php

Hasta ahora lo he podido solucionar desactivando y volviendo a activar los plugins de PODs, y de WordPress SEO

¿Dónde carajo han metido el emulador de Google Chrome?

Alguien en el brillante equipo de Google Chrome ha decidido (seguro que con buen criterio) el cambiar la localización de la pestaña del emulador de Google Chrome.

Esta funcionalidad es muy útil si deseas emular que estas viendo una web con alguna determinada versión de móvil o tablet.

¡Guala!, después de buscar un buen rato he encontrado la nueva localización.

chrome_emulator

Estos son los pasos que debes de seguir para abrirlo:

  1. Abrir el panel de Settigns, situado en la parte superior derecha del panel
  2. Comprobar que tienes marcada la opción de “Show ‘Emulation’ view in console drawer” y cerrar la ventana de configuración
  3. Abrir la pestaña de “Sources”
  4. Pulsa “Esc” y aparecerá un nuevo panel en la parte inferior
  5. Selecciona la pestaña de “Emulation” y listo!

La solución la he encontrado en esta página

Aplicar estilo CSS a Inputs de tipo File

A algunos tipos de campo de formulario no es fácil aplicarles cambios en el estilo, como por ejemplo, los radio button, los checkbox o los campos de subida de ficheros.

Con el plugin que hoy os propongo aquí, podremos falsear los campos de tipo file fácilmente.

La clave está en ocultar el campo real (el que no podemos editar) y crear un nuevo elemento que lo imite, pero en el que sí podamos aplicar todos los cambios deseados. Posteriormente, por medio de Javascript haremos que al hacer clic en el campo falso, se abra la ventana de diálogo, para la elección del fichero en cuestión.

customInputFile

Estructura HTML:

<div class="customFileInput inputWithButton inputWithButton_150">
  <div class="inputWrap fileName"></div>
  <div class="btWrap">
    <a class="bt" href="#">Select File...</a>
  </div>
  <input class="text" type="file" placeholder="Placeholder" />
</div>

Plugin jQuery:

$.fn.customFileInput = function () {

    return this.each(function() {
        var t = $(this),
            input = t.find('input'),
            fakeTrigger = t.find('.bt'),
            fakeInput = t.find('.fileName');

        input.change(function () {
            // get only file name, with out path
            var fileName = input.val().split('\\').pop();
            fakeInput.html(fileName);
        });

        input.click(function (e) {
            e.stopPropagation();
        })

        t.click(function (e) {
            e.preventDefault();
            input.click();
        });

    });
};

$(function () {
    $('.customFileInput').customFileInput();
});

Las propiedades CSS para darle estilo no las pongo aquí, ya que simplemente estoy usando la nueva versión del framework paraGridma que estamos termiando de desarrollar en Paradigma