Lo hice y lo entendí

El blog de Vicente Navarro
19 may

Desinstalar plugins de WordPress. Los casos de “subscribe to comments”, “WP Ajax Edit Comments” y WP-OpenID.

A WordPress le queda bastante que evolucionar en el área de la desinstalación de plugins. Primero, en lo que es propiamente el borrado de los ficheros y directorios que se añaden a wp-contents/plugins, ya que sería deseable que se pudiera hacer rápida y fácilmente desde el panel de administración. Pero segundo, y muchísimo más importante, en lo que respecta a la “basurilla” que los diferentes plugins han añadido a la base de datos y que tras borrar el plugin queda ahí por los siglos de los siglos a menos que nos arremanguemos y nos pongamos a borrar esos restos a mano. La verdad es que no sería mucho pedir que cada plugin se encargara de limpiar lo que deja en la base de datos.

En realidad, se podría argumentar que esos datos tienen que permanecer ahí por si alguna vez queremos volver a instalar el plugin, tener ahí disponible la configuración que se ajusta a nuestro entorno. Es decir, como los ficheros y directorios que comienzan por punto del $HOME de nuestros sistemas Linux, que son ficheros de configuración de los diferentes productos y que permanecen ahí aunque desinstalemos un producto, por si algún día decidimos volver a instalarlo. Pero en realidad, la comparación no es justa, ya que no es lo mismo en términos de sencillez hacer “rm -rf $HOME/.mozilla” o “rm -rf $HOME/.gnome” que tener que ir buscando por la base de datos qué se habrá dejado ahí tal o cual plugin.

Bueno, y esto no es que sea algo que piense yo sólo. También opinan así en:

La verdad es que ahora mismo cualquier responsabilidad en el área de la desinstalación de un plugin se deja enteramente en los desarrolladores de los mismos. Pero sin embargo, yo creo que a poco que hubiera una mínima infraestructura en la API de WordPress para fomentar o facilitar la desinstalación de plugins, muchos desarrolladores que hoy en día ignoran completamente el área de la desinstalación, probablente pasaran a considerarla.

En mi opinión, esto podría materializarse en que en el panel de administración de plugins, al lado de los enlaces para activar/desactivar y editar un plugin, que hubiera otro para limpiar la base de datos que llamara a alguna función predeterminada de cada plugin que se encargara de eso, de eliminar toda la información de la base de datos que sea responsabilidad suya. Así, si quisieras desactivar o eliminar los ficheros del plugin pero sin borrar su configuración de la base de datos por si la necesitaras en el futuro, que pudieras hacerlo sin problemas.

Hay plugins que no alteran la base de datos para nada, y que la poca configuración que necesitan se hace en el propio fichero PHP del plugin, como el Better Feed o el 1 Blog Cacher. No es muy cómodo personalizarlos, pero son muy limpios.

Hay otros plugins que lo único que hacen es guardar sus opciones en la tabla de la base de datos wp-options a través de las funciones de WordPress add_option/update_option/get_option/delete_option. Además, si examinamos la sencilla esctructura de dicha tabla:

mysql> desc wp_options;
+--------------+-------------+------+-----+---------+----------------+
| Field        | Type        | Null | Key | Default | Extra          |
+--------------+-------------+------+-----+---------+----------------+
| option_id    | bigint(20)  | NO   | PRI | NULL    | auto_increment | 
| blog_id      | int(11)     | NO   | PRI | 0       |                | 
| option_name  | varchar(64) | NO   | PRI |         |                | 
| option_value | longtext    | NO   |     |         |                | 
| autoload     | varchar(20) | NO   | MUL | yes     |                | 
+--------------+-------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)

y vemos cómo suelen usarla los distintos plugins, nos daremos cuenta de que a menudo (aunque no siempre) sólo se usa una única fila por cada plugin, ya que éstos suelen gestionar un único array de opciones en una sóla pareja option_name/option_value, como vemos que hace, por ejemplo, el plugin “subscribe to comments“:

add_option('sg_subscribe_settings', array(
'use_custom_style' => '',
'email' => get_bloginfo('admin_email'), 
'name' => get_bloginfo('name'), 
'header' => '[theme_path]/header.php', 
'sidebar' => '', 
'footer' => '[theme_path]/footer.php', 
'before_manager' => '<div id="content" class="widecolumn subscription-manager">', 
'after_manager' => '</div>', 
'not_subscribed_text' => __('Notify me of followup comments via e-mail', 'subscribe-to-comments'), 
'subscribed_text' => __('You are subscribed to this entry.  <a href="[manager_link]">Manage your subscriptions</a>.', 'subscribe-to-comments'),
'author_text' => __('You are the author of this entry.  <a href="[manager_link]">Manage subscriptions>/a<.', 'subscribe-to-comments'),
'version' => $this->version));

