Contenido

Creación de un módulo Drupal II

Tomando como referencia el tutorial Creación de un módulo Drupal, vamos a añadirle la posibilidad de “ver” los comentarios, marcándolos como leídos.

Drupal

Qué se explica aquí

En esta ocasión veremos cómo:

  • Actualizar la base de datos
  • Crear enlaces desde una tabla
  • Crear un formulario de edición para una tabla.
  • Entraremos un poquito más en profundidad en algunos temas.

Notas previas

En la primera parte del tutorial ya traté de inculcaros que sólo se debe abrir el tag de <?php, y no se debe cerrar con ?>. En esta ocasión no seré tan pesado.

El primer tutorial fue algo rápido, ya que era muy largo. En los próximos tutoriales, iré ampliando conceptos para que podáis desarrollar vuestros propios módulos. Prometo que serán más cortos.

Modificando la BBDD

Nuestro primer problema es que la base de datos va a cambiar. Necesitamos una columna nueva para la tabla feedback_messages, llamada “read”, (de ’leído’). Va a ser un booleano. Como tenemos nuestro plugin en producción, no nos apetece tener que estar desinstalándolo, instalándolo de nuevo, etc, perdiendo todos los comentarios que nos hayan puesto. Otra opción es modificar la base de datos a mano, pero no deja de ser igual de tedioso, y no podemos pedirles a nuestros usuarios que lo hagan así.

Vamos a hacerlo bien, editando feedback.install y añadiendo lo siguiente:

1
2
3
4
5
function feedback_update_6100() {
  $ret = array();
  db_add_field($ret, 'feedback_messages', 'read', array('type' => 'int', 'size' => 'tiny'));
  return $ret;
}

En la función virtualcoin_schema que creamos en la primera parte, lo que hicimos fue crear un enorme array de arrays. Realmente se le llama “schema”, ya que es el esquema de nuestra base de datos. Podríamos haber usado sentencias SQL, pero el uso de este esquema nos permite ser independientes de la base de datos que use el futuro usuario de nuestro módulo.

En este caso, completamos el schema añadiéndole la columna nueva, que, como veréis, no es booleana como dije. Eso es porque el tipo booleano no existe en Drupal. Podéis consultar la lista de tipos drupal.

La función se llama feedback_update_6100, ya que es del módulo “feedback”, es un “update”, para la versión 6 de drupal y es el que debe ejecutarse en primer lugar, si es que tuviéramos más actualizaciones que aplicar. No me voy a detener a explicar el por qué de los numeritos.

Después es necesario irnos a “update.php” y seguir las instrucciones. Con eso se nos actualizarán los módulos (en este caso, nuestro módulo) y tendremos la tabla actualizada.

Modificando los menús

Vamos a modificar el hook_menu.

Como ODIO que me pongan sólo parte de una función, os pego el código entero:

 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
