En esta ocasión vamos a tirar a la basura la mayor parte del código que ya tenemos. Así es la vida de dura… cuando estamos aprendiendo, las cosas se hacen de una manera; cuando ya sabemos, las cosas se hacen de otra manera diferente.

Hoy queremos que nuestros “feedbacks” se adapten a drupal. Hasta ahora parecían formar parte del site, pero realmente sólo tenían el mismo aspecto. Lo que queremos conseguir es que realmente pertenezcan a nuestro drupal: aparecerán en las búsquedas, se administrarán como parte del contenido, etc.

Me voy a basar en el magnífico tutorial para crear contenido en Drupal.

Versiones

Igual que es posible que este documento se copie, también es posible que yo lo modifique. Por eso, la última versión del mismo siempre se encontrará en la cuarta parte del tutorial de drupal. Un saludo.

Qué se explica aquí

Cómo crear un nodo programáticamente. Una vez hayamos terminado, todo lo que hayamos hecho se podría haber hecho con el módulo CCK, pero de lo que se trata es aprender.

Qué es el CCK

Fácil: es el Content Construction Kit :-D

Consiste en una herramienta muy potente para la creación de formularios mediante el propio Drupal. De esta manera, sólo tenemos que ir añadiendo una lista de elementos, que posteriormente se renderizarán como un formulario. Gran parte de nuestro módulo “feedback” se podría haber hecho directamente sobre el CCK, por no decir todo.

¿Por qué no usar el CCK? En nuestro caso, “porque estamos aprendiendo” es una razón tan válida como cualquier otra. En concreto, para un módulo de las características de “feedback”, tal vez haya sido una pérdida de tiempo usar un módulo en lugar de el CCK. Sin embargo, un módulo tiene unas características de replicabilidad que el CCK creo que no tiene.

Además, el CCK nos permite crear formularios simples, pero si lo que queremos es poder manipular los datos, entonces necesitaremos crear nuestro propio módulo.

Pasos

Vamos a necesitar los siguientes hooks, aunque algunos de ellos ya los hemos implementado:

  • hook_node_info
  • hook_perm
  • hook_access
  • hook_form

También vamos a necesitar algunos de los siguientes hooks opcionales:

  • hook_insert
  • hook_update
  • hook_delete
  • hook_validate
  • hook_nodeapi
  • hook_view
  • hook_load

Hooks obligatorios

Nosotros ya tenemos nuestro hook_node_info y nuestro propio hook_perm, así que saltamos directamente a implementar el hook_access:

<?php
/**
* Implements hook_access()
*/
function feedback_access ( $op, $node, $account )
{
  $retval = false;

  switch ( $op )
  {
    case 'view':
      $retval = user_access ( 'view messages', $account );
    case 'create':
      $retval = user_access ( 'send message', $account );
      break;
    case 'update':
      $retval = user_access ( 'admin messages', $account ) && ( $account->uid == $node->uid );
      break;
    case 'delete':
      $retval = user_access ( 'admin messages', $account ) && ( $account->uid == $node->uid );
      break;
  }

  return $retval;
}
?>

La función va a devolver true si queremos que el usuario tenga acceso. Para ello comprobamos si el usuario tiene los roles necesarios como para realizar ciertas acciones. En el caso de “update” y “delete” comprobamos también que el usuario sea el propietario del nodo.

Siguiendo con la lista, implementaremos el hook_node_info:

<?php
/**
* Implements hook_node_info()
*/
function feedback_node_info() {
  return array(
    'feedback' => array(
      'name' => t('Feedback'),
      'module' => 'feedback',
      'description' => "Handle a feedback message",
    ),
  );
}
?>

Ahora vamos a renombrar algunas de nuestras funciones… En concreto, feedback_message_form, que pasará a ser feedback_form. Es necesario también renombrar las funciones feedback_message_form_validate y feedback_message_form_submit para que todo siga funcionando como hasta ahora. También tendremos que modificar la llamada a feedback_message_form (ojo, que luego seguiremos transformando estas funciones):

