Use get_the_terms() instead of wp_get_object_terms()

I was recently debugging the front page of a WordPress site and found a lot of queries to the terms and term relationships database tables.

Digging a little deeper, I found that the culprit were a set of functions that were calling wp_get_object_terms() to get the terms from a set of looped posts… and then I thought… “wait a minute, doesn’t WordPress should be using the object cache for this?”

Well, it turns out that wp_get_object_terms() always queries the database.

If you’re looping over WP_Query results, you should prefer get_the_terms() instead. It’s pretty much the same for most use cases, but it uses the object cache, which by default gets populated with the terms for the posts matching your query — unless you specifically set update_post_term_cache as false when instantiating WP_Query.

The are several differences, though: wp_get_object_terms() can take arrays as the first and second argument, while get_the_terms() can only take the post ID (or object) as first argument (so you can’t get the terms for a bunch of posts on one function call) and a string for taxonomy (so you can’t get the terms for several taxonomies); and you can use a third argument on the former, which the latter doesn’t have.

You could still emulate some of this, and still benefit from using the object cache; for instance, let’s see how you would get the names of the my_custom_tax terms for the current post, ordered by use on a descending way.

// using wp_get_object_terms()
$popular_terms = wp_get_object_terms( $post->ID, 'my_custom_tax', array( 
    'orderby' => 'count',
    'order'   => 'DESC',
    'fields'  => 'names'
) );

// using get_the_terms()
$popular_terms = get_the_terms( $post->ID, 'my_custom_tax' );
// $popular_terms will be ordered alphabetically, so let's order by count
$popular_terms = usort( $popular_terms, function( $a, $b ){
    if ( $a->count < $b->count ) {
        return 1;
    }
    if ( $a->count > $b->count ) {
        return -1;
    }
    return 0;
} );  
// we only need slugs, so...
$popular_terms = wp_list_pluck( $popular_terms, 'name' );

Even if it’s somewhat troublesome, it’s probably worth the effort if you’re trying to maximize for performance.

Unified search with Elasticsearch and WordPress

During the last months of 2012, and as a part of AyerViernes, we worked on one of those projects that is as challenging as delightful to take part in, developing a unified search system for a network of over 200 WordPress sites (both single-install and multisite).

We developed a real-time sync plugin integrating the WordPress sites with an Elasticsearch instance with different content types (mappings) that give us plenty of room to index and search in hundreds of thousands documents generated by the university staff.

You can read the complete post (in spanish) on Medium: Desarrollo de sistema de búsqueda transversal: +200 sitios a un clic de distancia

Importar tu base de datos de WordPress (de la forma más rápida)

Uno de los grandes aprendizajes que he podido aplicar al desarrollo de sitios con WordPress, y del cual soy particularmente entusiasta de sermonear es la necesidad de mantener una versión local de desarrollo lo más parecida posible a lo que vas a utilizar en producción, lo que además se apoya y soporta un montón de otras buenas prácticas como evitar el cowboy coding, utilizar control de versiones, etc.

Puesto que para el cliente no se trata de uno de los objetivos del proyecto, la capacidad de importar una base de datos para tener una réplica de desarrollo debe ser algo sencillo y rápido de ejecutar. Aunque existen múltiples plugins y herramientas para hacerlo, para mí la forma más sencilla es recurrir a herramientas muy básicas a través de la línea de comandos.

Continue reading “Importar tu base de datos de WordPress (de la forma más rápida)”

Cómo agregar un nuevo panel a WordPress Debug Bar

Una de las ventajas del plugin WordPress Debug Bar es que puedes agregar nuevos paneles según tus propias necesidades; por ejemplo, para mostrar datos de respuestas desde APIs externas o conexiones con web services u otras funcionalidades que hayas implementado de forma particualr.

// el plugin utiliza el filtro 'debug_bar_panels'
// que puedes usar para agregar nuevos paneles
add_filter( 'debug_bar_panels', 'my_custom_panel_init' );

/**
 * Inicializar tu panel personalizado
 * @param array $panels Instancias de Paneles
 * @return array
 */
function my_custom_panel_init( $panels ) {
    // Debes extender la clase Debug_Bar_Panel que forma
    // parte del plugin
    class Debug_Bar_Custom_Panel extends Debug_Bar_Panel{
        public function init(){
            // Como mínimo, debes definir el título de tu panel
            // personalizado. Se usará como título de la pestaña
            $this->title( 'Panel Personalizado' );
        }
        public function render(){
            // Construye e imprime el contenido de la pestaña
        }
    }
    // Agrega tu pestaña a la barra de depuración
    $panels[] = new Debug_Bar_Custom_Panel();
    return $panels;
}

Por supuesto, este es un ejemplo simplificado, pero es el punto de partida para tu implementación. Con un par de líneas más ya puedes tener tu propio panel:

Agregar un panel de depuración personalizado a WordPress Debug Bar

Para un ejemplo más completo, puedes revisar el código de alguno de los plugins que extienden la funcionalidad de la barra de depuración, como el repositorio de Debug-Bar-Shortcodes.

Adding a new panel to the WordPress Debug Bar plugin

You can extend the WordPress Debug Bar plugin adding new custom panels to fit your needs; for instance, showing responses from external APIs or webservices, or for other custom features.

// the 'debug_bar_panels' filter it's used by the plugin;
// hook into it to add your custom panel
add_filter( 'debug_bar_panels', 'my_custom_panel_init' );

/**
 * Initialize my custom debug panel
 * @param array $panels Debug bar panels objects
 * @return array Debug bar panels with your custom panels
 */