<?php
/**
* Implementation of hook_menu().
*/
function feedback_menu ( )
{
  $items = array();

  $items['feedback/message'] = array
  (
    'title' => t('Feedback'),
    'page callback' => 'feedback_message',
    'access arguments' => array('send message'),
    'type' => MENU_CALLBACK,
  );
  $items['feedback/adminmessages/%'] = array
  (
    'title' => t('Feedback Admin'),
    'page callback' => 'feedback_admin',
    'page arguments' => array ( 2 ),
    'access arguments' => array('view messages'),
    'type' => MENU_CALLBACK,
  );
  $items['feedback/adminmessages'] = array
  (
    'title' => t('Feedback Admin'),
    'page callback' => 'feedback_admin',
    'access arguments' => array('view messages'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

Hay dos cambios respecto de la original. ¿Los véis? ¿No?

El primero consiste en el nombre del menú. Antes era feedback/adminmessages, y ahora feedback/adminmessages/%. Drupal irá tratando de encajar la ruta que hayan insertado con alguno de los patrones; el porcentaje (%) es como decir: cualquier cosa. En este caso, encajará con cualquier cosa que tenga: feedback/adminmessages/ y algo más (ojo, ya que no encaja si no tiene nada más).

El segundo cambio es la opción page arguments. Le asignamos el valor array (2):. Eso implica tomar la 3ª parte de la ruta y meterla en un vector que se le pasará a nuestra función.

Voy a explicarlo con un ejemplo. El usuario pone la ruta feedback/adminmessages/25. Por lo tanto, si ponemos el valor de page arguments a 0, obtendremos feedback; a 1, adminmessages y a 2, “25”, que es justo lo que queremos.

¿Qué ocurre si el usuario pone feedback/adminmessages/25/añsldfj? Pues que vuelve a encajar en el patrón dado (a falta de otro mejor), y se trataría exactamente igual que el ejemplo anterior (la parte añsldfj ignorará).

Dejamos también la parte que existía antes, con el fin de poder pintar también algo cuando no se ha seleccionado un mensaje.

Modificando funciones

Bueno, pues ya tenemos modificados los menus, así que vamos a modificar también las funciones.

Comenzamos por la función feedback_admin. En esta función vamos a hacer tres cambios:

 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
<?php
function feedback_admin ( $messageid=null )
{
  $content = '';

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

  $header = array
  (
    array('data' => t('Date'), 'field' => 'indate', 'sort' => 'desc'),
    array('data' => t('User'), 'field' => 'name'),
    array('data' => t('Message'), 'field' => 'message'),
  );

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

  $rows = array ();
  while ( $message = db_fetch_object ( $queryResult ) )
  {
    $row = array ();
    $row['name'] = $message->name;
    $row['indate'] = $message->indate;
    $row['message'] = $message->message;
    $row['edit'] = l ( t('edit'), "feedback/adminmessages/" . $message->id );
    $rows[] = $row;
  }

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

En primer lugar, hemos añadido un parámetro con un valor por defecto.

En segundo lugar, se añade la comprobación para saber si a la función se le llamó con algún parámetro (en caso contario, habría obtenido el valor por defecto, que es NULL). Si es así, pintamos un formulario (más adelante).

Y para finalizar, le hemos añadido una nueva fila a cada elemento de la lista, de manera que se creen enlaces a la dirección feedback/adminmessages/ seguida del id del mensaje a mostrar.

NOTA: ¿Qué ocurre si un usuario malicioso trata de escribir esta dirección sin tener permisos? No tenemos que preocuparnos. Recordad que al definir los menús (apartado anterior) establecimos los permisos.

Claro, la función anterior nos está pidiendo a gritos que implementemos el formulario que nos falta y que, por ser un formulario, requiere también de un submit:

 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
<?php
function feedback_messageadmin_form ( $form, &$messageid )
{
  $query = "SELECT fm.message, fm.read, fm.indate, u.name FROM {feedback_messages} fm, {users} u WHERE fm.id=%d and u.uid=fm.uid";
  $queryResult = db_query ( $query, $messageid );
  $message = db_fetch_object ( $queryResult );

  $form = array
    (
      'feedback' => array
      (
        '#type' => 'fieldset',
        '#title' => t( 'Feedback from ' . $message->name . ' sended on ' . $message->indate ),
        'messageid' => array ( '#type' => 'hidden', "#default_value" => $messageid ),
        'message' => array ( '#type' => 'textarea', "#default_value" => $message->message, "#disabled" => TRUE ),
        'read'  => array ( '#type'=> 'checkbox', "#title" => "Read","#default_value" => $message->read ),
        'submit' => array ( '#type' => 'submit',  '#value' => t('Submit'),  ),
      ),
    );
    return $form;
}

function feedback_messageadmin_form_submit ( $form, &$form_state )
{
  $messageid = $form_state['values']['messageid'];
  $read      = $form_state['values']['read'];

  $query = "UPDATE {feedback_messages} fm set fm.read=%d where fm.id=%d";
  $queryResult = db_query ( $query, $read, $messageid );
}

Una explicación breve: Hemos creado un formulario con un campo oculto (messageid) para albergar el id del mensaje. Además, añadimos el propio mensaje en un textarea deshabilitado, con el fin de que no se pueda modificar. Por último, añadimos un checkbox para marcar como leído, que es lo que puede hacer el administrador. Bueno, claro: y el botón de submit.

En este caso no hemos implementado un hook_validate debido a lo sencillo que era el formulario (en un formulario con un único checkbox es difícil hacer marranadas), pero sí hemos implementado el hook_submit, que tan sólo actualiza el mensaje en cuestión.

Y, para ya dejarlo todo niquelao, vamos a modificar la función que obtenía el número de mensajes a ver. Antes era tan cutre que mostraba todos los que tuvieran menos de una semana. Ahora podemos hacer que muestre los que no hemos marcado como leídos:

1
2
3
4
5
6
7
8
<?php
function _feedback_count ()
{
  $query = "SELECT count(id) as total from {feedback_messages} fm where fm.read != 1";
  $queryResult = db_query ( $query );
  $result = db_fetch_object ( $queryResult ) ;
  return $result->total;
}

Más tutoriales

Este tutorial es la continuación de Creación de un módulo Drupal, Además, podéis continuar el tutorial en: