.. _nginx-wordpress:

Wordpress
*********
`wordpress <http://wordpress.org>`_ es un |CMS| orientado a la creación de
*blogs*, aunque se usa también incluso para crear sitios *web* generales.

Lo habitual es que se instale para albergar un único *blog*, pero es posible
también hacer una instalación que permita la gestión de varios, cada uno de los
cuales con su administrador. Ambas instalaciones las trataremos bajo este
epígrafe.

.. note:: En la última revisión de este epígrafe la versión del
   :program:`wordpress` era la **5.8.2**.

.. _nginx-wordpress-mono:

Blog único
==========

Preliminares
------------
Se supone instalado ya un servidor :ref:`nginx <n-ginx>` con soporte para
contenido dinámico en |PHP| y *MySQL*, que reservaremos para
:program:`wordpress` el nombre *blog.example.net* y que albergaremos la
aplicación en :file:`/srv/www/blog`.

Se necesitan, adicionalmente, los siguientes módulos de |PHP|::

   # apt-get install php-{gd,mbstring,zip,imagick,xml{,rpc}}

Además es necesario crear una base de datos para :program:`wordpress` y un
usuario que la gestione::

   # mysql
   mysql> CREATE DATABASE wordpress;
   mysql> GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost' IDENTIFIED BY 'contraseñadificil';

.. note:: El usuario y la contraseña sólo serán necesarios recordarlos para el
   proceso de instalación con :program:`wordpress`, así que no es demasiado
   molesto que ambos sean complicadísimos.

Por último, debe descargarse y descomprimirse :program:`wordpress` en
:file:`/srv/www/blog`::

   # mkdir -p /srv/www/blog
   # wget -q http://wordpress.org/latest.tar.gz -O - | tar -C /srv/www/blog --strip-components=1 -zxvf -
   # chown -R www-data /srv/www/blog

.. note:: Durante la instalación damos la propiedad de toda la aplicación al
   usuario *www-data*, es decir, a aquel que ejecuta el servidor *web*. Esto
   permitirá que el proceso de instalación pueda hacer las modificaciones
   pertinentes sin problemas. Tras instalar, corregiremos esto para mejorar la
   seguridad del sitio.

.. _nginx-wordpress-simple:

Configuración de :program:`nginx`
---------------------------------
Como la aplicación requiere la introducción de contraseñas, es conveniente que
corra en un sitio seguro, así que podemos utilizar :download:`esta configuración
<files/site-blog>`:

.. literalinclude:: files/site-blog
   :language: nginx

.. _nginx-robotsoff:
.. _nginx-faviconoff:

La configuración incluye dos *snippets*:

:file:`robotsoff.conf`
   que deshabilita el registro de las consultas al fichero `robots.txt
   <https://es.wikipedia.org/wiki/Est%C3%A1ndar_de_exclusi%C3%B3n_de_robots>`_:

   .. code-block:: nginx

      # /etc/nginx/snippets/robotsoff.conf
      location = /robots.txt {
              allow all;
              log_not_found off;
              access_log off;
      }

:file:`faviconoff.conf`
   que deshabilita el registro de acceso al `favicon
   <https://es.wikipedia.org/wiki/Favicon>`_:

   .. code-block:: nginx

      # /etc/nginx/snippets/faviconoff.conf
      location = /favicon.ico {
         log_not_found off;
         access_log off;
         return 410;
      }

.. _wp-permisos:

Instalación
-----------
Basta con visitar el sitio desde un navegador y seguir las sencillísimas
instrucciones. Al término, conviene cambiar los permisos para aumentar la
seguridad del sitio::

   # chown -R root:root /srv/www/blog
   # chown -R www-data /srv/www/blog/wp-{config.php,content}

.. _nginx-plugins:

Además, si se quiere permitir la instalación de *plugins* desde la interfaz
*web*, debe añadirse la siguiente línea al fichero :file:`wp-config.php`::

   # Instalación directa de plugins
   define('FS_METHOD', 'direct');

.. warning:: Esta línea (y otras que se añadan a mano más adelante) deben
   añadirse siempre antes de la línea que reza::

      /* That's all, stop editing! Happy blogging. */

.. _nginx-wordpress-optimo:
      