En estos casos, para saber qué tenemos que borrar para eliminar los restos de los plugins que sólo tienen algunas opciones en wp-comments, sólo tenemos que mirar el código del plugin y revisar las llamadas a las funciones add_option o update_option para ver la(s) option_name que usa y eliminarla sin compasión con una sentencia SQL como esta:

mysql> delete from wp_options where option_name='nombre_de_la_opcion';
Query OK, 1 row affected (0.01 sec)

Pero también hay otros plugins que modifican más agresivamente la base de datos. En la mayoría de los casos, de forma fundamentada, ciertamente, ya que es necesario para su tarea. Pero sin embargo, esos plugins que tan alegremente añaden tablas o columnas, no tienen la capacidad de deshacer esos cambios con la misma facilidad.

Entre los plugins que yo conozco que entran en esta categoría están el WP Ajax Edit Comments, el WP-OpenID, o el subscribe to comments.

subscribe to comments

Yo instalé el “subscribe to comments” (Traducción al español de Subscribe to Comments 2.1) unos días antes de la entrada ¡Qué día el de aquella semana!, y la verdad es que va muy bien para cuando algún visitante está muy interesado en seguir los comentarios de alguna de las entradas. Al principio pensaba que no tendría mucho éxito, pero pronto me di cuenta de que mucha gente se suscribía a diferentes entradas. Sinceramente, creo que es mejor seguir los comentarios a través del RSS de los comentarios, pero veo que en general resulta más cómodo lo de los e-mails. Otra cosa que me da un poco de miedo con este plugin es que alguien no se acuerde de que se ha suscrito a alguna entrada y que cuando le lleguen e-mails los marque como spam dándole tal vez a entender a su proveedor de e-mail que el dominio vicente-navarro.com es fuente de spam.

En cualquier caso, pasemos a ver cómo eliminar los restos que deja si en algún momento decidimos (como yo me lo planteo a menudo), dejar de usar este plugin.

En primer lugar, vemos en el código PHP que añade una nueva columna a la tabla wp_comments llamada comment_subscribe para poder anotar si el autor de ese comentario desea subscribirse para recibir posteriores comentarios por e-mail:

$wpdb->query("ALTER TABLE $wpdb->comments ADD COLUMN comment_subscribe enum('Y','N') NOT NULL default 'N'");

de forma que la estructura de dicha tabla pasa a ser esta:

mysql> desc wp_comments;
+----------------------+---------------------+------+-----+---------------------+----------------+
| Field                | Type                | Null | Key | Default             | Extra          |
+----------------------+---------------------+------+-----+---------------------+----------------+
| comment_ID           | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment | 
| comment_post_ID      | int(11)             | NO   | MUL | 0                   |                | 
| comment_author       | tinytext            | NO   |     |                     |                | 
| comment_author_email | varchar(100)        | NO   |     |                     |                | 
| comment_author_url   | varchar(200)        | NO   |     |                     |                | 
| comment_author_IP    | varchar(100)        | NO   | MUL |                     |                | 
| comment_date         | datetime            | NO   |     | 0000-00-00 00:00:00 |                | 
| comment_date_gmt     | datetime            | NO   | MUL | 0000-00-00 00:00:00 |                | 
| comment_content      | text                | NO   |     |                     |                | 
| comment_karma        | int(11)             | NO   |     | 0                   |                | 
| comment_approved     | varchar(20)         | NO   | MUL | 1                   |                | 
| comment_agent        | varchar(255)        | NO   |     |                     |                | 
| comment_type         | varchar(20)         | NO   |     |                     |                | 
| comment_parent       | bigint(20)          | NO   |     | 0                   |                | 
| user_id              | bigint(20)          | NO   |     | 0                   |                | 
| comment_subscribe    | enum('Y','N')       | NO   |     | N                   |                | 
+----------------------+---------------------+------+-----+---------------------+----------------+
16 rows in set (0.00 sec)

Con la siguiente sentencia SQL podríamos ver quiénes son dichos visitantes y a qué entradas se han suscrito:

mysql> select com.comment_author com, com.comment_author_email, post.post_title from wp_comments com, wp_posts post where com.comment_subscribe='Y' and post.ID=com.comment_post_ID;

