Be a happy coder

You are here

Mostrar mensajes del sistema para un usuario, en llamadas como anónimo

20 Dic 2011

Recientemente me he encontrado con la necesidad de mostrar mensajes a un usuario, pero el código se ejecutaba como respuesta a una llamada desde un servicio externo. ¿Cómo resolver esto?

Si nuestro drupal tiene una parte de tienda que se comunica con un TPV, suele suceder que nos responda un servicio asíncrono informándonos del pago. En este caso tengo un ubercart con el módulo uc_sermepa para comunicación con el TPV, pero estoy implementando un hook_order para controlar el momento de respuesta del servicio del TPV. Entre otras cosas, se genera un mensaje con drupal_set_message que se muestra al usuario o mejor dicho, se mostraría si en ese momento la ejecución que llega al hook_order fuera el usuario del sistema, ya que el usuario es uid 0.

Obvio, el TPV envía un mensaje asíncrono a la url que le hemos indicado, donde el uc_sermepa intercepta los datos que le llegan, comprueba que los datos son correctos y procesa el pago, con lo cual se dispara el hook_order, pero claro, esto es un usuario anónimo.

Bien, la solución encontrada tal vez no sea la más elegante, pero es realmente efectiva. No podemos utilizar drupal_set_message, puesto que almacena nuestro mensaje al usuario en la variable global $_SESSION, así que lo almacenaremos en la base de datos. Pero tampoco veo la necesidad de crear una nueva tabla para algo tan "efímero", puesto que una vez mostrado el mensaje, será borrado.

Utilizaremos la tabla users, almacenando el o los mensajes en el campo data del usuario.

Esta sería la función para realizar esto:


/**
* Almacena un mensaje para el usuario indicado, en base de datos.
*
* Es un estilo a drupal_set_message, pero orientado a BBDD, para guardar mensajes dirigidos
* cuando no tenemos el usuario cargado.
* Para obtener y borrar los mensajes del usuario, dejar el parámetro $message a NULL.
*
* @author Javier Díaz
*
* @param type $message Mensaje para el usuario
* @param type $type Tipo de mensaje
* @param type $uid Identificador de usuario
* @return Array con los mensajes o FALSE si ocurre algún error
*/
function module_set_message($uid, $message = NULL, $type = 'status'){

// No se permite mensajes al usuario anónimo
if ($uid return FALSE;
}

// Obtener los datos del usuario
$data = db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $uid));

// Si el usuario no existe, salir
if (!$data){
return FALSE;
}

$user_data = unserialize($data);

// Almacenar mensaje
if ($message) {
if (!isset($user_data['my_messages'])) {
$user_data['my_messages'] = array();
}

if (!isset($user_data['my_messages'][$type])) {
$user_data['my_messages'][$type] = array();
}

// No admitir mensajes repetidos
if (!in_array($message, $user_data['my_messages'][$type])) {
$user_data['my_messages'][$type][] = $message;
}
}
// Obtener y borrar mensajes
else {
$messages = $user_data['my_messages'];
unset($user_data['my_messages']);
}

// Guardar cambios
$data = serialize($user_data);
$success = db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", array($data, $uid));

return ($success && isset($messages))? $messages : FALSE;
}

Y para mostrar los mensajes, utilizamos el hook_user para darle al usuario los mensajes que tiene pendientes, justo cuando lo cargamos. Al mismo tiempo, podemos borrar dichos mensajes para no volver a mostrarlos de nuevo, con lo que conseguimos nuestro objetivo:


/**
* Implementación de hook_user()
*
* Gestión de los mensajes introducidos con kallejeo_set_message()
*
* @param type $op
* @param type $edit
* @param type $account
* @param type $category
*/
function module_user($op, &$edit, &$account, $category = NULL) {
if ($op =='load'){
// Comprobar si hay mensajes
if ($my_messages = module_set_message($account->uid)){

// Lanzar al usuario sus mensajes
foreach($my_messages as $type => $messages){
foreach($messages as $message){
drupal_set_message($message, $type);
}
}
}
}
}

Ahora sólo nos queda llamar a nuestra función cuando sea necesario y listo.

module_set_message($order->uid, 'This is a message for you!');

Obviamente, lo más lógico es utilizar drupal_set_message siempre, esto sólo es para situaciones en las que el mensaje se perdería y cambiar todo el workflow sería muy complicado ;-)