creación de módulos cck básicos en drupal 6

23
Creación de tipos de campo CCK básicos en Drupal 6 Autor: Dany Alejandro Cabrera - [email protected] (Julio de 2010) Esta guía pretende explicar el proceso de creación de un nuevo módulo que provea un nuevo tipo de campo sencillo para utilizar con CCK. Requisitos previos Se espera que el lector esté familiarizado con: Terminología y uso avanzado de Drupal PHP (avanzado), HTML, JS, CSS CCK Esta guía solo persigue ser una introducción a la creación de este tipo de módulos. Aunque se explican algunos conceptos de construcción de módulos, tampoco pretende explicar la creación de un módulo, sino el caso concreto de los tipos de campo CCK. Recomiendo hacer un var_dump() en PHP para estudiar las variables que resulten confusas. Contenido 1. Introducción 2. Terminología y generalidades a. Campos y widgets b. Formatter 3. Construcción del módulo a. Estructura de archivos b. Archivo .info c. Archivo .install d. Archivo .module 4. Ejemplo 1 a. hook_field_info() b. hook_field_settings() c. hook_widget_info() d. hook_widget() e. hook_elements() f. hook_theme() g. hook_content_is_empty() Referencias [1] Jennifer Hodgdon, Creating a Compound Field Module for CCK in Drupal 6.x, [Online] http://www.poplarware.com/articles/cck_field_module , Jul. 2010 [2] Karen Stevenson, “Creating custom CCK fields”, *Online+ http://www.lullabot.com/articles/creating-custom-cck-fields , Jul. 2010 [3] Comunidad Drupal, ”Module developer’s guide”, [Online] http://drupal.org/developing/modules , Jul. 2010

Upload: dany-alejandro-cabrera

Post on 29-Jun-2015

1.374 views

Category:

Documents


3 download

DESCRIPTION

Esta guía pretende explicar el proceso de creación de un nuevo módulo que provea un nuevo tipo de campo sencillo u compuesto para utilizar con CCK en Drupal 6.

TRANSCRIPT

Page 1: Creación de Módulos CCK básicos en Drupal 6

Creación de tipos de campo CCK básicos en Drupal 6

Autor: Dany Alejandro Cabrera - [email protected] (Julio de 2010) Esta guía pretende explicar el proceso de creación de un nuevo módulo que provea un nuevo tipo de campo sencillo para utilizar con CCK. Requisitos previos Se espera que el lector esté familiarizado con:

Terminología y uso avanzado de Drupal

PHP (avanzado), HTML, JS, CSS

CCK Esta guía solo persigue ser una introducción a la creación de este tipo de módulos. Aunque se explican algunos conceptos de construcción de módulos, tampoco pretende explicar la creación de un módulo, sino el caso concreto de los tipos de campo CCK. Recomiendo hacer un var_dump() en PHP para estudiar las variables que resulten confusas. Contenido

1. Introducción 2. Terminología y generalidades

a. Campos y widgets b. Formatter

3. Construcción del módulo a. Estructura de archivos b. Archivo .info c. Archivo .install d. Archivo .module

4. Ejemplo 1 a. hook_field_info() b. hook_field_settings() c. hook_widget_info() d. hook_widget() e. hook_elements() f. hook_theme() g. hook_content_is_empty()

Referencias

[1] Jennifer Hodgdon, “Creating a Compound Field Module for CCK in Drupal 6.x“, [Online] http://www.poplarware.com/articles/cck_field_module , Jul. 2010

[2] Karen Stevenson, “Creating custom CCK fields”, *Online+ http://www.lullabot.com/articles/creating-custom-cck-fields , Jul. 2010

[3] Comunidad Drupal, ”Module developer’s guide”, [Online] http://drupal.org/developing/modules , Jul. 2010

Page 2: Creación de Módulos CCK básicos en Drupal 6

Introducción

En aplicaciones reales, podemos encontrar casos en los que los tipos de campo disponibles para CCK (incluidos en CCK o contribuidos por la comunidad) no encajan por completo con la solución que buscamos para ciertos problemas. Siempre existe la opción de usar combinaciones (complejas en algunos casos) de módulos con manipulación de hooks para obtener lo que queremos, pero esta es un arma de doble filo: aquellos que han trabajado sitios grandes con Drupal siempre notan un impacto negativo en el rendimiento por la cantidad de consultas SQL necesarias para hacer funcionar un número mayor de módulos, especialmente cuando el número de nodos se cuenta en miles y el tráfico es alto. Si el objetivo de instalar módulos (junto con sus pre-requerimientos) es permitir el funcionamiento de un pequeño pero importante campo CCK en algún formulario, deberíamos plantearnos crear el módulo para ese campo por nuestra cuenta: en algunos casos, la cantidad de consultas que nos ahorramos se cuenta por docenas (representando un impacto notable en rendimiento). También tiene otras ventajas: Tenemos un nuevo campo CCK hecho a nuestra medida para reutilizar, igual o modificado, en los formularios y sitios que deseemos con un impacto mínimo, conservando la modularidad en la arquitectura, extensible, sin depender de módulos extraños ni de sus actualizaciones y sobre el que tenemos control total (PHP, JS, CSS y HTML) sobre la forma en que se configura, almacena, muestra y valida. Podemos incluso compartirlo con la comunidad. Si tu caso es similar, se puede justificar la creación de un nuevo campo CCK. A pesar de lo poderoso que resulta Drupal, no podemos tomarlo como excusa para dejarnos limitar por la herramienta; La modularidad de Drupal está allí para que, siguiendo sus reglas, podamos anexarle cosas nuevas.