Además, el plugin también permite suscribirte sin tener que dejar un comentario, pero para que eso funcione, hay que introducir la siguiente línea en algún sitio del tema que estamos usando para que aparezca un formulario que nos pide el e-mail al que queremos recibir las notificaciones:

<?php show_manual_subscription_form(); ?>

Si lo tenemos activo, esta información no se guarda en la tabla wp_comments, ya que la suscripción no está asociada a ningún comentario en concreto, sino que se guarda en la tabla wp_postmeta, asociada únicamente a la entrada, como vemos en el código:

add_post_meta($postid, '_sg_subscribe-to-comments', $email);

Y vemos que el meta_value usado es el _sg_subscribe-to-comments, así que podemos consultar los usuarios suscritos de esta forma así:

mysql> select meta_value from wp_postmeta where meta_key='_sg_subscribe-to-comments';
+--------------------+
| meta_value         |
+--------------------+
| user1@example.com  | 
| user2@example.com  | 
+--------------------+
2 rows in set (0.00 sec)

Además, imaginemos que alguien se hace pasar por otra persona y se suscribe con el e-mail de esa otra persona. Cuando la persona suplantada comienza a recibir e-mails, tiene un enlace que le permite bloquear el envío de dichas notificaciones y que nunca más le puedan volver a suscribir. Para dicha funcionalidad, el “subscribe to comments” usa otra opción en la tabla wp_options llamada do_not_mail, que es lo que vemos en la función add_block:

        function add_block($email='') {
                if ( !is_email($email) )
                        $email = $this->email;
                global $wpdb;
                $email = strtolower($email);
 
                // add the option if it doesn't exist
                add_option('do_not_mail', '');
 
                // check to make sure this email isn't already in there
                if ( !$this->is_blocked($email) ) {
                        // email hasn't already been added - so add it
                        $blocked = get_settings('do_not_mail') . ' ' . $email;
                        update_option('do_not_mail', $blocked);
                        return true;
                        }
                return false;
        }
mysql> select option_value from wp_options where option_name='do_not_mail';
+---------------------------------------------------+
| option_value                                      |
+---------------------------------------------------+
| vicente.navarro@example.com supercoco@example.net |
+---------------------------------------------------+
1 row in set (0.00 sec)

Y ahora que ya hemos visto todos los cambios que hace el plugin en la base de datos, pasemos a ir deshaciéndolos todos. Por supuesto, es totalmente recomendable hacer un backup de la base de datos antes de aventurarnos a hacer cualquier modificación. Tras desactivar/desinstalar el plugin, empezamos con las opciones de wp_options.

mysql> delete from wp_options where option_name='sg_subscribe_settings';                            
Query OK, 1 row affected (0.00 sec)

mysql> select option_value from wp_options where option_name='sg_subscribe_settings';                         
Empty set (0.00 sec)
mysql> delete from wp_options where option_name='do_not_mail';
Query OK, 1 row affected (0.01 sec)

mysql> select option_value from wp_options where option_name='do_not_mail';                         
Empty set (0.00 sec)

Seguimos eliminando la información de los usuarios suscritos sin comentario:

mysql> delete from wp_postmeta where meta_key='_sg_subscribe-to-comments';                          
Query OK, 2 rows affected (0.00 sec)
 
mysql> select meta_value from wp_postmeta where meta_key='_sg_subscribe-to-comments';               
Empty set (0.01 sec)

Y finalmente, eliminamos la columna extra que nos ha añadido a wp_comments y comprobamos que los campos de la tabla han cambiado:

mysql> alter table wp_comments drop column comment_subscribe;
Query OK, 1891 rows affected (0.49 sec)
Records: 1891  Duplicates: 0  Warnings: 0

mysql> desc wp_comments;
+----------------------+---------------------+------+-----+---------------------+----------------+
| Field                | Type                | Null | Key | Default             | Extra          |
+----------------------+---------------------+------+-----+---------------------+----------------+
| comment_ID           | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment | 
| comment_post_ID      | int(11)             | NO   | MUL | 0                   |                | 
| comment_author       | tinytext            | NO   |     |                     |                | 
| comment_author_email | varchar(100)        | NO   |     |                     |                | 
| comment_author_url   | varchar(200)        | NO   |     |                     |                | 
| comment_author_IP    | varchar(100)        | NO   | MUL |                     |                | 
| comment_date         | datetime            | NO   |     | 0000-00-00 00:00:00 |                | 
| comment_date_gmt     | datetime            | NO   | MUL | 0000-00-00 00:00:00 |                | 
| comment_content      | text                | NO   |     |                     |                | 
| comment_karma        | int(11)             | NO   |     | 0                   |                | 
| comment_approved     | varchar(20)         | NO   | MUL | 1                   |                | 
| comment_agent        | varchar(255)        | NO   |     |                     |                | 
| comment_type         | varchar(20)         | NO   |     |                     |                | 
| comment_parent       | bigint(20)          | NO   |     | 0                   |                | 
| user_id              | bigint(20)          | NO   |     | 0                   |                | 
+----------------------+---------------------+------+-----+---------------------+----------------+
15 rows in set (0.01 sec)