Optimizaciones
--------------
Cacheo de contenido dinámico
""""""""""""""""""""""""""""
El rendimiento de nuestra *blog* puede verse mejorado muy sensiblemente si
habilitamos una caché para las páginas dinámicas, de manera que el código |PHP|
sólo vuelva a generar la página, si esta realmente ha cambiado; en caso
contrario echaremos mano de la página cacheada.

Para este propósito necesitamos dos componentes:

1. Habilitar la capacidad de *proxy* caché de :program:`nginx`, de manera
   semejante a como lo hicimos para :ref:`crear la caché de paquetes de debian
   <nginx-debcache>`, pero en este caso cacheando *fastcgi*.

2. Un plugin de :program:`wordpress`, `nginx cache
   <https://es.wordpress.org/plugins/nginx-cache/>`_, que ordene a
   :program:`nginx` borrar las páginas cacheadas que han sufrido algún cambio
   al producirse una acción en :program:`nginx`.

Para lo primero basta con crear un directorio adecuado que, por coherencia con
el epígrafe dedicado a la otra cache, llamaremos::

   # mkdir -pm700 /var/cache/nginx
   # chown www-data /var/cache/nginx

y modificar el :download:`fichero de configuración ya propuesto
<files/site-blog>` para añadir\ [#]_:

.. code-block:: nginx
   :emphasize-lines: 1-4, 13-18, 32-39

   fastcgi_cache_path /var/cache/nginx/wp-cache
                      levels=1:2
                      keys_zone=wp-cache:100m
                      inactive=60m;

   server {

      # Configuracion...

      include snippets/robotsoff.conf;
      include snippets/faviconoff.conf;

      # Evitamos cachear el panel de administración, porque
      # el plugin no parece funcionar bien y no lo purga cuando debe.
      set $skip_cache 0;
      if ( $request_uri ~ /wp-admin/ ) {
         set $skip_cache 1;
      }

      # Evita que se ejecute cualquier
      # script php subido por el usuario.
      location ^~ /wp-content/uploads/ {
         expires max;
         access_log off;
         log_not_found off;
      }

      location ~ \.php$ {
         include snippets/fastcgi-php.conf;
         fastcgi_pass php;

         fastcgi_no_cache $skip_cache;
         fastcgi_cache wp-cache;
         fastcgi_cache_valid 60m;
         fastcgi_cache_bypass $http_pragma;  # Evita la cache con Ctrl+F5 (Pragma: no-cache)
         fastcgi_cache_key "$scheme$request_method$host$request_uri";
         fastcgi_cache_use_stale error timeout invalid_header http_500;
         fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
         add_header X-RunCloud-Cache $upstream_cache_status;
      }

      # Resto de configuración ...
   }

.. MIRAR: En algunas respuestas de internet el if es:
   $request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml"
   Quizás porque no usan ningún plugin.

.. seealso:: Para saber más sobre el cacheo de contenido dinámico, consulte
   :ref:`el apartado dedicado a ello <nginx-fastcgi-cache>`.

Para lo segundo, no hay más que instalar el *plugin* y, al configurarlo, indicar
cuál es el fichero de caché.

.. warning:: Sobre esta configuración es indispensable hacer dos precisiones:

   + El *plugin* no se comporta bien con las páginas del panel de administración,
     por lo que debemos evitar cachearlas según se propone en la configuración
     de más arriba.

   + Es una :ref:`instalación multisitio <nginx-wordpress-multi>` es necesario:

     - Configurar el *plugin* para cada blog particular.
     - El bloque:

       .. code-block:: nginx

          if (!-e $request_filename) {
                rewrite ^/wp-admin/network(/.*) /wp-admin$1 last;
                rewrite ^(?:/[^/]+)?(/.*) $1 last;
          }

       **no** debe situarse antes del bloque condicional que fija el valor de la
       variable *$skip_cache*.

Cacheo de contenido estático
""""""""""""""""""""""""""""
.. warning:: Las últimas versiones de los navegadores rechazan cargar contenido
   no seguro dentro de una página segura, por lo cual lo expuesto bajo este
   epígrafe resulta ya totalmente inútil. Se conserva, no obstante, por si
   resulta interesante el análisis de la técnica.

.. note:: Esta optimización es independiente de la anterior, así que si se
   quiere aplicar las dos no hay más que añadir los cambios expuestos bajo el
   epígrafe anterior a la configuración sugerida bajo éste.

.. warning:: Aunque bajo en el epígrafe se entremezclen, esta optimización sobre
   contenido estático, se basa en dos aspectos que pueden ser implementados
   independientemente:

   * En que :ref:`nginx <n-ginx>` indexe los contenidos estáticos para que sea
     capaz de servirlos más eficientemente (las sentencias
     :code:`open_file_cache`).

   * En que los contenidos estáticos se sirvan con otro nombre
     (*static.example.net*) a fin de que con este nombre la transferencia sea
     |HTTP| y no |HTTP|\ s, lo cual permitirá a proxies intermedios cachear ese
     contenido. Ahora bien, esto sólo funciona si el certificado es válido y
     acreditado, ya que en caso contrario el navegador rechazará los contenidos
     de *static.example.net* sin llegar a preguntarnos si confiamos en el
     certificado para este segundo sitio, pues nunca entramos explícitamente en
     él.

   Si utiliza un servicio como :ref:`Cloudflare <dos-cloudflare>` este segundo
   aspecto es absolutamente irrelevante, ya que este proxy se encarga de
   descifrar y recifrar para el cliente; y sólo tiene interés implementar el
   primer aspeecto.

La configuración sugerida para :program:`nginx` obliga a que toda la
comunicación sea segura. Sin embargo, que la configuración sea segura tiene un
pequeño inconveniente: inutiliza los *proxies* caché intermedios ya que todo
está cifrado, incluso las cabeceras. Por ello, vamos a hacer lo siguiente:

* Crearemos otro nombre de máquina *static.example.net*.
* Modificaremos la configuración de :program:`wordpress` para que el contenido
  estático lo pida a través de este nombre y para que las *cookies* estén
  asociadas exclusivamente al otro (*blog.example.net*).
