¿En qué podemos ayudarte?

Si hacemos desarrollo en WordPress, llegará un momento en que que tendremos que realizar actualizaciones. Bien para incorporar funcionalidades nuevas, o con mucha más frecuencia para corregir errores. Si solo tenemos que actualizar nuestra web, el proceso suele ser sencillo y la mayoría de las veces será suficiente con sustituir los archivos modificados. Sin embargo, la cosa se complica si hay que actualizar diferentes instalaciones. Incluso, si desarrollamos para terceros, es posible que no tengamos acceso a algunas de ellas.. 

Veamos que podemos hacer para aprovechar los funciones que WordPress pone a nuestra disposición para gestionar las actualizaciones.. 

Actualizaciones automáticas en Wordresss

WordPress incorpora de serie un magnifico sistema de actualizaciones automáticas. Solo tenemos que subir un par de plugins y ya empieza a dar la tabarra las actualizaciones pendientes:

Y así día tras día.

Como usuarios, podemos decir cuando llevar a cabo la actualización, y además, podemos llevar a cabo el proceso de manera selectiva para temas o plugins específicos. Incluso podremos tirarnos a la piscina y activar las actualizaciones automáticas, y así olvidarnos del tema. Sin embargo, la comodidad tiene un riesgo, aunque casi siempre el proceso de actualización transcurre sin incidencias, de vez en cuando nos podemos encontrar con incompatibilidades que tendremos que revisar y solucionar.

 ¿Qué podemos hacer para que nuestro desarrollo en WordPress se aproveche de este mecanismo? La respuesta rápida es publicar nuestro proyecto en WordPress.org. Tendremos que registrarnos, si aun no lo hemos hecho. Nuestro plugin será revisado, y si cumple con las directrices establecidas,  publicado en el repositorio oficial. El proceso de revisión suele suele tardar unos días. Una vez sea aprobado recibiremos un correo con la información necesaria para gestionarlo mediante Subversion.

El repositorio oficial de WordPress solo admite versiones gratuitas. Si comercializamos nuestro desarrollo, o si por algún otro motivo no queremos que esté disponible para el público; no tendremos más remedio que implementar nuestro propio gestor de actualizaciones. Por suerte WordPress nos da las herramientas necesarias.

Funcionamiento de la API de actualizaciones

La "magia" detrás de las actualizaciones automáticas es sencilla y podemos entenderla si echamos un vistazo al archivo /wp-includes/update.php.  

Cada 12 horas nuestra se realiza una conexión al repositorio oficial de WordPress para obtener información sobre los complementos instalados. La solicitud y la respuesta obtenida se almacenan como opciones "transient" (temporales). Cada transient ("update_plugins", "update_themes" o "update_core") contendrá la información necesaria sobre los plugin, temas y el núcleo de WordPress. 

Nos interesan los dos primeros. Su estructura contiene tres elementos:

  • last_checked: la fecha y hora de la petición
  • checked:  la lista de complementos verificados
  • response: la respuesta del servidor sobre cada plugin o tema verificado. Esa respuesta contiene la información sobre la actualización y la url de descarga. 

Por tanto nuestra solución debe

  1. Conectarse a nuestro servidor para obtener la información sobre nuestro desarrollo en WordPress
  2. Si existe una versión nueva agregar los datos a la lista "response" del transient
  3. Engancharse al filtro "plugins_api" para mostrar la información detallada sobre dicha actualización (si actualizamos un plugin)
  4. Y por último,  necesitamos un servidor de actualizaciones al que consultar. En principio el servidor solo tiene que servir archivos estáticos que veremos más abajo 


Gestionando la actualización de un plugin

En este caso utilizaremos el filtro "site_transient_update_plugins" para incorporar la información sobre nuestro plugin. Usaremos una función similar a la siguiente:

Ver código

function actualizar_mi_plugin( $transient ){
   if ( empty($transient->checked) ) {return $transient;}
   if ( false == ($remote = get_transient( 'actualiza_nombre-del-plugin')) ) {
      $remote = wp_remote_get( 'https://servidor/actualizaciones/info.json', ['timeout' => 10, 'headers' => ['Accept' => 'application/json']]);
       if ( !is_wp_error( $remote ) && isset( $remote['response']['code'] ) && $remote['response']['code'] == 200 && !empty( $remote['body'] ) ) {
           set_transient( 'actualiza_nombre-del-plugin', $remote, 43200 );
      }
   }
   if( $remote ) {
      $remote = json_decode( $remote['body'] );
      if( $remote && version_compare( '1.0', $remote->version, '<' )  && version_compare($remote->requires, get_bloginfo('version'), '<' ) ) {
         $res = new stdClass();
         $res->slug = 'nombre-del-plugin';
         $res->plugin = 'nombre-del-plugin/nombre-del-plugin.php'; 
         $res->new_version = $remote->version;
         $res->tested = $remote->tested;
         $res->package = $remote->download_url;
        $transient->response[$res->plugin] = $res;
      }
   }
   return $transient;
}
add_filter('site_transient_update_plugins', 'actualizar_mi_plugin' );

La función comprueba si existe una consulta no caducada. Si es así la utiliza. En caso contrario se conecta a nuestro servidor de actualizaciones, descarga el archivo con la información y la incorpora al transient. El transient tiene una caducidad de 12 horas que podemos modificar.

Pasamos al archivo info.json. Este archivo debe tener una estructura  como la mostrada a continuación:

Ver código

{   "name" : "Nombre del plugin",
    "version" : "1.1",
    "download_url" : "https://servidor/actualizaciones/nombre-del-plugin/nombre-del-plugin.zip",
    "requires" : "5.4",
    "tested" : "5.5",
    "requires_php" : "7.2",
    "last_updated" : "2020-08-17 02:10:00",
     "sections" : {
        "description" : "Descripción de la actualización",
        "installation" : "Instrucciones para actualizar",
        "changelog" : "<h4>1.1 – 17/08/2020</h4><ul><li>Correcciones menores.</li><li>Versión inicial.</li></ul>"
      },
      "banners" : {
         "low" : "https://servidor/actualizaciones/nombre-del-plugin/banner-772x250.jpg",
        "high" : "https://servidor/actualizaciones/nombre-del-plugin/banner-1544x500.jpg"
      },
      "screenshots" : "<ol><li><a href='IMG_URL' target='_blank'><img src='IMG_URL' alt='CAPTION' /></a><p>CAPTION</p></li></ol>"
 }

Los nombres de los elementos describen su funcionalidad. Es un archivo con formato json, por tanto debemos escapar correctamente las cadenas

Solo nos queda subir los archivos del plugin comprimidos en formato zip, así como los banners a las carpetas del servidor indicadas en info.json.  

Para mostrar a los usuarios información detallada sobre la actualización utilizamos el filtro "plugins_api". 

Ver código

function mi_plugin_info( $res, $action, $args ){
     $plugin_slug = 'nombre-del-plugin';
     // si no se solicita información o no es nuestro plugin no hacemos nada
    if( 'plugin_information' !== $action ) {return false;}
    if( $plugin_slug !== $args->slug ) {return false;}
    if( false == $remote = get_transient( 'actualiza_'.$plugin_slug ) ) {
        $remote = wp_remote_get( 'https://servidor/actuaizaciones/info.json', ['timeout' => 10, 'headers' => ['Accept' => 'application/json']]);
        if (!is_wp_error( $remote ) && isset($remote['response']['code']) && $remote['response']['code'] == 200 && !empty($remote['body'])) {
          set_transient( 'actualiza_'.$plugin_slug, $remote, 43200 );  
       }
     }
    if( !is_wp_error($remote) && isset($remote['response']['code']) && $remote['response']['code'] == 200 && ! empty($remote['body'])) {
   $remote = json_decode( $remote['body'] );
   $res = new stdClass();
   $res->name = $remote->name;
   $res->slug = $plugin_slug;
   $res->version = $remote->version;
   $res->tested = $remote->tested;
   $res->requires = $remote->requires;
   $res->author = '<a href="https://url-autor">Nobre del autor</a>'; 
   $res->author_profile = 'https://perfil-del-autor';
   $res->download_link = $remote->download_url;
   $res->trunk = $remote->download_url;
   $res->requires_php = $remote->requires_php;
   $res->last_updated = $remote->last_updated;
   $res->sections = array(
       'description' => $remote->sections->description,
       'installation' => $remote->sections->installation,
       'changelog' => $remote->sections->changelog
       // pueden existir secciones adicionales en info.json
    );
    if(!empty( $remote->sections->screenshots)) {$res->sections['screenshots'] = $remote->sections->screenshots;}
    $res->banners = array(
      'low' => $remote->sections->banners->low,
      'high' => $remote->sections->banners->high
     );
     return $res;
   }
   return false;
}
add_filter('plugins_api', 'mi_plugin_info', 20, 3);

Actualización de un tema personalizado

En el caso de un tema el filtro a utilizar es "site_transient_update_themes". El prototipo de la función se muestra a continuación:

Ver código

function comprueba_version_tema ( $transient ) {
    if( empty( $transient->checked['nombre-del-tema'] ) ) return $transient;
    $request = obtiene_ultima_version_del_tema();
    if ( is_wp_error( $request ) || wp_remote_retrieve_response_code( $request ) != 200 ) {return $transient;} 
    else {$response = wp_remote_retrieve_body( $request );}
    $data = json_decode( $response );
    if ( version_compare( $transient->checked['nombre-del-tema'], $data->new_version, '<' ) ) {
       $transient->response['nombre-del-tema'] = (array) $data;
       add_action('admin_notices', 'aviso_tema_admin_notice');
    }
  return $transient;
}

function obtiene_ultima_versiondel_tema() {
   $request = wp_safe_remote_get( 'https://servidor/actualizaciones/tema.json' );
   /*
   La respuesta debe tener el siguiente formato:
  {"new_version": "1.0.4",
    "url": "https://servidor/actualizaciones/tema/changelog/",
    "package": "https://servidor/actualizaciones/tema/theme.zip"
   }
   */
   return $request;
}

function aviso_tema_admin_notice(){
   echo '<div class="notice notice-warning notice-alt is-dismissible"><p>Actualización disponible para Nombre del Tema.</p></div>';
}

add_filter ( 'site_transient_update_themes', 'comprueba_version_tema' );

Al igual que con los plugins solo tenemos que actualizar los archivos en el servidor para que WordPress haga el resto.


Librerías para actualizar nuestro desarrollo en WordPress

¿Qué más podemos hacer para simplificar esta tarea? En realidad, prácticamente toda la información requerida por las funciones mostradas puede ser obtenida de  forma automática por el servidor de actualizaciones utilizando la información de cabecera  del plugin y/o tema.. Por otra parte las urls de descarga pueden ser generadas automáticamente.

Sin adaptamos el servidor de actualizaciones, solo tendremos que los archivos comprimidos y nada más. No hay que inventar la rueda 2 veces. Existe una librería gratuita que se encarga de realzar este trabajo. La librería gestiona tanto l prte del servidor como el cliente de actualizaciones.

Gestión del servidor

Para gestionar el servidor de actualizaciones prefiero utilizar la librería de YahnisElsts. Es un script sencillo que funciona desde cualquier carpeta del servidor.

Una vez descomprimido tendremos la estructura de carpetas mostrada a la derecha. 

Solo tenemos que subir un archivo comprimido con nuestro plugin o tema a la subcarpeta "packages". A partir de ese momento, el servidor está listo para atender las peticiones de actualización para nuestros desarrollos en WordPress en la url: 

http://servidor/carpeta/?action=get_metadata&slug=nombre-del-plugin

Es posible incluir un readme.txt en la carpeta comprimida con la información de la actualización y en "packages-assets" subiremos las imágenes correspondiente.

El servidor mantiene un registro con las estadísticas de descargas y puede ser ampliado fácilmente para incluir validación de licencias, conexiones a bases de datos  y otras funcionalidades que podamos necesitar.

Desarrollo wordpress - update server

Me gusta por su simplicidad, ya que prácticamente no hay que hacer nada para hacerlo funcionar. Es la base de otros desarrollos más sofisticados.

Clientes de actualización

El servidor viene acompañado de 2 librerías: "plugin-update-checker" y "theme-update-checker" que una vez incorporadas a nuestros desarrollos harán todo el trabajo duro por nosotros.

En el caso de un plugin incorporamos la biblioteca a los archivos y añadimos l archivo principal las siguientes líneas de código

$MyUpdateChecker = Puc_v4_Factory::buildUpdateChecker(
    'https://servidor/carpeta/?action=get_metadata&slug=nombre-del-plugin',  //URL de descarga
    __FILE__, //Ruta al archivo principal del plugin.
   'nombre-del-plugin'  //Slug del plugin
);

Eso es todo. A partir de ese momento nuestros usuarios serán notificados cuando exista una nueva versión disponible. 

Este es el resultado para el plugin de Dropshipping que utilizo en la tienda

Si revisamos los detalles de la versión obtendremos el siguiente resultado

En el caso de un temas, usamos el mismo procedimiento. Copiamos los archivos de la librería a la carpeta del tema y añadimos las siguientes líneas al functions.php

require 'ruta-a-theme-updater/theme-update-checker.php';
$mi-tema_update_checker = new ThemeUpdateChecker(
   'nombre-del-tema',
   'http://servidor/carpeta/?action=get_metadata&slug=nombre-del-tema'
);


Conclusiones

Hemos visto que implementar el servicio de actualizaciones automáticas para nuestros desarrollos en WordPress es extremadamente simple. Estaremos obligados a hacerlo si tenemos previsto vender nuestros trabajos o si por cualquier motivo no queremos que estén disponibles para el público en general.

En mi opinión, las actualizaciones automáticas son una funcionalidad imprescindible. Las librerías mostradas nos ofrecen libertad total para implementar diferentes funcionalidades como integrar nuestros desarrollos con GitHub o BitBucket o tener nuestro propio sistema de control de licencias.

En definitiva, no tenemos ninguna excusa para que nuestros desarrollos no tengan ese toque profesional que aportan las actualizaciones automáticas. Y a la larga nos ahorraremos tiempo y esfuerzo en su mantenimiento.

0 0 votes
Valoración del artículo
Suscribir
Notificar de
guest
0 Comentarios
Newest
Oldest Most Voted
Inline Feedbacks
View all comments