Page 3: Creación de Módulos CCK básicos en Drupal 6

Terminología y generalidades

Se puede encontrar información y recomendaciones sobre desarrollo de módulos en la guía para desarrolladores de Drupal: http://drupal.org/developing/modules . En particular, en la sección de “Mejores prácticas” (http://drupal.org/node/360052) explica diferentes convenciones que se recomienda seguir al programar un módulo. Nota: al final de los archivos PHP del módulo y sus posibles archivos incluidos se recomienda omitir el delimitador de cierre del código PHP “?>” para evitar ciertos problemas (las razones se discuten en http://drupal.org/coding-standards). En los ejemplos se omite a propósito.

Hooks

Los módulos son piezas de código capaces de responder a ciertas peticiones del sistema. El módulo debe estar programado según ciertas reglas para poder “encajar” en lo que el sistema espera de él. El sistema de módulos de Drupal está basado en “hooks”: funciones PHP especiales cuyo nombre inicia con el nombre del módulo seguido de una barra inferior (“_”) y el nombre del hook (ejemplo: para implementar “hook_block()” escribimos “mimodulo_block()”). Cada hook tiene tanto sus parámetros como su tipo de retorno ya definidos. Los hooks suelen tener una versión por defecto que sobre-escribimos en los módulos; si no los implementamos, Drupal usará la versión por defecto (aunque algunos hooks son obligatorios según el propósito del módulo). Cuando Drupal desea permitir la intervención de un módulo, determina qué módulos activos implementan un hook y lo invoca. Así, los hooks que estén implementados determinan el comportamiento y capacidades del módulo. Los módulos también pueden definir hooks para que otros módulos los puedan sobre-escribir; CCK ofrece un conjunto de hooks que, al ser implementados en otros módulos, permiten extender sus funcionalidades. La documentación de los hooks que ofrece Drupal 6 a los módulos en general (sin contar los hooks de CCK) se encuentra en http://api.drupal.org/api/group/hooks/6 . La documentación de los hooks especiales para tipos de campo CCK (Julio 2010) se encuentra incompleta (http://drupal.org/node/342987), sin embargo en algunos casos la correspondiente a Drupal 5 es de utilidad (http://drupalcontrib.org/api/5).

Campos y widgets

El CCK es un módulo que, a grandes rasgos, nos permite almacenar y cargar valores de la base de datos. Aquellos que tienen experiencia realizando consultas a una base de datos desde PHP habrán notado que, cuando se crea un formulario capaz de almacenar datos en una base de datos, podemos imaginar que la información pasa por 3 “capas”:

(capa 1) El formulario que puede ver el usuario, escrito en HTML.

Page 4: Creación de Módulos CCK básicos en Drupal 6

(capa 2) El script PHP que recibe los datos enviados en el formulario y solicita al sistema de gestión de base de datos (ej: MySQL) almacenar los datos.

(capa 3) La estructura de tablas que almacena los datos dentro de la base de datos. El formulario en HTML no necesariamente muestra al usuario las columnas tal y como aparecen en la base de datos (por ejemplo, podemos almacenar datos sin que el usuario lo sepa, como la fecha de inserción del nuevo registro o su IP). Es decir, la capa 1 no es necesariamente un reflejo exacto del capa 3. Cuando estamos en el marco de trabajo que nos proporciona CCK, para cada campo por separado distinguimos 3 capas diferentes:

(capa 1) El widget que puede ver el usuario.

(capa 2) El campo CCK.

(capa 3) El módulo CCK. Cada capa es relativamente independiente de la otra (aunque para que el módulo funcione las debe involucrar a todas) y realiza sus propias operaciones de validación. La interacción con la base de datos (entre otras cosas) la realiza el módulo CCK por su cuenta, a menos que cambiemos el comportamiento por defecto implementando ciertos hooks (por lo general no es necesario). Los campos CCK (CCK fields) son contenedores de datos relacionados a cierto nodo. Por cada campo CCK, habrá columnas para almacenar su información en cierta tabla en la base de datos (es decir, están relacionados con la estructura estática de la base de datos). Un mismo campo CCK puede almacenar más de un valor al mismo tiempo. Un widget es un elemento (o conjunto de elementos) que se encarga de recibir y gestionar los datos que ingresa el usuario para cierto campo CCK que le corresponde. Generalmente dichos datos vienen de un elemento en el formulario (representación visual del widget). Se podría ver como el enlace entre un campo y el elemento del formulario que lo representa. Por ejemplo, el modulo de tipo de campo “Text” (incluido por defecto en CCK), ofrece un único campo (“Text”) que se almacena en base de datos en una columna cuyo tipo puede ser “varchar”, “text” o “int” (dependiendo de la configuración inicial que se le dió al campo cuando se lo estaba creando). Para ese campo CCK, tenemos a nuestra disposición 5 widgets diferentes:

El seleccionar uno u otro widget para el elemento no cambia su campo CCK; sólo la manera en que lo presentamos al usuario en el formulario.

Page 5: Creación de Módulos CCK básicos en Drupal 6

Formatters

Un “formatter” (formateador) es una función de tema que se encarga de “renderizar” en el documento HTML los valores almacenados de un campo CCK, como si se tratara de un tema particular para dicho campo. Todo tipo de campo CCK debería tener al menos un formatter (‘default’) para poder aparecer en pantalla (por ejemplo, en la vista de un nodo). Al formatter se le asocia el código PHP, HTML y JS utilizado para presentar el campo visualmente al usuario. El administrador del sitio puede seleccionar el formatter que desea aplicar para cierto campo tanto en la vista de resumen (“Teaser”) como en la vista de nodo (en “Display fields”, al configurar los campos de un tipo de contenido). También es posible solicitar el contenido de un campo en cierto formato mediante PHP (por ejemplo, en los archivos .tpl.php).

El tipo de campo CCK “number” ofrece 11 formatters, incluido “unformatted”

Podemos tener diferentes formatters disponibles para un mismo campo CCK. Se suele ofrecer un formatter especial que devuelve el contenido sin formato (‘plain’) para que se puedan manipular los valores almacenados del campo con facilidad.

Un mismo campo al que se le aplica 2 formatters diferentes

Es posible incluso, crear módulos que ofrezcan nuevos formatters para tipos de campos CCK que no definan ellos mismos, con lo que podemos extender la manera en que se muestran los campos CCK según lo requiera el problema, facilitando el trabajo de los themers. Dentro del módulo, todo formatter debe ser registrado en hook_theme() y en hook_field_formatter_info() para que sea reconocido por el sistema.

Page 6: Creación de Módulos CCK básicos en Drupal 6

Sobre Drupal Form API (FAPI)

Es una biblioteca de funciones (API) incluida en Drupal que nos ofrece métodos para la construcción y validación de formularios. Para emplearla, describiremos los elementos de formulario en arreglos asociativos siguiendo cierta estructura estándar; la FAPI se encargará de generar el HTML del elemento, su funcionalidad, seguridad y extensibilidad. También se encarga de ofrecer callbacks durante ciertos procesos. Por ejemplo, para describir un campo de texto de nombre máquina “apellido” emplearíamos la siguiente estructura:

Nótese que el símbolo “#” precede a los atributos. Mediante la Forms API podemos también alterar el comportamiento de cualquiera de los formularios de Drupal y sus módulos (incluidos los del core) con bastante libertad. Es la forma recomendada de construir formularios en Drupal 6. Es recomendable aprender a usar la FAPI si se desea controlar el comportamiento del elemento del formulario (escapa el alcance de la guía). Se puede encontrar documentación de la FAPI en http://drupal.org/node/204270. También hay libros avanzados de Drupal que tratan el uso de FAPI para crear formularios. El arreglo $element En FAPI, $element es un arreglo asociativo que representa un elemento de formulario (contiene la información del elemento, sus valores almacenados e incluso el formulario en el que aparece). Este arreglo se emplea con regularidad cuando se describe un elemento en un tipo de campo CCK. Contiene los siguientes índices (a algunos les damos valores, otros los ingresa CCK automáticamente):

Algunos índices de interés son:

'#field_name' – El nombre máquina del campo.

‘#title’ – Nombre legible del campo CCK (ingresado en la configuración del campo).

#type, #default_value, #title, #description, #required, #weight, #delta, #columns,

#field_name, #type_name, #post, #programmed, #tree, #parents, #array_parents,

#processed, #attributes, #input, #process, #name, #id, #value, #element_validate,

#defaults_loaded, #sorted, #children

$form['apellido'] = array(

'#type' => 'textfield',

'#title' => t('Apellidos'),

'#size' => 60,

'#maxlength' => 64,

'#description' => t('Por favor ingrese su apellido completo.')

);

Page 7: Creación de Módulos CCK básicos en Drupal 6

‘#delta’ - Número consecutivo que indica la posición de este elemento en el grupo de elementos que aparece cuando el campo CCK recibe múltiples valores.

'#description' – Texto de ayuda (ingresado en la configuración del campo).

'#value' – Arreglo asociativo cuyas llaves son los nombres máquina de las columnas del campo y cuyos valores son los valores actualmente almacenados en dichas columnas (ej: cuando se edita el nodo o en la vista previa).

‘#children’ – Código HTML construido por el motor de temas a partir de la información en este elemento FAPI.

‘#columns’ – Nombre de las columnas que corresponden a este módulo (desde hook_field_settings()).

‘#required’ – Si el campo es obligatorio (desde el formulario de configuración del campo CCK).

$element también contiene la información (arreglos FAPI) de los sub-elementos de este elemento (los índices son sus nombres máquina sin el prefijo “#”, aquellos ingresados en el callback de procesado cuando construimos el elemento).

Estructura de archivos de un módulo

Un módulo de un tipo de campo CCK tiene la misma estructura que un módulo normal: la carpeta del módulo con el nombre máquina (nombre interno) del módulo con, como mínimo, los archivos (de texto plano):

<nombre modulo>.info - con la información básica del módulo.

<nombre modulo>.install - con las notificaciones a CCK de cuándo el módulo ha sido instalado, desinstalado, habilitado y deshabilitado. Algunos prefieren insertar su contenido directamente en el archivo .module .

<nombre modulo>.module - con los hooks que implementan el funcionamiento del módulo.

Como con cualquier módulo, se recomienda un nombre máquina corto y sencillo para que los nombres de los hooks no se tornen engorrosos.

Page 8: Creación de Módulos CCK básicos en Drupal 6

Hooks de un tipo de campo CCK

Entre otros, tenemos los siguientes hooks a nuestra disposición (llamamos “obligatorio” a los hooks necesarios para que, dentro del alcance de esta guía, un campo CCK pueda aparecer tanto en un formulario como en la vista de un nodo): Hooks de definición de campos

(CCK) hook_field_info(): Describe a nivel general los campos CCK del módulo. (obligatorio)

(CCK) hook_field_settings(): Describe varios aspectos de configuración de los campos, incluyendo el formulario de configuración de cada campo y los tipos de columna que soportarán los datos de los campos en la base de datos. (obligatorio)

(CCK) hook_field(): Permite controlar el comportamiento del tipo de campo en ciertos instantes especiales. Se suele usar para agregar validaciones y filtrado a nivel de campo. (opcional)

(CCK) hook_content_is_empty(): Define las condiciones para que el contenido de un campo se considere vacío. (obligatorio)

Hooks de definición de widgets

(CCK) hook_widget_info(): Describe a nivel general los widgets del módulo. (obligatorio)

(CCK) hook_widget(): Permite controlar el comportamiento del widget en ciertos instantes especiales. Se suele usar para agregar validaciones y filtrado a nivel de widget. (obligatorio)

(CCK) hook_widget_settings(): Describe varios aspectos de configuración de los widgets, incluyendo el formulario de configuración de cada widget. (opcional)

(FAPI) hook_elements(): Describe los elementos de formulario (mediante Forms API) que estarán disponibles para cada widget en el módulo. (obligatorio a menos que utilicemos hook_widget() para sus funciones)

Hooks de temas

(core) hook_theme(): Registra las funciones de tema que ofrece nuestro módulo, como los temas de los widgets y los formatters. (obligatorio)

(CCK) hook_field_formatter_info(): Describe a nivel general los formatters del módulo. (obligatorio)

Nótese que no todos los hooks provienen de CCK (la creación del módulo también involucra hooks de FAPI y del “core” de Drupal).

Page 9: Creación de Módulos CCK básicos en Drupal 6

Ejemplo

Imaginemos que, en un portal de recetas de cocina, se nos solicita un tipo de contenido “Receta”: A cada receta, se le deben poder agregar un número (teóricamente ilimitado) de referencias a nodos tipo “Ingediente” (ya existentes), y cada vez que agregamos un ingediente a la receta es obligatorio anexar una cantidad y opcionalmente una nota. Por ejemplo, a la receta “huevos a la española” puedo agregar el ingrediente “huevo”, con una cantidad igual a “3” y una nota que dice “batidos previamente”. Solución Con propósitos didácticos, crearemos el nuevo widget en varias iteraciones. Primero intentaremos crear un campo CCK muy simple y luego iremos agregándole funcionalidades.

Page 10: Creación de Módulos CCK básicos en Drupal 6

Archivo .info

Es un archivo de texto plano (en un formato de pares “propiedad = valor” en cada línea, tal y como los archivos *.ini) que permite a Drupal obtener información sobre el módulo (metadatos) sin necesidad de tenerlo instalado. El archivo .info permite al sistema reconocer la presencia de un módulo. Entre otras cosas, en el archivo .info podemos incluir:

name - El nombre legible de nuestro módulo. (obligatorio)

description - La descripción legible, en máximo 255 caracteres. (obligatorio)

dependencies – Un arreglo con los nombres máquina de los módulos de los cuales depende este módulo (si no están presentes no permitirá la instalación). (opcional)

package - El paquete de módulos al cual pertenece. Si no se especifica, el módulo aparecerá en el paquete “Otros”. (opcional)

core - La versión de Drupal en la que funciona: “6.x” en nuestro caso. (obligatorio) La documentación completa para archivos .info en Drupal 6 se encuentra en http://drupal.org/node/231036 . En nuestro caso, lo único diferente al archivo .info de un modulo común y corriente es la dependencia obligada del módulo “content” (CCK). Ejemplo:

En presencia de este archivo Drupal ya debería detectar nuestro módulo.

name = "Ingredient reference field"

description = "Defines a composite field type for selecting an

ingredient in a recipe."

dependencies[] = content

package = Recipe website modules

core = 6.x

Page 11: Creación de Módulos CCK básicos en Drupal 6

Archivo .install

El archivo .install se consulta cuando un módulo se instala, habilita, deshabilita y desinstala, y se encarga de los procesos que cada módulo requiere en dichas ocasiones. También se utilizan en las operaciones de actualización. Aunque generalmente este archivo se usa para manipular tablas en la base de datos, en nuestro caso el módulo content (CCK) realiza dichos procesos automáticamente. Nos ocuparemos solamente de notificarle al módulo CCK cuando ocurren estos eventos. Implementaremos los hooks:

hook_install() – Ejecutado cuando se instala el módulo.

hook_uninstall() – Ejecutado cuando se desinstala el módulo.

hook_enable() – Ejecutado cuando se habilita el módulo.

hook_disable() – Ejecutado cuando se deshabilita el módulo. Estos hooks notificarán al módulo CCK que nuestro módulo existe, describe un campo CCK y algo ha ocurrido con él. Dentro de cada hook realizamos 2 operaciones: cargamos el módulo “content” y le notificamos de lo ocurrido mediante content_notify():

<?php

function ingredient_ref_install() {

drupal_load('module', 'content');

content_notify('install', 'ingredient_ref');

}

function ingredient_ref_uninstall() {

drupal_load('module', 'content');

content_notify('uninstall', 'ingredient_ref');

}

function ingredient_ref_enable() {

drupal_load('module', 'content');

content_notify('enable', 'ingredient_ref');

}

function ingredient_ref_disable() {

drupal_load('module', 'content');

content_notify('disable', 'ingredient_ref');

}

Page 12: Creación de Módulos CCK básicos en Drupal 6

hook_field_info()

Este hook describe a nivel general los campos disponibles en nuestro módulo. Retorna un arreglo asociativo que tiene por llave el nombre máquina del campo (máximo 32 caracteres), y por valor un arreglo asociativo que describe dicho campo con las siguientes llaves:

label: Nombre legible del campo.

description: Descripción legible. También se suelen agregar al arreglo (según se vea necesario) otras llaves especiales que utilizan otros módulos (por ejemplo, el ícono de panels). El arreglo retornado puede contener más de 1 campo; se podrán seleccionar del combobox de tipo de dato (‘Type of data to store’) cuando se agrega el campo CCK a un tipo de contenido. En este caso solo crearemos uno:

Los campos que describe esta función aparecerán en el combobox de selección de tipo de dato al agregar un nuevo elemento CCK a un tipo de contenido:

Nótese que, mientras el módulo tiene el nombre máquina “ingredient_ref”, hemos llamado al campo CCK “ing_ref_fld'”. Es importante diferenciarlos porque un mismo módulo puede implementar más de un campo CCK al mismo tiempo (aunque por razones de organización, legibilidad etc. se recomienda separarlos en módulos diferentes). Por ejemplo, el módulo del campo CCK “number” (incluido en el CCK) ofrece 3 tipos de campo diferentes (“number_integer”, “number_float” y “number_decimal”) entre los cuales puede elegir el usuario; dichos campos se almacenan en 3 tipos de columna diferentes en la base de datos (“int”, “float” y ”numeric” respectivamente).

function ingredient_ref_field_info() {

$arr_fields = array();

$arr_fields['ing_ref_fld'] = array(

'label' => t('Ingredient selector'),

'description' => t('This CCK field stores an ingredient node

reference with a quantity and a comment.'),

);

return $arr_fields;

}

Page 13: Creación de Módulos CCK básicos en Drupal 6

hook_field_settings()

Retorna los parámetros de configuración del módulo. Recibe por parámetro una variable (tipo cadena) $op que según tome ciertos valores le indica a la función qué se desea averiguar. Según el valor de $op nos podrían estar solicitando:

‘form’ – Retornar un arreglo FAPI que describe el formulario de configuración global (‘global settings’) del campo CCK. Sus valores se guardarán en la base de datos y podrán ser accedidos mediante PHP en el arreglo $field. Por ejemplo, el tipo de campo “number” tiene un formulario de configuración como el siguiente:

Mientras que “Required” y “Number of values” son opciones de configuración que provee CCK automáticamente, todas las demás se agregan en la opción “form” del módulo. Por defecto, el módulo CCK se encarga de las siguientes opciones de configuración:

Texto de ayuda que aparece con el elemento en el formulario

Si el campo es obligatorio (“Required”)

Número de valores (“Number of values”) Estas opciones funcionan sin necesidad de implementarlas, aunque es posible alterar su comportamiento (escapa el alcance de esta guía).

Page 14: Creación de Módulos CCK básicos en Drupal 6

‘save’ – Un arreglo con los nombres máquina de los valores de la configuración de este campo CCK (a la hora de almacenarlos en BD). Deben corresponder a los que aparecen en ‘form’.

‘database_columns’ – Un arreglo asociativo que describa las columnas en la base de datos en las que se guardarán el valor o los valores que se ingresen en este campo CCK. El formato requerido es el de la Schema API (http://api.drupal.org/api/group/schemaapi/6).

Inicialmente almacenaremos un comentario corto sin opciones (sólo necesitamos la definición de la columna para almacenar el dato):

(corregir)

function ingredient_ref_field_settings($op, $field) {

switch($op) {

case 'database columns':

$columns['comment'] = array(

'type' => 'varchar',

'length' => 255,

'not null' => FALSE

);

return $columns;

break;

}

}

Page 15: Creación de Módulos CCK básicos en Drupal 6

hook_widget_info()

Este hook retorna un arreglo asociativo, cuyas llaves son los nombres máquina de los widget y cuyos valores son arreglos asociativos con información sobre dichos widget. Similar al caso de los campos CCK, cada widget contiene información sobre sí mismo, entre otros:

label - El nombre legible del widget.

field types - Un arreglo con los nombres máquina de los campos CCK con los que este widget puede ser utilizado.

multiple values - Indica si quien maneja los valores múltiples en este campo cuando se presentan es el propio módulo CCK (comportamiento por defecto - CONTENT_HANDLE_CORE) o lo haremos en el módulo por nuestra cuenta (CONTENT_HANDLE_MODULE). (opcional)

callbacks – Un arreglo asociativo de funciones para llamar al terminar ciertas tareas (callbacks). CCK las puede manejar automáticamente (comportamiento por defecto - CONTENT_CALLBACK_DEFAULT), podemos especificar un arreglo asociativo de callbacks por nuestra cuenta (CONTENT_CALLBACK_CUSTOM) o evitar el uso de callbacks (CONTENT_CALLBACK_NONE). (opcional)

En este caso solo tendremos uno:

Esta información aparece al agregar un elemento de este tipo al formulario, donde el usuario puede seleccionar el widget de su preferencia:

function ingredient_ref_widget_info() {

return array(

'ing_wdg' => array(

'label' => 'Ingredient widget',

'field types' => array('ing_ref_fld')

)

);

}

Page 16: Creación de Módulos CCK básicos en Drupal 6

hook_widget()

Este hook suministra información sobre la construcción y el comportamiento de los widget. Será invocado cada vez que aparece un widget en un formulario (si el elemento en cuestión soporta valores múltiples, una vez por cada sub-elemento que aparezca en pantalla). Se le solicita el elemento del formulario para cierto campo, el cual retorna en formato Forms API. Recibe los parámetros:

form – Una referencia al arreglo asociativo que define el formulario completo. En particular, $form*‘node’+ contiene la información del nodo.

form_state – Referencia el estado actual del formulario. $form_state['values'][$field[<nombre del campo>]] contiene el valor de cierto campo actualmente.

field – El arreglo que describe el campo correspondiente al widget.

items – El arreglo completo de items (si el campo CCK aplicado al formulario soporta valores múltiples, este arreglo los contiene a todos).

delta – Un número consecutivo (iniciando desde 0) que indica el elemento en particular que será construido del arreglo de sub-elementos que se maneja cuando el campo soporta valores múltiples (aparecen múltiples widgets). Por ejemplo:

Mediante este hook podríamos describir los elementos de formulario para todos los widget, con lo que no necesitaríamos implementar hook_elements(). Sin embargo, el método recomendado es construir y configurar los elementos en hook_elements() y retornarlos en hook_widget(). Dado que este hook se invoca cada vez que un elemento aparece en el formulario, podemos agregar también cualquier tarea extra que requiera nuestro módulo. En este caso, sólo necesitamos describir el único widget que tenemos (‘ing_wdg’):

Page 17: Creación de Módulos CCK básicos en Drupal 6

hook_elements()

Este hook (perteneciente a FAPI) devuelve un arreglo FAPI que describe los elementos que construiremos (en el formulario) por cada widget. Ofrece para los widgets de CCK todas las ventajas de FAPI, entre otras que sus elementos son “re-utilizables” por otros módulos (podemos insertar elementos dentro de otros elementos) y la posibilidad de usar los callbacks de FAPI en un widget específico. Nota: Por cada elemento que declaremos en este hook, es obligatorio implementar su tema correspondiente (más adelante). Podemos describir elementos en el hook de 2 formas:

1. Definir completamente el elemento del formulario mediante FAPI. 2. Declarar el elemento e incluir una función callback de “procesado” implementada por

nuestra cuenta, que se encargará de construir y retornar el elemento del formulario mediante FAPI.

El segundo método es el generalmente recomendado. El hook debería retornar un arreglo FAPI donde para cada widget (nombre máquina) tenemos un arreglo asociativo con sus atributos (los soportados por FAPI), entre otros:

“#input”: (booleano) indica si este elemento lleva un valor (aún si está oculto).

“#process": Arreglo de callbacks a invocar cuando el elemento está siendo construido.

“#validate": Arreglo de callbacks a invocar cuando el formulario entero está siendo validado.

"#element_validate": Arreglo de callbacks a invocar cuando el elemento en particular está siendo validado.

(La lista de atributos es mucho más grande y corresponde a un estudio de la FAPI; aquí sólo mencionamos los mínimos) En este ejemplo, para el widget ‘ing_wdg’ declaramos una función callback ‘ing_wdg_process’ que se encargará de describir el elemento a mostrar en el formulario:

function ingredient_ref_widget(&$form, &$form_state, $field,

$items, $delta = 0) {

$element = array(

'#type' => $field['widget']['type'],

'#default_value' => isset($items[$delta]) ? $items[$delta] : ''

);

return $element;

}

Page 18: Creación de Módulos CCK básicos en Drupal 6

Nótese que en este hook no asociamos los elementos de formulario con los campos CCK; dicha tarea corresponde a hook_wiget_info(). El uso de hook_elements() es discutido porque en hook_widget() también se pueden describir los elementos del formulario (haciendo innecesario este hook). hook_elements() representa otra capa de abstracción (y complejidad) que para muchos puede no ser necesaria. Sin embargo, la existencia de este hook en el módulo obedece a ciertas decisiones que se tomaron conforme el módulo CCK era desarrollado. En los tiempos de Drupal 5, hook_widget() tenía funciones (y parámetros) similares a las de hook_field(); hook_elements() no era usado, pues CCK realizaba internamente el procesado de los elementos sin usar FAPI. En ese entonces, la construcción del widget se realizaba en hook_widget() con métodos propios del módulo CCK. Conforme el módulo se fue actualizando a Drupal 6, se prefirió el uso de FAPI para construir el elemento y configurar sus callbacks. Ahora que la definición de los elementos se hace en hook_elements(), la labor de hook_widget() es retornar los elementos que hayamos definido anteriormente, y realizar cualquier tarea extra que necesitemos en el acto (el hook se invoca para cada widget renderizado).

Implementación del callback de procesado

Para cada elemento en el hook_elements(), podemos definir una función callback (“#process”) que modifique y retorne el elemento al que corresponde (arreglo FAPI). Este callback se ejecuta cuando el elemento está siendo construido y permite que lo alteremos en ese instante. En esta función completaremos el elemento con la información necesaria para mostrarlo en el formulario. Recibe los parámetros:

element – Arreglo FAPI con la definición del elemento que está siendo procesado.

edit – Arreglo asociativo con los valores actualmente almacenados para el elemento (si se está editando o previsualizando, por ejemplo).

form_state – Referencia el estado actual del formulario completo.

form – Arreglo asociativo que define el formulario completo que se está ejecutando actualmente.

En el ejemplo, tenemos 2 cajas de texto (textfield) en el formulario: una para el id del nodo y otra para un comentario opcional. Construiremos 2 sub-elementos para este elemento:

function ingredient_ref_elements() {

$arr_elements = array();

$arr_elements['ing_wdg'] = array(

'#input' => TRUE,

'#columns' => array('node_id', 'comment'),

'#process' => array('ing_wdg_process')

);

return $arr_elements;

}

Page 19: Creación de Módulos CCK básicos en Drupal 6

Obsérvese que:

Recibimos, modificamos y retornamos $element.

La llave en el arreglo $element debe corresponder exactamente al nombre de la columna donde será almacenado. Algunos prefieren obtener dicho nombre desde $element*‘#columns’+.

Podemos tomar de los valores que ya trae $element los datos de configuración que haya podido ingresar el usuario para este campo cuando lo estaba creando.

En ‘#default_value’ insertamos el valor anterior almacenado desde $element['#value'] (o si aún no existe insertamos una cadena vacía). En el momento de ejecución de este callback (“#process”), $element['#value'] ya tiene sus valores establecidos. Si aún no se ha ingresado ningún valor, los valores en $element['#default_value'] ya se han reemplazado en $element['#value'].

Implementación del tema del widget

Cuando declaramos elementos mediante hook_elements(), el sistema asume que existirá una función de tema con el mismo nombre del elemento (nombre máquina del widget con el prefijo “theme_”), a menos que se haya incluido el atributo “#theme” en el elemento (en cuyo caso buscará la función especificada). Esta función de tema determina cómo lucirá el elemento en el formulario (si la función theme del elemento no existe, el elemento no aparecerá). Debido a que estará registrada en hook_theme(), será posible modificarla externamente. La función recibe por parámetro el arreglo FAPI $element que ya mencionamos (desde hook_theme()), y devuelve una cadena con el código HTML del elemento en cuestión. En este punto, $element*‘#children’+ ya contiene el código HTML que nos genera el motor de temas de Drupal para este elemento. Por ejemplo:

function ing_wdg_process($element, $edit, $form_state, $form) {

$value = isset($element['#value']['node_id']) ?

$element['#value']['node_id'] : '';

$element['node_id'] = array(

'#type' => 'textfield',

'#title' => 'Ingredient id',

'#required' => $element['#required'],

'#default_value' => $value

);

$value = isset($element['#value']['comment']) ?

$element['#value']['comment'] : '';

$element['comment'] = array(

'#type' => 'textfield',

'#title' => 'Comment',

'#required' => $element['#required'],

'#default_value' => $value

);

return $element;

}

Page 20: Creación de Módulos CCK básicos en Drupal 6

Retornaría el código HTML generado para cada sub-elemento, concatenado en una sola cadena:

Aunque en esta función es posible retornar código HTML escrito manualmente, lo recomendado es construirlo usando la información almacenada en $element. Si deseamos renderizar los sub-elementos individualmente, podemos usar la función theme() sobre el sub-elemento correspondiente:

hook_theme()

Todas las implementaciones de funciones de temas en el módulo (formatters incluidos) deben ser registradas en el registro de temas de Drupal por medio de este hook. Dichas funciones podrán ser utilizados y modificados desde fuera del módulo (se convierten en nuevos hooks), siempre y cuando Drupal los haya reconocido. Con esto es posible cambiar el aspecto de nuestros campos y widgets sin necesidad de modificar el módulo. Las funciones de tema tienen nombre propio, aunque en su implementación deben tener el prefijo “theme_”. Desde fuera del módulo, las invocaremos mediante la función theme(), cuyos argumentos son el nombre de la función de tema seguido de sus parámetros correspondientes. Este hook debe retornar un arreglo asociativo cuyas llaves son los nombres de las funciones de tema en el módulo (el nombre de las funciones que los implementan, omitiendo el prefijo “theme_”), y cuyos valores son arreglos asociativos con la información de cada función de tema.

function theme_ing_wdg($element) {

$widget_html = '<table border="1">';

$widget_html .= '<tr><td>' . theme('textfield', $element['node_id']) . "</td></tr>";

$widget_html .= '<tr><td>' . theme('textfield', $element['comment']) . "</td></tr>";

$widget_html .= '</table>';

return $widget_html;

}

<div class="form-item" id="edit-field-rec-ingredients-0-node-id-wrapper">

<label for="edit-field-rec-ingredients-0-node-id">Ingredient id: </label>

<input type="text" maxlength="128" name="field_rec_ingredients[0][node_id]" id="edit-

field-rec-ingredients-0-node-id" size="60" value="" class="form-text" />

</div>

<div class="form-item" id="edit-field-rec-ingredients-0-comment-wrapper">

<label for="edit-field-rec-ingredients-0-comment">Comment: </label>

<input type="text" maxlength="128" name="field_rec_ingredients[0][comment]" id="edit-

field-rec-ingredients-0-comment" size="60" value="" class="form-text" />

</div>

function theme_ing_wdg($element) {

return $element['#children']; }

Page 21: Creación de Módulos CCK básicos en Drupal 6

En particular, debemos especificar qué argumentos (parámetros) deberá recibir cada función; cuando se implementan temas para elementos de formulario o formatters se suele incluir “element”, con “NULL” como valor por defecto. Temas con propósitos diferentes incluyen argumentos diferentes.

En el ejemplo hemos registrado tanto el tema del widget como los 2 formatters (no olvidar limpiar el caché del registro de temas para que Drupal detecte los nuevos). Este es un hook del “core” de Drupal y hace parte de un estudio de la implementación de temas. En la documentación de la Drupal API se puede encontrar más información (http://api.drupal.org/api/function/hook_theme/6).

hook_field_formatter_info()

Similar al caso de hook_field_info() y hook_widget_info(), este hook describe a nivel general los formatters que nuestro módulo ofrecerá para mostrar los campos. Retorna un arreglo asociativo cuyas llaves son los nombres máquina de los formatters, y cuyos valores son arreglos asociativos que describen dichos formatters, con las llaves:

label – Descripción legible del formatter

field types – Arreglo con los nombres máquina de los campos a los cuales es posible aplicar este formatter.

multiple_values – Indica si CCK le entregará al formatter valores múltiples (CONTENT_HANDLE_MODULE) o un único valor (CONTENT_HANDLE_CORE - por defecto).

Se suele incluir el formatter ‘default’ que se aplicará por defecto cuando no se especifica ningún formatter, y el formatter ‘plain’ que devuelve los valores sin formato. Para cada formatter que declaremos en este hook, hemos de implementar su función theme y registrarla en hook_theme().

function ingredient_ref_theme() {

$arr_themes = array();

$arr_themes['ing_wdg'] = array(

'arguments' => array('element' => NULL)

);

$arr_themes['ingredient_ref_formatter_default'] = array(

'arguments' => array('element' => NULL)

);

$arr_themes['ingredient_ref_formatter_plain'] = array(

'arguments' => array('element' => NULL)

);

return $arr_themes;

}

Page 22: Creación de Módulos CCK básicos en Drupal 6

Los formatters aquí declarados aparecen en “Display fields”:

Implementación de formatters

El formatter recibe el arreglo asociativo $element y se espera que retorne una cadena con el código HTML para mostrar los valores almacenados. $element contiene información sobre el elemento, sus valores almacenados (en el arreglo asociativo $element['#item']) e incluso el nodo entero ($element*‘$node’+).

hook_field()

Permite realizar operaciones sobre los datos del campo en ciertos momentos especiales, por ejemplo antes y después de cargarlos / guardarlos en la base de datos. Suele ser usado para filtrar y validar el contenido, o en módulos que requieren acciones extra al manipular datos (mover archivos por ejemplo). Podemos escoger el momento en el cual deseamos que se ejecute, entre otros:

delete – El nodo está siendo eliminado de la base de datos insert – El nodo está siendo insertado en la base de datos load – El nodo está a punto de ser cargado desde la base de datos sanitize – El valor del campo está a punto de ser mostrado en pantalla. validate – El usuario terminó de editar/crear el formulario del nodo y está

intentando previsualizar/enviar el contenido. Se suele usar para validar

datos ingresados antes de guardarlos en la base de datos.

Los comúnmente usados son ‘sanitize’ y ‘validate’, es decir, este hook suele usarse

para labores de validación y filtrado. Dado que cada widget hace su propia

validación y no sabemos qué widget ha sido utilizado, aquí se validan los valores

function ingredient_ref_field_formatter_info() {

$arr_formatters = array();

$arr_formatters['default'] = array(

'label' => t('Default format'),

'field types' => array('ing_ref_fld')

);

$arr_formatters['plain'] = array(

'label' => t('Unformatted data'),

'field types' => array('ing_ref_fld')

);

return $arr_formatters;

}

Page 23: Creación de Módulos CCK básicos en Drupal 6

del campo. Desde aquí también podemos lanzar errores cuando los valores no

pasan la validación.