function my_custom_panel_init( $panels ) {
    // you'll be extending the Debug_Bar_Panel class provided
    // by the plugin
    class Debug_Bar_Custom_Panel extends Debug_Bar_Panel{
        public function init(){
            // you should set at least the title of your custom
            // panel. it'll be used as the tab title
            $this-&gt;title( 'Custom Panel' );
        }
        public function render(){
            // build and echo your relevant output
        }
    }
    // add your custom panel
    $panels[] = new Debug_Bar_Custom_Panel();
    return $panels;
}

That’s an extremely simple example. You can check a more complex and complete one by viewing the source of one of the Debug Bar extender plugins, such as the Debug-Bar-Shortcodes GitHub repo.

Capturar el nombre de un subdominio en la configuración de nginx con expresiones regulares

Al configurar el nombre del dominio en un vhost de nginx puedes utilizar una expresión regular. Esto te sirve, por ejemplo, para capturar el nombre del subdominio para reutilizarlo como variable.

En este snippet, capturamos el nombre del subdominio como subdomain (línea 2) y luego utilizamos el método de carga de imágenes remotas con proxy inverso para mostrar las imágenes que no existen en nuestra copia de desarrollo.

server {
  server_name ~^(?<subdomain>[a-z0-9-]+)\.development\.lo$;
  root /var/www/development/htdocs;
  resolver 208.67.222.222 208.67.220.220 8.8.8.8 8.8.4.4;
  location ~* ^/wp-content/uploads/(.*)$ {
	if ( !-e $request_filename ) {
	  proxy_pass http://$subdomain.testing.com$uri;
	}
  }
  include global/restrictions.conf;
  include global/wordpress.conf;
}

Automatizar tareas de traducción de WordPress con Grunt

Grunt es una de esas herramientas que al principio parece un poco intimidante, pero al empezar a utilizarla se hace cada vez más necesaria y adictiva.

Por si aún no lo conoces, Grunt es un programita hecho en javascript sobre node.js que permite automatizar una gran cantidad de tareas, y sirve especialmente para poner a punto la versión en producción de un sitio; por ejemplo: optimizar imágenes, compilar estilos desde LESS a CSS (o Coffeescript a javascript), ejecutar pruebas unitarias, minificar y concatenar estilos y scripts, etc.

Hace algunos posts escribí sobre algunas herramientas de localización para WordPress que ayudan a generar el catálogo de traducciones POT necesario para hacer un tema o plugin traducible a otros idiomas, y que es un excelente ejemplo de tareas en las que Grunt se luce.

Afortunadamente bradyvercher adaptó esas herramientas para trabajar como módulos de Grunt, por lo que podemos utilizar las mismas herramientas de localización “oficiales” de WordPress de forma automática con Grunt.

Continue reading “Automatizar tareas de traducción de WordPress con Grunt”

Usar DISQUS en sitios multilenguaje

Recientemente rediseñamos el sitio de nuestra aplicación para la gestión de hoteles, y uno de los cambios más importantes fue poder ofrecer una mejor experiencia a los visitantes según su idioma. También nos interesaba tener una mejor interacción en los comentarios del blog, y para ello decidimos utilizar la plataforma de Disqus, que entre otras cosas, permite suscripciones por correo electrónico a los comentarios, identificación con cuentas de terceros, moderación por e-mail, etc.

Uno de los problemas que encontramos fue que Disqus tiene una configuración global para la cuenta, por lo que la opción que configuras en su panel aplica a todas las conversaciones en el sitio. Sin embargo, escudriñando el código del plugin para WordPress pude hallar un filtro que permite indicar explícitamente el idioma en que se debe cargar la sección de comentarios:

add_filter('disqus_language_filter', function( $lang ){
	// la función pll_current_language es del plugin polylang; y devuelve el idioma de la entrada actual
	$current_language = function_exists('pll_current_language') ? pll_current_language('locale') : 'es_ES';
	// ojo que en Disqus, inglés es "en" pero español "es_ES" :-P
	return $current_language == 'en_US' ? 'en' : $current_language;
});

Y si no estás usando WordPress, puedes indicar el idioma en la configuración del embebible.

Cómo asegurar las cookies de acceso a tu sitio WordPress

Recientemente se ha dado a conocer una vulnerabilidad en el sistema de autenticación de usuarios para los sitios con WordPress, que básicamente consiste en lo siguiente:

  • La vulnerabilidad afecta tanto a sitios en WordPress.com como instalaciones propias.
  • Al acceder a la administración de tu sitio, WordPress crea una cookie que le permite validarte como un usuario que ha iniciado sesión.
  • Una gran cantidad de sitios con WordPress no funciona sobre HTTPS, por lo que la cookie se envía como texto plano.
  • Un atacante puede interceptar esa cookie, insertarla en su navegador, y luego hacerse pasar por el usuario que ha iniciado sesión. Esto es particularmente fácil en situaciones donde hay transmisiones “abiertas” de datos, por ejemplo en un café donde la conexión no tiene contraseña.
  • La cookie sigue siendo válida aun cuando el usuario cierre su sesión, por lo que podrías ingresar a tu administrador, cerrar la sesión, y el atacante aun tendría acceso.
  • El tiempo de expiración de la cookie es de 3 años en blogs de WordPress.com y 14 días en instalaciones propias.

Ahora pasemos a lo importante, ¿cómo evitarlo?

En primer lugar, la opción más sencilla pero seguramente inefectiva es la abstinencia: simplemente, evitar acceder a la administración de WordPress en redes no confiables. Pero como estamos hablando de seguridad, y en este sentido no se puede ser demasiado paranoico, desechemos esta opción.

Continue reading “Cómo asegurar las cookies de acceso a tu sitio WordPress”