MagMax Blog

Aviso: En este blog puede encontrar código!

Creación De Un Módulo Drupal III

| Comments

Qué vamos a hacer

En esta ocasión me voy a dedicar a arreglar los estropicios que hice anteriomente :D

Además, voy a añadir enlaces para eliminar los feeds (como me han solicitado) y también vamos a redirigir al usuario a la página principal después de añadir un feed (como también me han solicitado).

También añadiremos un mail al usuario que inserta el mensaje, por si no está registrado. En caso de que esté registrado, guardaremos una referencia al mismo.

Qué se explica aquí

En esta ocasión veremos cómo:

  • Redirigir al usuario a la página que deseemos.
  • Ordenación de tablas.
  • Expresiones regulares.

Notas previas

En la primera parte del tutorial drupal ya insistí en que sólo se debe abrir el tag de <?php, y no se debe cerrar con ?>. Dicho esto, continuamos.

Modificando la BBDD

Añadimos el campo usermail. Además, modificamos el campo “uid” para que referencie al usuario si éste hizo login.

function feedback_update_6102() {
$ret = array();
db_add_field ( $ret, 'feedback_messages', 'usermail', array ( 'type' => 'text' ) );
db_change_field ( $ret, 'feedback_messages', 'uid', 'uid', array ( 'type'=> 'int', 'unsigned'=>TRUE ) );
return $ret;

}

Fe de erratas

El primer error fue la contabilización de feeds. Si miráis el tutorial Creación de un módulo Drupal, veréis la siguiente línea (función _feedback_count):

$query = &quot;SELECT count(id) as total from {feedback_messages} fm where fm.read != 1&quot;;

Bueno, pues está mal. Lo correcto es lo siguiente:

$query = &quot;select count(*) from drupal6.feedback_messages fm where isnull (fm.read) or fm.read != 1&quot;;

Como véis, comprobamos que no sea nulo, ya que en ese caso no estaba contabilizando correctamente.

El segundo error cometido es que los set_error deben realizarse sobre un campo. Así, en el primer tutorial, donde aparecen los set_error_, están mal. Realmente no veremos la diferencia si no la conocemos: Si lo hacemos bien, los campos indicados se marcarán en rojo (depende del tema) indicando que el problema se encuentra en ése campo. De esta manera, quedarían así (función *@feedback_message_formsubmit@*):