<?php
function feedback_message ()
{
  $content = '';

  $content .= drupal_get_form ( 'feedback_form' );

  return $content;
}

function feedback_form ()
{
  [...]
}

function feedback_form_validate ( $form, &$form_state )
{
  [...]
}

function feedback_form_submit ( $form, &$form_state )
{
  [...]
}
?>

Y…. ya está. Ya tenemos nuestro elemento integrado con Drupal. Si recargamos la caché (Administration→Performance), y nos vamos a “create content”, veremos que tenemos un tipo de contenido llamado “feedback”.

El caso es que también veremos que no funciona del todo bien :-D

Hooks recomendables

Antes de nada, os voy a hablar de hook_nodeapi. Hacéos un favor a vosotros mismos: olvidad que alguna vez existió. Las funciones que tienen parámetros que cambian en función de otros parámetros nunca son una buena idea. Además, en Drupal 7 lo han quitado, por lo que su uso está obsoleto. Toda la funcionalidad que podía ofrecer esta función la cumplen el resto de los hooks de la lista: insert, update, delete, validate, view y load. Existe una remota posibilidad de que necesitéis usar hook_nodeapi; pensáoslo bien antes de usarla.

El resto de hooks se ejecutarán automáticamente cuando alguien realice la operación esperada: hook_insert cuando se cree un nuevo elemento, etc.

Realmente lo que vamos a hacer es realizar una serie de transformaciones. Por ejemplo, nuestro antiguo feedback_form_submit se va a transformar en el nuevo y flamante feedback_insert, nuestro propio hook_insert:

<?php
function feedback_insert ( $node )
{
  $userid  = $node->uid;
  $message = $node->message;
  $mail    = $node->mail;

  $query = "INSERT into {feedback_messages} ( uid, message, indate, usermail ) values ( %d, '%s', now(), '%s' ) ";
  $queryResult = db_query ( $query, $userid, $message, $mail );

  if ( db_affected_rows() )
  {
    drupal_set_message ( t ( "Thank you for your feedback." ) );
    drupal_goto ( 'node' );
  }
  else
    form_set_error ( 'feedback', t( "An error has happened. Your feedback has not been sent." ) );
}
?>

Vamos a estudiarlo un poquito… Hemos cambiado la cabecera, de manera que ahora el UID, el mensaje y el e-mail lo sacamos del nodo en lugar de usar variables globales y formularios. Nosotros tenemos que decidir cómo almacenar la información que sobrepasa a la información del nodo, por lo que insertamos en nuestra tabla exactamente la misma información que antes. Es decir: a partir de $query no hemos cambiado nada.

No ha sido difícil, ¿verdad? Ahora ya podemos insertar nodos que son feeds. Y podemos probarlo: Create Content→feedback, escribimos y guardamos.

Se nos ha quedado el submit del formulario. Eso queda un poquito feo, ¿no? Ahora que tenemos nuestro botón “preview” y nuestro “save”, amén de todas las opciones de publicación, permisos, etc… Así que vamos a quitarlo. Además hay un problema: se va a utilizar el mismo formulario para editar y para crear nuevos elementos. Por esa razón, he decidido sacar una serie de variables que se establecerán cuando se esté editando el nodo. Además, en modo “edición” se mostrará también el check para indicar que ya se ha leído.

<?php
function feedback_form ( &$node )
{
  global $user;

  $title = sprintf ( 'Feed from %s', $node->name );
  $mail = "";
  $message = "";
  $read = false;
  $editing = false;

  if ( $user->uid != 0 )
    $mail = $user->mail;

  if ( $node->nid > 0 )
  {
    // estamos editando
    $editing = true;
    $query = "SELECT uid, message, indate, usermail, fm.read FROM {feedback_messages} fm WHERE id=%d";
    $queryResult = db_query ( $query, $node->nid );
    $storednode = db_fetch_object ( $queryResult );

    $mail    = $storednode->usermail;
    $message = $storednode->message;
    $read    = $storednode->read;
  }

  $form ['feedback'] = array
    (
      '#type' => 'fieldset',
      '#title' => t( 'Feedback' ),
      'mail' => array ('#type' => 'textfield', "#title"=>"E-Mail", "#description"=>"If you want any answer, please, tell me your mail. It would not be shown anywhere.", "#default_value" => $mail ),
      'message' => array ( '#type' => 'textarea', "#description"=>"Write here your message", '#default_value'=>$message ),
      '#required' => TRUE,
    );

  if ( $editing )
  {
    $form ['feedback']['read'] = array ( '#type' => 'checkbox', "#title"=>"The message has been read", '#default_value'=>$read ) ;
  }

  $form['title'] = array
    (
      '#type' => 'hidden',
      '#default_value' => $title,
    );

  return $form;
}
?>

¡Eh!, ¡Para!, que aquí al final hay algo extraño… Sí, lo que hacemos es preguntarle a Drupal el tipo de nodo que estamos tratando, y comprobar si este tipo necesita un título. Si es así, pues lo pintamos. Se podría hacer lo mismo con el body, pero en esta ocasión no lo queremos. El título es interesante para que después nos aparezca en la lista de contenido y podamos pincharle.

Ahora vamos a realizar las comprobaciones adecuadas, renombrando feedback_form_validate y haciendo los cambios pertinentes:

<?php
function feedback_validate ( $node, &$form )
{
  $message = $form['values']['message'];
  $mail    = $form['values']['mail'];
  $validmail = preg_match ("/^[^0-9][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}$/" , $mail );

  if ( $mail == '' && $node->uid == 0 )
    form_set_error ( 'mail', t('You are not a registered user. Please, insert your e-mail.') );
  if ( $mail != '' && ! $validmail )
    form_set_error ( 'mail', t('The mail does not like an e-mail address.') );
  if ( $message == '' )
    form_set_error ( 'message', t('Message cannot be empty.'));

  return $form;
?>

Es una lástima ver lo fácil que habría resultado realizarlo así desde el principio, ¿verdad? ¡¡Pero había que aprender!! Pasito a pasito…

Ahora vamos a “ver” los feeds. No sé si habréis notado que cuando pulsáis sobre un feed, no se ve nada. Igual que en el preview. Eso es porque tenemos que implementar el hook_view:

<?php
function feedback_view ( $node, $teaser = FALSE, $page = FALSE )
{
  $messageread = false;
  $messagedata = "";
  $messagename = "";
  $messageusermail = "";

  if ( $node->message )
  {
    // ya tenemos el mensaje. Puede deberse a que estamos editando.
    $messageread = $node->read;
    $messagedata = $node->message;
    $messagename = $node->name;
    $messageusermail = $node->mail;
  }
  else
  {
    // tenemos que obtener el mensaje
    $query = "SELECT fm.uid, message, indate, usermail, u.name, fm.read FROM {feedback_messages} fm, {users} u WHERE id=%d and fm.uid=u.uid";
    $queryResult = db_query ( $query, $node->nid );
    $message = db_fetch_object ( $queryResult );

    $messageread = $message->read;
    $messagedata = $message->message;
    $messagename = $message->name;
    $messageusermail = $message->usermail;
  }

  $read = "";
  if ( $messageread )
    $read = t(" and <b>already read</b>");

  $content  = sprintf ( t('<blockquote><i>%s</i></blockquote><p>Sent by %s<a href="mailto:%s">&lt;%s&gt;</a>%s.</p>'), $messagedata, $messagename, $messageusermail, $messageusermail, $read );


  $node = node_prepare ( $node, $teaser );
  $node->content['message'] = array(
    '#value' => $content,
    '#weight' => 1,
  );

  return $node;
}
?>

Ahora vamos con la edición, para que se hagan efectivos los cambios:

<?php
function feedback_update ( $node )
{
  $userid  = $node->uid;
  $message = $node->message;
  $mail    = $node->mail;
  $read    = $node->read;

  $query = "UPDATE {feedback_messages} fm SET message='%s', usermail='%s', fm.read=%d where id=%d";
  $queryResult = db_query ( $query, $message, $mail, $read, $node->nid );

  if ( db_affected_rows() )
  {
    drupal_set_message ( t( "The feedback has been updated." ) );
    drupal_goto ( 'node' );
  }
  else
    form_set_error ( 'feedback', t( "An error has happened. Your feedback has not been sent." ) );
}
?>

Ahora una nota sobre el borrado: Si tratamos de borrar el nodo, veremos que se realiza sin problemas, pero eso no quiere decir que lo estemos haciendo BIEN. Se habrá borrado el nodo, pero no se habrá borrado la información asociada a nuestro feed, dejando basura inaccesible en la base de datos. Por eso implementaremos:

<?php
/**
* Implementation of hook_delete
*/
function feedback_delete ( $node )
{
  $query = "DELETE FROM {feedback_messages} where id=%d";
  $queryResult = db_query ( $query, $node->nid );
}
?>

Con esto, queda obsoleto lo que implementamos en la cuarta entrega del curso para poder eliminar mensajes :-D. Claro, que también hay una serie de enlaces anticuados que nos están permitiendo crear feeds que no son nodos. Por estas razones, vamos a eliminar:

  • feedback_delete_form_submit
  • feedback_delete_form
  • feedback_messageadmin_form_submit
  • feedback_messageadmin_form_submit
  • feedback_messageadmin_form

Y vamos a cambiar los enlaces en feedback_admin:

<?php
function feedback_admin ( $messageid=null )
{
  $content = '';

  if ( $messageid != null )
  {
    $content .= drupal_get_form ( 'feedback_messageadmin_form', $messageid );
    $content .= "<hr/>";
  }

  $header = array
  (
    array('data' => t('Date'), 'field' => 'indate', 'sort' => 'desc'),
    array('data' => t('User'), 'field' => 'name'),
    array('data' => t('email'), 'field'=>'mail'),
    array('data' => t('Message'), 'field' => 'message'),
    array('data' => t('Read'), 'field'=>'fm.read'),
  );

  $query = "SELECT fm.id, u.uid as uid, u.name, fm.usermail, fm.indate, fm.message, fm.read FROM {feedback_messages} fm, {users} u WHERE fm.uid=u.uid";
  $query .= tablesort_sql ( $header );
  $queryResult =  db_query ( $query );

  $rows = array ();
  while ( $message = db_fetch_object ( $queryResult ) )
  {
    $row = array ();
    $row['indate'] = $message->indate;
    $row['name'] = $message->uid != 0 ? '<a href="?q=user/'. $message->uid .'">' . $message->name . '</a>' : $message->name;
    $row['mail'] = $message->usermail;
    $row['message'] = $message->message;
    $row['read'] = $message->read;
    $row['edit'] = l ( t('edit'), "node/" . $message->id . "/edit");
    $rows[] = $row;
  }

  $content .= theme_table($header, $rows);
  return $content;
}

Y lo más importante: ¡¡Ahora no podemos permitir que se cree un elemento feedback sin que sea un nodo!! Así que se tercian dos cambios: Cambiar el enlace del bloque (acordaos de limpiar la caché en Administration→performance) y eliminar el acceso del hook_menu:

<?php
/**
* Implementation of hook_block().
*/
function feedback_block ( $op = 'list', $delta = 0, $edit = array() )
{
  if ( $op == "list")
  {
      $blocks = array();
      $blocks[0]["info"] = t('Feedback');
      $blocks[1]["info"] = t('New Feedbacks');
      return $blocks;
  }
  else if ( $op == "view" )
  {
      $content = '';
      $block = array();
      switch ( $delta )
      {
        case 0:
          $block['subject'] = t('Feedback');

          $options = array( "attributes" => array("title" => t("Sends a feedback message") ) );
          $link = l( t("New Feedback"), "node/add/feedback", $options );
          $content .= '<div class="link">' . $link . "</div>";
          break;
        case 1:
          $block['subject'] = t('Feedback Messages');

          $options = array( "attributes" => array("title" => t("Shows feedback messages") ) );
          $title = t("There are @total new feeds", array ( '@total' => _feedback_count() ) );
          $link = l( $title , "feedback/adminmessages", $options );
          $content .= '<div class="link">' . $link . "</div>";
          break;
      }
      $block['content'] = $content;
      return $block;
  }
}

/**
* Implementation of hook_menu().
*/
function feedback_menu ( )
{
  $items = array();

  $items['feedback/adminmessages'] = array
  (
    'title' => 'Feedback admin',
    'page callback' => 'feedback_admin',
    'access arguments' => array('view messages'),
    'type' => MENU_CALLBACK,
  );

  return $items;
}
?>

Drupal 7

Mucho me temo que todo lo que cuento aquí es válido para Drupal 6, pero no para Drupal 7.

Hay tutoriales de cómo pasar de Drupal 6 a Drupal 7, y seguro que todo lo contado en esta parte del tutorial se verá afectado.

Más tutoriales

Tengo en mente hacer un tutorial más y terminar con toda la tanda. Lo que queda por ver es sencillo: aplicar themes y añadir archivos de traducción. Si se os ocurre algo más, ¡¡decidlo ahora!!

Opinión personal

En muchos casos puede venir bien crear un módulo para drupal que gestiona nodos. Sin embargo, también es cierto que en este caso a mí, particulamente, me parece un problema. Al ser nodos, se verán afectados por los permisos de nodos, por lo que si en tu site no dejas crear contenido, se acabaron los feeds.

De todas maneras, me parecía interesante contarlo de esta manera, con el fin de que quede como una continuación y se aprenda a crear contenido para Drupal, que no es nada difícil

Código

Como en este caso no se ha modificado nada del feedback.install, pongo sólo el módulo principal:

feedback.module

<?php
/*
Copyright 2009-2010 Miguel Ángel García Martínez <miguelangel.garcia@gmail.com>

  'Feedback' is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

  'Feedback' is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
along with 'Feedback' (maybe in file "COPYING"); if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
/**
* Implements hook_help().
*/
function feedback_help ( $path, $arg )
{
  $output=''; // para construir la salida
  switch ( path )
  {
    case 'admin/help#feedback':
      $output .= '<p>' . t('Módulo que permite la inserción de mensajes de feedback.') . '</p>';
      break;
  }
  return $output;
}

/**
* Implements hook_perm().
*/
function feedback_perm ()
{
  return array ( 'send message', 'view messages', 'admin messages' );
}

/**
* Implements hook_access()
*/
function feedback_access ( $op, $node, $account )
{
  $retval = false;

  switch ( $op )
  {
    case 'view':
      $retval = user_access ( 'view messages', $account );
    case 'create':
      $retval = user_access ( 'send message', $account );
      break;
    case 'update':
      $retval = user_access ( 'admin messages', $account ) && ( $account->uid == $node->uid );
      break;
    case 'delete':
      $retval = user_access ( 'admin messages', $account ) && ( $account->uid == $node->uid );
      break;
  }

  return $retval;
}

/**
* Implements hook_node_info()
*/
function feedback_node_info() {
  return array(
    'feedback' => array(
      'name' => t('Feedback'),
      'module' => 'feedback',
      'description' => "Handle a feedback message",
    ),
  );
}

/**
* Implementation of hook_block().
*/
function feedback_block ( $op = 'list', $delta = 0, $edit = array() )
{
  if ( $op == "list")
  {
      $blocks = array();
      $blocks[0]["info"] = t('Feedback');
      $blocks[1]["info"] = t('New Feedbacks');
      return $blocks;
  }
  else if ( $op == "view" )
  {
      $content = '';
      $block = array();
      switch ( $delta )
      {
        case 0:
          $block['subject'] = t('Feedback');

          $options = array( "attributes" => array("title" => t("Sends a feedback message") ) );
          $link = l( t("New Feedback"), "node/add/feedback", $options );
          $content .= '<div class="link">' . $link . "</div>";
          break;
        case 1:
          $block['subject'] = t('Feedback Messages');

          $options = array( "attributes" => array("title" => t("Shows feedback messages") ) );
          $title = t("There are @total new feeds", array ( '@total' => _feedback_count() ) );
          $link = l( $title , "feedback/adminmessages", $options );
          $content .= '<div class="link">' . $link . "</div>";
          break;
      }
      $block['content'] = $content;
      return $block;
  }
}

/**
* Implementation of hook_menu().
*/
function feedback_menu ( )
{
  $items = array();

  $items['feedback/adminmessages'] = array
  (
    'title' => 'Feedback admin',
    'page callback' => 'feedback_admin',
    'access arguments' => array('view messages'),
    'type' => MENU_CALLBACK,
  );

  return $items;
}

function feedback_message ()
{
  $content = '';

  $content .= drupal_get_form ( 'feedback_form' );

  return $content;
}

function feedback_form ( &$node )
{
  global $user;

  $title = sprintf ( 'Feed from %s', $node->name );
  $mail = "";
  $message = "";
  $read = false;
  $editing = false;

  if ( $user->uid != 0 )
    $mail = $user->mail;

  if ( $node->nid > 0 )
  {
    // estamos editando
    $editing = true;
    if ( $node->message )
    {
      // estamos previsualizando datos
      $mail    = $node->mail;
      $message = $node->message;
      $read    = $node->read;
    }
    else
    {
      $query = "SELECT uid, message, indate, usermail, fm.read FROM {feedback_messages} fm WHERE id=%d";
      $queryResult = db_query ( $query, $node->nid );
      $storednode = db_fetch_object ( $queryResult );

      $mail    = $storednode->usermail;
      $message = $storednode->message;
      $read    = $storednode->read;
    }
  }

  $form ['feedback'] = array
    (
      '#type' => 'fieldset',
      '#title' => t( 'Feedback' ),
      'mail' => array ('#type' => 'textfield', "#title"=>"E-Mail", "#description"=>"If you want any answer, please, tell me your mail. It would not be shown anywhere.", "#default_value" => $mail ),
      'message' => array ( '#type' => 'textarea', "#description"=>"Write here your message", '#default_value'=>$message ),
      '#required' => TRUE,
    );

  if ( $editing )
  {
    $form ['feedback']['read'] = array ( '#type' => 'checkbox', "#title"=>"The message has been read", '#default_value'=>$read ) ;
  }

  $form['title'] = array
    (
      '#type' => 'hidden',
      '#default_value' => $title,
    );

  return $form;
}

function feedback_insert ( $node )
{
  $userid  = $node->uid;
  $message = $node->message;
  $mail    = $node->mail;

  $query = "INSERT into {feedback_messages} ( id, uid, message, indate, usermail ) values ( %d, %d, '%s', now(), '%s' ) ";
  $queryResult = db_query ( $query, $node->nid, $userid, $message, $mail );

  if ( db_affected_rows() )
  {
    drupal_set_message ( t ( "Thank you for your feedback." ) );
    drupal_goto ( 'node' );
  }
  else
    form_set_error ( 'feedback', t( "An error has happened. Your feedback has not been sent." ) );
}

function feedback_update ( $node )
{
  $userid  = $node->uid;
  $message = $node->message;
  $mail    = $node->mail;
  $read    = $node->read;

  $query = "UPDATE {feedback_messages} fm SET message='%s', usermail='%s', fm.read=%d where id=%d";
  $queryResult = db_query ( $query, $message, $mail, $read, $node->nid );

  if ( db_affected_rows() )
  {
    drupal_set_message ( t ( "The feedback has been updated." ) );
    drupal_goto ( 'node' );
  }
  else
    form_set_error ( 'feedback', t( "An error has happened. Your feedback has not been sent." ) );
}

/**
* Implementation of hook_delete
*/
function feedback_delete ( $node )
{
  $query = "DELETE FROM {feedback_messages} where id=%d";
  $queryResult = db_query ( $query, $node->nid );
}

function feedback_validate ( $node, &$form )
{
  $message = $node->message;
  $mail    = $node->mail;
  $validmail = preg_match ("/^[^0-9][A-z0-9_]+([.][A-z0-9_]+)*[@][A-z0-9_]+([.][A-z0-9_]+)*[.][A-z]{2,4}$/" , $mail );

  if ( $mail == '' && $node->uid == 0 )
    form_set_error ( 'mail', t('You are not a registered user. Please, insert your e-mail.') );
  if ( $mail != '' && ! $validmail )
    form_set_error ( 'mail', t('The mail does not like an e-mail address.') );
  if ( $message == '' )
    form_set_error ( 'message', t('Message cannot be empty.'));

  return $form;
}

function feedback_view ( $node, $teaser = FALSE, $page = FALSE )
{
  $messageread = false;
  $messagedata = "";
  $messagename = "";
  $messageusermail = "";

  if ( $node->message )
  {
    // ya tenemos el mensaje. Puede deberse a que estamos editando.
    $messageread = $node->read;
    $messagedata = $node->message;
    $messagename = $node->name;
    $messageusermail = $node->mail;
  }
  else
  {
    // tenemos que obtener el mensaje
    $query = "SELECT fm.uid, message, indate, usermail, u.name, fm.read FROM {feedback_messages} fm, {users} u WHERE id=%d and fm.uid=u.uid";
    $queryResult = db_query ( $query, $node->nid );
    $message = db_fetch_object ( $queryResult );

    $messageread = $message->read;
    $messagedata = $message->message;
    $messagename = $message->name;
    $messageusermail = $message->usermail;
  }

  $read = "";
  if ( $messageread )
    $read = t(" and <b>already read</b>");

  $content  = sprintf ( t('<blockquote><i>%s</i></blockquote><p>Sent by %s<a href="mailto:%s">&lt;%s&gt;</a>%s.</p>'), $messagedata, $messagename, $messageusermail, $messageusermail, $read );


  $node = node_prepare ( $node, $teaser );
  $node->content['message'] = array(
    '#value' => $content,
    '#weight' => 1,
  );

  return $node;
}

function feedback_admin ( $messageid=null )
{
  $content = '';

  if ( $messageid != null )
  {
    $content .= drupal_get_form ( 'feedback_messageadmin_form', $messageid );
    $content .= "<hr/>";
  }

  $header = array
  (
    array('data' => t('Date'), 'field' => 'indate', 'sort' => 'desc'),
    array('data' => t('User'), 'field' => 'name'),
    array('data' => t('email'), 'field'=>'mail'),
    array('data' => t('Message'), 'field' => 'message'),
    array('data' => t('Read'), 'field'=>'fm.read'),
  );

  $query = "SELECT fm.id, u.uid as uid, u.name, fm.usermail, fm.indate, fm.message, fm.read FROM {feedback_messages} fm, {users} u WHERE fm.uid=u.uid";
  $query .= tablesort_sql ( $header );
  $queryResult =  db_query ( $query );

  $rows = array ();
  while ( $message = db_fetch_object ( $queryResult ) )
  {
    $row = array ();
    $row['indate'] = $message->indate;
    $row['name'] = $message->uid != 0 ? '<a href="?q=user/'. $message->uid .'">' . $message->name . '</a>' : $message->name;
    $row['mail'] = $message->usermail;
    $row['message'] = $message->message;
    $row['read'] = $message->read;
    $row['edit'] = l ( t('edit'), "node/" . $message->id . "/edit");
    $rows[] = $row;
  }

  $content .= theme_table($header, $rows);
  return $content;
}

/** Devuelve el número de feeds creados en la última semana
*/
function _feedback_count ()
{
  $query = "SELECT count(id) as total from {feedback_messages} fm where fm.read != 1 or isnull(fm.read)";
  $queryResult =  db_query ( $query );
  $result = db_fetch_object ( $queryResult ) ;

  return $result->total;
}
?>