WP Ajax Edit Comments

El “WP Ajax Edit Comments”, del que ya hablé en ¡Ya se pueden revisar los comentarios!, guarda unas claves de seguridad cada vez que un usuario deja un comentario, pero el propio plugin tiene una funcionalidad para eliminarlas en el panel de opciones del plugin. Y si queremos hacerlo después de haberlo desactivado/desinstalado, sólo tenemos que hacer lo que el código en PHP del plugin ya nos sugiere:

 $query = "delete from $wpdb->postmeta where left(meta_value, 6) = 'wpAjax'";

es decir, en SQL, un:

mysql> delete from wp_postmeta where left(meta_value,6)='wpAjax';
Query OK, 62 rows affected (0.15 sec)

Además, este plugin usa dos entradas en la tabla wp_comments:

mysql> select option_name from wp_options where option_name like 'WPAjaxEdit%';
+-----------------------------+
| option_name                 |
+-----------------------------+
| WPAjaxEditAuthorUserOptions | 
| WPAjaxEditComments          | 
+-----------------------------+
2 rows in set (0.01 sec)

que habría que eliminar con:

mysql> delete from wp_options where option_name='WPAjaxEditAuthorUserOptions' or option_name='WPAjaxEditComments';
Query OK, 2 rows affected (0.07 sec)

WP-OpenID

El WP-OpenID, del que hablé en OpenID en WordPress.org y cómo usar WordPress.com como proveedor de identidad, es el plugin menos considerado con la base de datos de WordPress que conozco.

Para empezar, crea tres tablas nuevas:

mysql> show tables;     
+-------------------------+
| Tables_in_wordpress_dev |
+-------------------------+
...
| wp_openid_associations  | 
| wp_openid_identities    | 
| wp_openid_nonces        | 
...
+-------------------------+
16 rows in set (0.00 sec)

Añade la columna openid a la tabla wp_comments para poder indicar que ese comentario se ha dejado tras haber comprobado la identidad con OpenID:

mysql> desc wp_comments;
+----------------------+---------------------+------+-----+---------------------+----------------+
| Field                | Type                | Null | Key | Default             | Extra          |
+----------------------+---------------------+------+-----+---------------------+----------------+
...
| openid               | tinyint(1)          | NO   |     | 0                   |                | 
...
+----------------------+---------------------+------+-----+---------------------+----------------+
16 rows in set (0.00 sec)

Luego, añade hasta cinco entradas en la tabla wp_options:

update_option( 'oid_enable_commentform', isset($_POST['enable_commentform']) ? true : false );
update_option( 'oid_enable_approval', isset($_POST['enable_approval']) ? true : false );
update_option('oid_plugin_revision', WPOPENID_PLUGIN_REVISION );
update_option('oid_db_revision', WPOPENID_DB_REVISION );
update_option('oid_plugin_enabled', true);
mysql> select option_name from wp_options where option_name like 'oid_%';
+------------------------+
| option_name            |
+------------------------+
| oid_db_revision        | 
| oid_enable_approval    | 
| oid_enable_commentform | 
| oid_plugin_enabled     | 
| oid_plugin_revision    | 
+------------------------+
5 rows in set (0.00 sec)

Y además, añade un wp_usermeta a cada usuario llamado has_openid con la función de WordPress update_usermeta:

update_usermeta( $userdata->ID, 'has_openid', (empty($identities) ? false : true) );
mysql> select user_id, meta_value from wp_usermeta where meta_key='has_openid';
+---------+------------+
| user_id | meta_value |
+---------+------------+
|       1 | 1          | 
+---------+------------+
1 row in set (0.02 sec)

Si queremos eliminar todo esto, tras desactivar/desinstalar el plugin, podemos eliminar las nuevas tablas:

mysql> drop table wp_openid_associations;
Query OK, 0 rows affected (0.03 sec)