form_set_error ( &#39;feedback&#39;, t( &quot;An error has happened. Your feedback has not been sent.&quot; ) );

Veremos la función completa cuando añadamos más cosas.

Y ya, como error menor, deciros que las etiquetas ‘Date’ y ‘User’ estaban cambiadas en la función feedback_admin.

Al final de este tutorial, pondré todo seguido para que podáis aclararos.

Modificando funciones

Primero vamos con lo más sencillo de todo: Queremos que cuando alguien meta un feed, que vuelva a la pantalla de inicio. Asumiré que la página de inicio es “?q=node”, por lo que basta con indicarle:

bc(brush:php). drupal_goto ( ‘node’ );

A continuación, no sé si notásteis que la tabla con todos los mensajes no se ordena al pulsar las columnas. Esto nos puede venir bien para poder agrupar los mensajes no leídos, para ordenar por fecha, para agrupar los mensajes sin contenido, etc. ¡¡ Y es tan sencillo !! Basta con realizar un cambio durante la consulta, así que en feedback_admin:

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

Con eso le decimos a drupal que nos ordene la tabla. Ésa es una de las gracias de usar los headers en una tabla a parte, y por eso los definimos antes de realizar la consulta.

Añadiendo un mail

En las modificaciones de la base de datos, añadimos el campo “usermail”. Ahora vamos a usarlo. Necesitaremos que el usuario lo inserte, así que tenemos que modificar feedback_message_form:

function feedback_message_form ()

{
global $user;
$mail = "";

if ( $user->uid != 0 ) $mail = $user->mail; $form = array ( 'feedback' => array ( '#type' => 'fieldset', '#title' => t( 'Feedback' ), 'mail' => array ('#type' => 'textfield', "#title"=>"E-Mail", "#default_value" => $mail, "#description"=>"If you want any answer, please, tell me your mail. It would not be shown anywhere." ), 'message' => array ( '#type' => 'textarea', "#description"=>"Write here your message" ), 'submit' => array ( '#type' => 'submit', '#value' => t('Submit'), ), ), ); return $form;

}

Como veréis, esta función tiene un añadido: si el usuario ha hecho login, rellenamos el campo con el mail que puso para su usuario. Así le ahorramos trabajo. El UID del usuario no es necesario que lo metamos en el formulario, ya que podemos recuperarlo directamente de la variable global.

Las validaciones también cambian, ya que no sólo vamos a comprobar que el mensaje no esté vacío, sino que queremos hacer más cosas. Queremos que, si el usuario hizo login, entonces el campo de e-mail no es obligatorio. Si no lo hizo, entonces sí que lo es. Y el e-mail tiene que parecer un e-mail, es decir, tener su arroba y demás:

function feedback_message_form_validate ( $form, &amp;$form_state )

{
global $user;

$message = $form_state['values']['message']; $mail = $form_state['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 == '' && $user->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;

}

Aquí hemos introducido la creación de una expresión regular. Explicada brevemente, estamos comprobando si encaja el mail con un patrón dado. La expresión regular es bastante compleja, así que sólo comentaré lo más básico de todo:

  • Lo que va entre corchetes, significa “uno de estos”. Ej: [A-z0-9_] significa que queremos una letra mayúscula o minúscula, un número o un guión bajo.
  • Lo que va entre corchetes pero comienza por “^”, significa “ninguno de estos”. Ej. [^0-9] significa “algo que no sea un número”.
  • El asterisco significa “lo anterior se puede repetir 0 o más veces”. Ej: [0-9]* significa que puede no haber nada o bien un número indeterminado de números.
  • El símbolo de suma significa “lo anterior se puede repetir 1 o más veces”. Ej: [0-9]+ significa que puede haber un número indeterminado de números, pero debe aparecer al menos uno.
  • Los paréntesis permiten agrupar cosas para aplicarle otros símbolos.
  • Las llaves implican la multiplicidad exacta. Ej: [A-z]{2} significa “dos letras”. [A-z]{2,4} significa “entre 2 y 4 letras”.

Habitualmente, crear una expresión regular es mucho más sencillo de lo que parece, pero modificar una que ya existe suele ser bastante complicadete :-D

Bueno, y nos queda la inserción:

function feedback_message_form_submit ( $form, &amp;$form_state )

{
global $user;
$userid = NULL;
if ( $user )
$userid = $user->uid;
$message = $form_state['values']['message'];
$mail = $form_state['values']['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." ) );

}

Como ya sabemos redirigir, si todo va bien, nos iremos a la página principal :-D

Ya que tenemos el mail, lo suyo es que lo veamos desde la tabla principal de feeds:

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('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 ? '' . $message->name . '' : $message->name; $row['mail'] = $message->usermail; $row['message'] = $message->message; $row['read'] = $message->read; $row['edit'] = l ( t('edit'), "feedback/adminmessages/" . $message->id ); $rows[] = $row; } $content .= theme_table($header, $rows); return $content;

}

¡¡¡ Mucho ojo, porque a esta función aún tenemos que añadirle una línea para el borrado !!!

Además he añadido una cosa nueva. Si pulsáis sobre el nombre de uno de los usuarios, os redirigirá a la página personal del mismo.

Borrando

Ahora vamos a añadir una nueva operación: el borrado.

La forma de realizarlo va a ser similar a como está hecha la edición, con la diferencia de que vamos a cambiar el botón por uno de “eliminar”. Así que resulta fácil realizar este paso, aunque tendrá sus inconvenientes :-D

Comenzamos como siempre: permisos, menú, función principal y formularios.

No necesitamos más permisos de los que ya tenemos, así que en la función de menú (feedback_menu), añadimos lo siguiente:

$items[&#39;feedback/delete/%&#39;] = array
(
'title' => 'Feedback delete',
'page callback' => 'feedback_delete',
'access arguments' => array('view messages'),
'page arguments' => array ( 2 ),
'type' => MENU_CALLBACK,
);

Ahora creamos la función principal:

function feedback_delete ( $messageid=null )

{
$content = '';
$content .= drupal_get_form ( 'feedback_delete_form', $messageid );

return $content;

}

Muy similar a feedback_message. Y también creamos el formulario:

function feedback_delete_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, "#disabled" => TRUE ), 'submit' => array ( '#type' => 'submit', '#value' => t('Delete forever'), ), ), ); return $form;

}

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

$query = "DELETE FROM {feedback_messages} where id=%d"; $queryResult = db_query ( $query, $messageid ); if(db_affected_rows()){ drupal_set_message ( t ( "Message deleted" ) ); } drupal_goto ('/feedback/adminmessages');

}

Como véis, me he saltado la validación, ya que no hay nada que validar. El formulario es muy parecido al de feedback_messageadmin_form, pero con la diferencia de que el campo “read” también es de sólo lectura.

Lo que nos queda es poder llegar a este formulario. Para llegar, dijimos que usaríamos un sistema similar al del botón “edit”, así que eso mismo vamos a hacer: En la función feedback_admin, el final quedará así:

function feedback_admin( $messageid=null ) {
[…]
$row['edit'] = l ( t('edit'), "feedback/adminmessages/" . $message->id );
$row['delete'] = l ( t('delete'), "feedback/delete/" . $message->id );
$rows[] = $row;
}
$content .= theme_table($header, $rows);
return $content;

}

Dos palabras

Últimamente estoy recibiendo muchas consultas sobre cómo hacer tal o cual cosa en drupal. La verdad es que no esperaba tener “tanto” éxito con los tutoriales… casi ni explico nada, sólo he desglosado un ejercicio en partes tan pequeñas que no dé miedo atacarlas.

Así que tan sólo puedo deciros una cosa: ¡¡ Muchas gracias !! ;)

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

Aquí tenéis todo el código necesario:

feedback.info

; $Id$

name = "MagMax Feedback"
description = "Proporciona un sistema de feedbacks fácil para drupal"
core = 6.x

feedback.install

&lt;?php

/*
Copyright 2009 Miguel Ángel García Martínez

'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
*/

function feedback_schema ()
{
$schema['feedback_messages'] = array
(
'description' => t('Tabla para mensajes de retroalimentacion'),
'fields' => array
(
'id' => array
(
'description' => t('Identificador de mensaje'),
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array
(
'description' => t('Identificador del usuario que realiza el feedback'),
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
),
'message' => array
(
'description' => t('Mensaje de feedback'),
'type' => 'text',
'not null' => TRUE,
),
'indate' => array
(
'description' => t('Fecha en la que se crea el comentario'),
'type' => 'datetime',
'not null' => TRUE,
),
),
'primary key' => array('id'),
);
return $schema;
}

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

function feedback_update_6103() {
$ret = array();
db_add_field ( $ret, 'feedback_messages', 'usermail', array ( 'type' => 'text' ) );
db_change_field ( $ret, 'feedback_messages', 'uid', 'uid', array ( 'type'=> 'int', 'unsigned'=>TRUE ) );
return $ret;
}

function feedback_install() {
// Create my tables.
drupal_install_schema('feedback');
}

function feedback_uninstall() {
// Drop my tables.
drupal_uninstall_schema('feedback');
}

feedback.module

&lt;?php

/*
Copyright 2009 Miguel Ángel García Martínez

'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 .= '' . t('Módulo que permite la inserción de mensajes de feedback.') . '';
    break;
    }
    return $output;
    }

/**

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

/**

  • Implementation of hook_block().
    */
    function feedback_block ( $op = 'list', $delta = 0, $edit = array() )
    {
    if ( $op == "list")
    {
    $blocks = array();
    $blocks0["info"] = t('Feedback');
    $blocks1["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"), "feedback/message", $options ); $content .= '' . $link . ""; break; case 1: $block['subject'] = t('Feedback Messages'); $options = array( "attributes" => array("title" => t("Shows feedback messages") ) ); $title = t("There are total new feeds&quot;, array ( &#39;total' => _feedback_count() ) ); $link = l( $title , "feedback/adminmessages", $options ); $content .= '' . $link . ""; break; } $block['content'] = $content; return $block; }

}

/**

  • Implementation of hook_menu().
    */
    function feedback_menu ( )
    {
    $items = array();
$items['feedback/message'] = array ( 'title' => 'Feedback', 'page callback' => 'feedback_message', 'access arguments' => array('send message'), 'type' => MENU_CALLBACK, ); $items['feedback/adminmessages/%'] = array ( 'title' => 'Feedback admin', 'page callback' => 'feedback_admin', 'page arguments' => array ( 2 ), 'access arguments' => array('view messages'), 'type' => MENU_CALLBACK, ); $items['feedback/adminmessages'] = array ( 'title' => 'Feedback admin', 'page callback' => 'feedback_admin', 'access arguments' => array('view messages'), 'type' => MENU_CALLBACK, ); $items['feedback/delete/%'] = array ( 'title' => 'Feedback delete', 'page callback' => 'feedback_delete', 'page arguments' => array ( 2 ), 'access arguments' => array('view messages'), 'type' => MENU_CALLBACK, ); return $items;

}

function feedback_message ()
{
$content = '';

$content .= drupal_get_form ( 'feedback_message_form' ); return $content;

}

function feedback_message_form ()
{
global $user;
$mail = "";

if ( $user->uid != 0 ) $mail = $user->mail; $form = array ( '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" ), 'submit' => array ( '#type' => 'submit', '#value' => t('Submit'), ), ), ); return $form;

}

function feedback_message_form_validate ( $form, &$form_state )
{
global $user;

$message = $form_state['values']['message']; $mail = $form_state['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 == '' && $user->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_message_form_submit ( $form, &$form_state )
{
global $user;
$userid = NULL;
if ( $user )
$userid = $user->uid;
$message = $form_state['values']['message'];
$mail = $form_state['values']['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." ) );

}

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('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 ? '' . $message->name . '' : $message->name; $row['mail'] = $message->usermail; $row['message'] = $message->message; $row['read'] = $message->read; $row['edit'] = l ( t('edit'), "feedback/adminmessages/" . $message->id ); $row['delete'] = l ( t('delete'), "feedback/delete/" . $message->id ); $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;

}

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 ); drupal_goto ('/feedback/adminmessages');

}

function feedback_delete ( $messageid=null )
{
$content = '';
$content .= drupal_get_form ( 'feedback_delete_form', $messageid );

return $content;

}

function feedback_delete_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, "#disabled" => TRUE ), 'submit' => array ( '#type' => 'submit', '#value' => t('Delete forever'), ), ), ); return $form;

}

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

$query = "DELETE FROM {feedback_messages} where id=%d"; $queryResult = db_query ( $query, $messageid ); if(db_affected_rows()){ drupal_set_message ( t ( "Message deleted" ) ); } drupal_goto ('/feedback/adminmessages');

}

Comments