Contenido

Creación de un módulo Drupal IV

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.

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?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):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?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:

1
2
3
4
5
6
7
8
9
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?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

Más tutoriales

Este tutorial es la continuación de una serie de tutoriales relacionados con Drupal. Podéis consultar cualquiera de ellos:

Código

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

feedback.module

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
<?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;
}