mysql> drop table wp_openid_identities;  
Query OK, 0 rows affected (0.02 sec)

mysql> drop table wp_openid_nonces;    
Query OK, 0 rows affected (0.02 sec)

La columna extra de la tabla wp_comments:

mysql> alter table wp_comments drop column openid;
Query OK, 1891 rows affected (0.57 sec)
Records: 1891  Duplicates: 0  Warnings: 0

Las opciones de wp_options:

mysql> delete from wp_options where option_name like 'oid_%';         
Query OK, 5 rows affected (0.03 sec)

Y las entradas de wp_usermeta:

mysql> delete from wp_usermeta where meta_key='has_openid';
Query OK, 1 row affected (0.03 sec)

Conclusiones

Los plugins de WordPress pueden llegar a introducir grandes cambios en nuestra base de datos de WordPress. Por ello, antes de instalar alegremente un plugin, es importante hacer pruebas en un entorno de desarrollo y asegurarnos de que ese plugin realmente tiene las funcionalidades que necesitamos y que funciona bien antes de aventurarnos a probar decenas de plugins en nuestro WordPress de producción, ya que la inmensa mayoría de ellos dejan alguna huella en la base de datos, en unos casos grande, y en otros, mínima, pero normalmente algo dejan.

Si a pesar de haber estudiado el funcionamiento de un plugin y haber decidido ponerlo en producción por sus bondades, en algún momento decidimos que ya no es lo que queríamos o que no funciona bien, o lo que sea, y queremos desinstalarlo eliminando cualquier rastro de su presencia, tendremos que hacer un ejercicio de investigar qué es lo que ha dejado por la base de datos.

Para empezar, si tiene un panel de opciones, seguro que habrá guardado algunas opciones en wp_options. Para ver cuáles, tenemos que ir al código del plugin y buscar las llamadas a la función update_option.

Si tiene opciones asociadas al usuario, es posible que haya guardado algo en wp_usermeta. Para ver qué, tenemos que ir al código del plugin y buscar las llamadas a la función update_usermeta.

Es posible incluso que el plugin guarde información asociada a cada entrada, en wp_postmeta. Para ver si es así, tenemos que ir al código del plugin y buscar llamadas a la función update_post_meta.

Además, el plugin puede decidir ignorar las funciones estándar de WordPress para modificación de la base de datos y actuar directamente sobre ella, lo que puede ser más difícil de ver en el código sin examinarlo más detenidamente.

En los casos detallados anteriormente, por ejemplo, podemos ver ejemplos de plugins que crean tablas y añaden columnas a las existentes. Para buscar dichos casos, podemos buscar en el código por “create table” o por “alter table” (sin distinguir entre mayúsculas y minúsculas, ya que a menudo las sentencias SQL se escriben en mayúsculas).

Para finalizar, conceder el Premio “Lo hice y lo entendí” al plugin más respetuoso con la base de datos a…

…por la posibilidad que nos da en el panel de configuración del plugin de eliminar todas sus opciones de configuración de la base de datos.

:wq

Entradas relacionadas

3 Comentarios a “Desinstalar plugins de WordPress. Los casos de “subscribe to comments”, “WP Ajax Edit Comments” y WP-OpenID.”

  • albertjh dice:

    Sabes… es de las primeras veces que me entero a la primera de un post tuyo, normalmente requieren mucho esfuerzo por mi parte, pero al ver tablas mysql, comandos, wordpress, etc me ha quedado claro :-P

    Empiezo a coger ya bases importantes en esto ejejeje, puedo decir que “ya me entero!”, cosa que no lo hacia muy a menudo :-P

    Saludos!

  • @albertjh Pues me alegro de que “te enteres” en este caso ;-) , pero me queda un poco la sensación de que tal vez no haya sido capaz en otras entradas previas de explicarme bien causando yo mismo el síndrome de “no me entero”… 8O

  • albertjh dice:

    Jajajaj que va, lo que pasa que sobre lo que escribes es superior a mi nivel de entendimiento, lo mismo me pasa cuando en mi blog escribo sobre el sistema de ficheros en linux, seguro que la mayoría ni se entera, yo trato de hacerlo “entendible” pero se hace lo que se puede, por eso digo que en muchos de tus post me los tengo que releer un par de veces porque ni a la segunda me entero, pero al final acabo de enterarme te lo aseguro…

    Tu sigue como hasta ahora, que lo haces muy muy bien ;-) Saludos!

Tema LHYLE09, creado por Vicente Navarro