* Modificamos :program:`nginx` para que al pedir contenido estático, excepto
  javascript y |CSS|\ [#]_, la petición se redirija al sitio no seguro.

**Ventajas**
   #. Como la solicitud de contenido estático no incluye *cookie* alguna,
      podemos cachear con el propio *nginx* el contenido.
   #. Si hay un *proxy* intermedio que cachea contenidos, todo nuestro contenido
      estático, excepto javascript y css, lo cacheará también.

**Desventajas**
   #. El contenido estático no seguro se pide dos veces: primero por |HTTP|\ s y
      luego por |HTTP|, que es cuando se sirve. En cualquier caso, como la
      redirección es permanente (código **301**), cada navegador sólo repetirá
      la petición una vez.

.. note:: El cacheo local de contenidos que permite a :program:`nginx` acelerar
   el servicio de contenido estático es independiente de que los contenidos se
   sirvan por protocolo seguro o no. Por tanto, podríamos poner en práctica la
   mitad de esta configuración (la referente a las directivas
   ``open_file_cache``) sin llegar a hacer la redirección hacia el sitio no
   seguro.

Para llevarlo a cabo hay que editar el fichero :file:`wp-config.php` del
directorio raíz de :program:`wordpress` y añadir estas tres líneas al comienzo
del fichero::

   define("WP_CONTENT_URL", "https://static.example.net/wp-content");
   define("COOKIE_DOMAIN", "blog.example.net");
   define("WP_PLUGIN_URL", "https://static.example.net/wp-content/plugins");

Y dejar una :download:`configuración para nginx como esta
<files/site-blog.alternativo>`\ [#]_:

.. literalinclude:: files/site-blog.alternativo
   :language: nginx
   :emphasize-lines: 6, 13, 14, 25-31, 46-60

.. note:: Si quiere utilizar esta optimización y la anterior, asegúrese
   de incluir el bloque que define la variable *$skip_cache* **fuera**
   del bloque :code:`location /`.

.. note:: Si el cambio se lleva a cabo, cuando el *blog* ya está en
   funcionamiento y contiene entradas y mensajes, entonces habría que cambiar
   en la base de datos referencias de *blog* a *static*::

      # mysql wordpress
      mysql> UPDATE wp_posts SET post_content = REPLACE(post_content,
       -> '//blog.example.net/wp-content/uploads/','//static.example.net/wp-content/uploads/');

.. warning:: En una :ref:`instalación multisitio <nginx-wordpress-multi>`,
   asegúrese de que el bloque:

   .. code-block:: nginx

       if (!-e $request_filename) {
          rewrite ^/wp-admin/network(/.*) /wp-admin$1 last;
          rewrite ^(?:/[^/]+)?(/.*) $1 last;
       }

   se encuentra fuera del bloque :code:`location /`.

.. _nginx-wordpress-multi:

Multisitio
==========
Las versiones modernas de :program:`wordpress` (a partir de la versión *3.0*)
permiten el alojamiento de múltiples *blogs* independientes en vez de uno sólo
(que es el caso que se ha visto bajo :ref:`el epígrafe anterior
<nginx-wordpress-mono>`).

Nuestra intención ahora es alojar una multitud de *blogs* bajo el dominio
*blogs.example.net*, de manera que se llegue a ellos a través de
subdirectorios\ [#]_.  Por tanto:

========= ====================================
Blog      Dirección
========= ====================================
principal ``https://blogs.example.net``
blog2     ``https://blogs.example.net/blog2``
etc       ``https://blogs.example.net/etc``
========= ====================================

Preliminares
------------
Siguiendo la :ref:`instalación para un único blog <nginx-wordpress-simple>`
necesitamos tener instalados en el servidor :ref:`nginx <n-ginx>`, |PHP| y
*MySQL*.  Hecho esto, procedemos a realizar una instalación de
:program:`wordpress` como si pretendiéramos configurarlo para un solo blog
usando el :download:`fichero de configuración <files/site-blog>` ya facilitado.

.. note:: Todo es igual salvo por el hecho de que, ya que la instalación nos
   servirá para varios sitios, hemos decidido:

   * Llamar a la máquina *blogs.example.net*.
   * Usar como directorio raíz :file:`/srv/www/blogs`.

   El fichero de configuración debe modificarse para que así sea.

Configuración
-------------
Completada la instalación de :program:`wordpress`, lo primero que debemos hacer
es añadir estas dos líneas a :file:`wp-config.php` a fin de que aparezcan las
opciones multisitio en la interfaz de administración::

   # Multisitio
   define('WP_ALLOW_MULTISITE', true);
   define('WPMU_ACCEL_REDIRECT', true);

Hecho lo cual, debemos volver a entrar en la interfaz de administración de
wordpress, donde veremos, ahora sí, la entrada ``Tools>Network Setup``. Basta
con elegir un nombre para nuestra red de blogs y pinchar sobre *Install*. El
propio :program:`wordpress` nos advertirá de qué debemos escribir en el fichero
:file:`wp-config.php`::

   define('MULTISITE', true);
   define('SUBDOMAIN_INSTALL', false);
   define('DOMAIN_CURRENT_SITE', 'blogs.example.net');
   define('PATH_CURRENT_SITE', '/');
   define('SITE_ID_CURRENT_SITE', 1);
   define('BLOG_ID_CURRENT_SITE', 1);

Con ello debemos modificar la configuración de :program:`nginx` para dejarlo de
:download:`esta otra forma <files/site-blog.multisitio>`:

.. literalinclude:: files/site-blog.multisitio
   :language: nginx
   :emphasize-lines: 1-4, 20, 31-34

Los añadidos no son muchos\ [#]_. En puridad, sólo es necesario el bloque
``if``.  El otro añadido, que implica usar ``map``, sólo sirve para dividir los
registros de acceso para que cada blog lo haga en fichero aparte. Para que esto
sea viable, además, es necesario que el usuario *www-data* pueda crear los
ficheros de registro en :file:`/var/log/nginx`::

   # chown www-data /var/log/nginx
   # chmod g+s /var/log/nginx

.. warning:: Por alguna razón, al hacer una instalación multisitio, la vista de
   las entradas pertenecientes a una categoría, deja de funcionar, porque
   *wordpress* convierte las direcciones en::

      http://blogs.example.net/nombre-blog/category/nombre-categoria

   Podemos soslayar este problema, configurando cada *blog* particular para que
   en ``Ajustes>Enlaces permanentes``, la ``Categoría base`` sea
   :file:`/category` u otra cualquiera de nuestra elección (p.e.
   :file:`/secciones`).

Optimizaciones
--------------
Como en el caso de un solo blog, es posible el cacheo de contenido estático y
dinámico. Como en la configuración multisitio los cambios son mínimos, no es
difícil implementarlos siguiendo :ref:`las instrucciones ya indicadas
<nginx-wordpress-optimo>`.

Postinstalación
===============

Extensiones indispensables
--------------------------
Completada la instalación, hay ciertas extensiones (*plugins*) que pueden
resultarnos muy útiles:

`nginx cache <https://es.wordpress.org/plugins/nginx-cache/>`_
   Plugin para gestionar una caché interna que aceleré *wordpress*. Se ha
   tratado al hablar sobre las optimizaciones.

   .. note:: En una instalación multisitio, es necesario tener en cuenta que
      hay que habilitar el plugin para cada sitio por separado.

`Advanced noCaptcha & invisible Captcha (v2 & v3) <https://es.wordpress.org/plugins/advanced-nocaptcha-recaptcha/>`_
   Habilita la `versión 2 <https://www.google.com/recaptcha/intro/v2.html>`_ o la `versión 3
   <https://www.google.com/recaptcha/intro/v3.html>`_ de Google reCaptcha. Es
   necesario generar en el enlace facilitado un par de claves con una cuenta de
   *Google* para poder usarlo, ya que la configuración lo requiere. Con las
   extensión podemos proteger el formulario de acceso, el de registro, la
   creación de comentarios y la petición para cambio de contraseña.

`Easy WP SMTP <https://es.wordpress.org/plugins/easy-wp-smtp/>`_
   *Plugin* indispensable para permitir que *Wordpress* use un servidor |SMTP|
   para expedir correos, en vez del módulo de |PHP| que por lo general provoca
   que los mensajes sean rechazados por los servidores y, en consecuencia, jamás
   sean entregados.

   Con la extensión podemos o usar nuestro servidor de correo local, si es que
   lo hemos instalado y configurado; o un servidor externo al que accedamos por
   autenticación.

`Adaptación RGPD/LOPD <https://es.wordpress.org/plugins/click-datos-lopd/>`_
   En mayo de 2018 entró en vigor el `Reglamento General de Protección de Datos
   <https://rgpd.es/>`_ que obliga a informar al usuario del tratamiento que se
   hará de sus datos y cómo será este. Para adecuar nuestro *Wordpress* a él
   este *plugin* es extraordinariamente útil.

   .. seealso:: `Esta entrada en la web de webempresa.com
      <https://www.webempresa.com/blog/rgpd-en-wordpress-guia-facil-aplicacion-normativa-reglamento.html>`_
      puede servirnos de mucha ayuda.

   .. Alternativa: https://es.wordpress.org/plugins/cookies-and-content-security-policy/
      En los ajustes se puede enlazar con una página que contenga la política de
      cookies.

Estas cuatro (o tres, si no optimizamos) extensiones debe ser de obligada
instalación, o bien, otras tales que hagan la misma tarea. La instalación de
otras extensiones dependerá de cuál sea el uso que demos al sitio.

Actualizaciones
---------------
Con los :ref:`permisos sugeridos <wp-permisos>`, tanto la actualización de
temas como la de *plugins*, debe llevarse a cabo a través de la interfaz web sin
problemas. La actualización a una versión de :program:`wordpress`, en cambio,
exige un cambio global de permisos::

   # chown -R www-data /srv/www/blog

para poder llevar a cabo la actualización. Una vez completada ésta, es
recomendable volver a los permisos anteriores::

   # chown -R root /srv/www/blog
   # chown -R www-data /srv/www/blog/wp-{config.php,content}

Usuarios
--------
Los usuarios de *Wordpress* tienen un rol, esto es, un distinto nivel de
privilegios:

#. *Administrador de la red* o *Superadministrador*, que será el administrador
   total de todo el sitio y podrá realizar cualquier tarea y, en exclusiva:

   - Instalar *plugins* en la aplicación.
   - Instalar *temas* en la aplicación\ [#]_.
   - Ajustar parámetros que afectan al funcionamiento general del sitio (activar
     el resgistro automático de usuarios, fijar el tamño máximo de archivo, etc.)

#. *Administrador de blog*\ [#]_, que es aquel que tiene la labor de gestionar
   por completo un *blog* particular del sitio.

#. *Editor*, que tiene capacidad de publicar entradas y de editar logias propias
   y ajenas.

#. *Autor*, capaz de publicar y editar sólo sus propias entradas.

#. *Colaborador*, que puede editar sus propiar entradas, pero no publicarlas.

#. *Suscriptor*, que sólo es capaz de leer entradas y editar su perfil.

.. warning:: En instalaciones *multisitio*, salvo el primero, como es natural,
   el resto de *roles* se tienen para un *blog* particular, de modo que
   un *usuario* puede tener un *rol* en un *blog* y otro *rol* distinto en otro. 

.. note:: En principio, el auto-registro de nuevos usuarios está deshabilitado y
   sólo es posible mediante invitación del *superadministrador*. El
   auto-registro puede habilitarlo el *superadministrador* en ``Ajustes>Ajustes
   de la red``. Sea como sea, el registro sólo implica el alta en la red de
   *blogs*, no el adquirir un rol determinado en un *blog* particular. Para esto
   segúndo, deberá el administrador de tal blog conferir uno concreto.

.. rubric:: Autenticación con *Google*

Es posible autenticarse en :program:`wordpress` mediante el sistema de
autenticación de *Google*, para lo cual debe instalarse un *plugin* apropiado
como `Login with Google <https://es.wordpress.org/plugins/login-with-google/>`_.
Hay que tener presente varios aspectos:

+ Se necesitarán unas credenciales de Google para hacer efectivo el plugin.
  Basta una cuenta en *Google* y acudir a la `consola de desarrollo
  <https://console.developers.google.com/>`_ para crer un proyecto y, dentro de
  él, unas credenciales.

+ La autenticación con *Google* permite la autenticación alternativa de
  *usuarios*  y puede configurarse el *plugin* para que si el usuario no existe,
  se genere automáticamente una cuenta.

Recetas
-------
Bajo el presente epígrafe se enumeran muy esquemáticamente algunas recetas para
incorporar contenido al sitio.

**Vídeo**
   Mediante incorporación de elementos ``iframe`` al contenido. En el caso
   particular de `youtube <http://www.youtube.com>`_ es posible, simplemente,
   incorporando en párrafo aparte el enlace en la vista *visual*::

      https://www.youtube.com/watch?v=e8TZbze72Bc

   Si se quiere tener más control sobre el formato (p.e. ajustar el tamaño),
   entonces debe recurrirse a añadir directamente el ``<iframe>``, cuyo código
   puede obtenerse del enlace "*Compartir*", y dentro de él "*Insertar*"

**Mapas** (de *Google*)
   Basta, de igual modo, con hacerse con el ``iframe`` de la vista que nos
   interese. El código puede obtenerse del enlace "*Compartir*".

   .. note:: Si lo que se pretende es crear mapas interactivos, siga las
      `instrucciones de esta página
      <https://neliosoftware.com/es/blog/anadir-mapas-de-google-maps-en-wordpress/#attachment_3517>`_.

.. note:: En general, cualquier objeto no-\ |HTML| que deseamos mostrar, podrá
   incustrarse recurriendo al código con ``<iframe>`` que se nos facilite (p.e.
   cualquier *podcast* ofrecido a través de la plataforma `ivoox
   <https://www.ivoox.com/>`_)

.. warning:: En los :program:`wordpress` multisitio sólo el superadministrador
   puede incrustar elementos ``<iframe>``. En el siguiente apartado se
   proporciona :ref:`una solución para sortear este inconveniente <wp-iframes>`.

Migración
---------
:program:`wordpress` incorpora un sencillo mecanismo para migrar un blog desde
un servidor de internet a otro distinto, que sigue el siguiente esquema:

.. image:: files/wp-migracion.png

.. note:: No se realiza la migración de un sitio entero, sino de un blog
   particular, lo cual debe tener en cuenta si la instalación es un
   :program:`wordpress` multisitio

Para poder llevar a cabo la migración:

En el **servidor web original**
   debemos exportar el *blog* a través de la entrada
   "Herramientas" del Escritorio del blog en cuestión. Tal acción generará un
   fichero |XML| que contendrá el texto y la estructura del blog. En cambio, del
   contenido multimedia, como las fotos, sólo se incluirá el enlace a su
   ubicación en el sitio original (*www.example.net* en el ejemplo).

En el **servidor web de destino**
   con wordpress instalado, se importa el
   contenido del *blog* también a través de la entrada "Herramientas"\ [#]_.
   Durante este proceso de importación, el *plugin* de importación se encargara
   de descargar desde el sitio web original el contenido multimedia y añadirlo a
   la biblioteca multimedia.

Es importante tener claro los siguientes aspectos de la migración:

+ Se migra un único *blog*.
+ Se importan contenido y usuarios\ [#]_, pero nada más: estilos y *plugins*
  corren de nuestra cuenta.
+ Durante el proceso los dos servidores deben estar activos.
+ Los servidores deben tener distinto nombre.

.. rubric:: ¿Y si no es posible mantener el línea el antiguo servidor?

El procedimiento es bastante sencillo al margen de que requiramos volver a
recrear el estilo o instalar *plugins*. Sin embargo, es absolutamente inútil, si
no disponemos del antiguo servidor en línea. Un caso en que esto no es posible
se produce cuando cambiamos el sistema de servidor, de modo que en el momento en
que tenemos el nuevo sistema en línea, el sistema ya ha dejado de existir por
pura sustitución.

A pesar de ello, podemos ingeniárnoslas para ensayar este esquema de migración:

.. image:: files/wp-migracion-auto.png

Esto es:

Antes de inutilizar el **antiguo servidor**
   descargamos el |XML| de importación y los ficheros multimedia en un paquete
   hecho con :ref:`tar <tar>`. El |XML| lo manipulamos para que los enlaces
   incluidos en él cambien el nombre de la máquina por *localhost*.

En el **nuevo servidor** 
   creamos un nuevo sitio web estático al que subimos los ficheros multimedia y
   configuramos el servidor web para que los ficheros se descarguen según sean
   enlazados en el |XML|. Hecho esto, procedemos a importar el |XML| tal como
   hicimos en el caso anterior y, si hemos procedido correctamente, la migración
   se resolverá sin problema. Finalmente, podremos borrar el sitio estático.

Paso a paso, y suponiendo que el nombre antiguo era *www.example.net*, debemos
hacer lo siguiente:

#. Exportar en |XML| el *blog* a través de su menú "Herramientas" (supongamos
   que lo llamamos :file:`blogs.xml`).
#. Crear un paquete con las imágenes del *blog*. Tales imágenes están dentro el
   subdirectorio :file:`wp-content/uploads`, así que crearemos el `tarball
   <http://computing.help.inf.ed.ac.uk/FAQ/whats-tarball-or-how-do-i-unpack-or-create-tgz-or-targz-file>`_
   con el contenido de tal fichero:: 

      # tar -C /srv/www/blogs/wp-content -acvf /tmp/multimedia.tar.gz uploads

   Obviamente, debemos rescatar el fichero del servidor.

   .. note:: Este *tarball* contendrá las ficheros multimedia de todos los *blogs*
      en caso de una instalación multisitio. Si desea extraer sólo los ficheros
      del sitio en cuestión debe hacer para el caso del sitio principal::

         # tar --exclude=uploads/sites -C /srv/www/blogs/wp-content -acvf /tmp/multimedia.tar.gz uploads
            
      y para el caso de cualquier otro *blog*::

         # tar -C /srv/www/blogs/wp-content -acvf /tmp/multimedia.tar.gz uploads/sites/N

      donde **N** es **2**, **3**, **4**... y es un número que identifica al
      blog en cuestión y se asigna en el momento de la creación. Para saber qué
      número tiene asignado cada *blog* basta con consultar la ruta de uno de
      sus imágenes.

#. Modificamos el |XML| para sustituir el nombre *www.example.net* por
   *localhost*::

      # sed -ri 's;((http)s(://))?www\.example\.net\b;\2\3localhost;g' blog.xml

   .. note:: La expresión, además de modificar el nombre, convierte el protocolo
      |HTTP|\ s en |HTTP| en caso de que sea necesario.

#. Subimos el paquete al nuevo servidor, y lo descomprimimos en un lugar
   adecuado::

      # mkdir -p /srv/www/BORRAR
      # tar -C /srv/www/BORRAR -axvf /tmp/multimedia.tar.xz

#. Creamos un sitio adecuado para que sirva los ficheros multimedia manteniendo
   su ruta original (excepto porque ahora la máquina será *localhost*):

   .. code-block:: nginx

      # /etc/nginx/sites-availables/BORRAR
      server {
         listen 80;
         server_name localhost;

         access_log /var/log/nginx/access.BORRAR.log;
         error_log  /var/log/nginx/error.BORRAR.log;

         location /SITIO/wp-content {
            alias /srv/www/BORRAR;
         }
      }

   "*SITIO*" es el nombre del sitio que aparece en la ruta, y que tuvo que
   definirse al crearlo. En el caso del blog principal debemos eliminarlo
   por completo con lo que la localización será, simplemente,
   :file:`/wp-content`.

   .. note:: Si queremos estar seguros de que hemos hecho correctamente la
      configuración podemos intentar desde el propio servidor acceder a alguna
      de las imágenes cuyo *link* contenga el |XML|. Por ejemplo::

         $ wget -S --spider http://localhost/wp-content/uploads/2019/01/foto.jpg
           HTTP/1.1 200 OK
           Server: nginx/1.10.3
           Date: Tue, 15 Jan 2019 17:48:47 GMT
           Content-Type: image/jpeg
           ...

      o un enlace parecido.

   .. _wp-iframes:

#. Para evitar que :program:`wordpress` se niegue a descargar los ficheros de
   una |IP| privada y que no importe correctamente los ``<iframe>``, si no se
   usa como usuario importador el superadministrador, es necesario subir
   :download:`este fichero al servidor <files/myfunctions.php>`, colocarlo en el
   directorio de plugins::

      # mv /tmp/myfunctions.php /srv/www/blogs/wp-content/plugins/

   y activarlo en la sección de *plugins* en la que debe aparecer como *plugin*
   disponible.

#. Instalamos los *plugins* que sean necesarios para que funcione el *blog* que
   importamos. Por ejemplo, si usamos formularios de
   `Contact Form 7 <https://es.wordpress.org/plugins/contact-form-7/>`_
   necesitaremos tener instalada el *plugin* previamente.

#. Si la instalación es *multisitio*, creamos un nuevo *blog* sobre el que
   importaremos el antiguo. Antes (o después de la importación) convendrá borrar
   la entrada "¡Hola, mundo!", porque la importación no elimina los contenidos
   que ya contuviera el blog.

#. Aplicamos sobre el blog el *tema* que tuviera en el sitio original.

#. Procedemos a la importación a través del menú "Herramientas". Si no hemos
   realizado otra importación anteriormente, el *plugin* importador no se
   encontrará instalado, por lo cual deberemos previamente instalarlo bien desde
   la página de instalación de *plugins* (se llama `Wordpress Importer
   <https://es.wordpress.org/plugins/wordpress-importer/>`_), bien desde la
   página de importación del *blog* principal.
  
   .. warning:: Si lo hace de este segundo modo en una instalación multisitio,
      asegúrese de volver al *blog* en el que realmente quiere hacer la
      importación.

#. Restablecer la configuración del servidor para que no quede ni rastro
   de nuestra argucia::

      # rm /srv/www/BORRAR -rf
      # rm /etc/nginx/sites-{available,enabled}/BORRAR -f
      # invoke-rc.d nginx restart

.. note:: Si el *blog* importado tenía muchas entradas, es posible que el
   proceso se demore demasiado y ante la falta de respuesta por parte del
   procesador |PHP| el servidor cancele la operación. Para evitarlo puede
   :ref:`aumentar el tiempo de temporización <timeout-php>`.

Enlaces de interés
------------------

* `Curso de Wordpress con videotutoriales
  <https://www.webempresa.com/wordpress/curso-wordpress-gratis.html>`_.

* `Blog con recetas para Wordpress <https://decodecms.com/>`_

.. rubric:: Notas al pie

.. [#] ``fastcgi_cache`` permite el valor *off*, así que puede temporalmente
   incluirse la configuración necesaria pero con este valor inicialmente y,
   cuando se tenga totalmente instalado todo, incluido el plugin necesario,
   cambiar su valor al nombre de la caché que escogiéramos (*wp-cache*, en
   nuestra configuración).

.. [#] Los navegadores bloquean este tipo de contenido si es inseguro y la
   página que lo carga segura.

.. [#] Otra pequeña diferencia de esta configuración respecto a la anterior es
   como tratamos los registros: por defecto no registramos los accesos y sólo
   cuando se ejecuta un *script* sí se hace. Esto nos evita el uso de los
   *snippets* anteriores.

.. [#] Hay otra posible configuración multisitio que consiste en reservar un
   subdominio para cada blog, de manera que las direcciones quedaría así:

   ========= ====================================
   Blog      Dirección
   ========= ====================================
   principal ``https://blogs.example.net``
   blog2     ``https://blogs2.blogs.example.net``
   etc       ``https://etc.blogs.example.net``
   ========= ====================================

   Esta configuración no se tratará aquí.

.. [#] Descontado, claro, está que hemos deficido poner "*blogs*" donde antes
   decía "*blog*".

.. [#] En instalaciones *monositio* los roles de *Administrador del sitio* y
   *Administrador del blog* se fusionan.

.. [#] Los administradores de los blogs tienen libertad para habilitar o
   deshabilitar plugins o cambiar el tema en el blog que gestionan, pero siempre
   sobre aquellos plugins y temas que hayan sido instalados por el
   *Administrador de la red*.

.. [#] El importador es un *plugin* que debe instalarse, pero la propia entrada
   "Herramientas" facilita su instalación sin necesidad de recurrir a la sección
   de *plugins*.

.. [#] La importación detectará los usuarios que hayan participado en el *blog*
   antiguo y nos permitirá para cada uno:

   + Crear un nuevo usuario que lo sustituya.
   + Asimilar el usuario a uno ya existente en el blog.

   .. todo:: Es posible que esto sólo sea posible si se importa desde la cuenta
      de superadministrador. Debe comprobarse.

.. |CMS| replace:: :abbr:`CMS (Content Management System)`
.. |PHP| replace:: :abbr:`PHP (PHP Hypertext Preprocessor)`
.. |CSS| replace:: :abbr:`CSS (CSS Cascade StyleSheets)`
.. |HTML| replace:: :abbr:`HTML (HyperText Markup Language)`
.. |XML| replace:: :abbr:`XML (eXtensible Markup Language)`