Curso de Drupal 7 (XIII): Temas visuales

Una de las cuestione principales en la puesta en marcha de un sitio web es la personalización del aspecto visual del sitio.
Para ello Drupal 7 cuenta con una serie de funcionalidades para la gestión de Temas visuales a partir de la Administración->Apariencia, al como se comentó en la VII entrega del curso
Ahora lo que debemos comentar son los pasos necesarios para la puesta en macha de un tema personalizado y adaptado a nuestras necesidades.

Localización de directorios

El directorio principal para la colocación de temas visuales, relativo a la ruta de instalación de Drupal, es el directorio “sites/all/themes”. En dicha carpeta veremos un directorio por cada tema visual instalado.
Lo primero que deberemos hacer es crear una carpeta con el nombre de nuestro tema visual, digamos que se llamará “ejemplo”.

El fichero .info

Una vez creada la carpeta, necesitaremos crear un fichero con el nombredeltema.info, en nuestro caso como el tema visual se llama ejemplo el fichero se llamará “ejemplo.info”. El fichero como todo en Drupal, deberá estar codificado en UTF-8.
A continuación veremos los parámetros más habituales de configuración que aparecen en el fichero:

  • name: nombre del tema
  • description
  • screenshot: fichero que representa la captura del tema
  • version: versión del tema
  • core: versión de drupal
  • engine: motor de plantillas a utilizar, el típico es phptemplate
  • regions[]: nombres de las regiones
  • features[]: funcionalidades que permite manejar el tema visual
  • stylesheets[]: hojas de estilo referenciadas en el tema
  • scripts []: ficheros Javascript referenciados con el tema
  • php: versión de PHP

un ejemplo de fichero .info sería:
name = Garland
description = Tableless, recolorable, multi-column, fluid width theme (default).
version = 7.12
core = 7.x
engine = phptemplate
stylesheets[all][] = style.css
stylesheets[print][] = print.css

Definiendo Regiones

Una de las cuestiones fundamentales a la hora de generar una pantilla es definir las regiones que quiere exponer el tema visual a Drupal. Éstas regiones como ya se vió anteriormente marcan zonas, habitualmente divs que permiten agrupar bloques visuales dentro del tema.
Para definir éstas regiones basta con incluir una serie de entradas en el fichero .info, una por cada región que se queira definir, similar a la siguiente…
regions[header]= Header
En este caso se define la región “header” y se le pone el nombre “Header”.
Dicha región deberá ser definida en el fichero “page.tpl.php” que se explica más adelante. Pero con esta inclusión bastará para que Drupal conozca esas regiones para ese tema visual y desde la configuración de bloques podamos asignar los bloques a dichas regiones.

Fichero de Screenshot

Dentro del mismo directorio será necesario incluir una captura de como queda el tema visual, dicho fichero debe llamarse por defecto screenshot.png, pero podemos cambiar su nombre y localización dentro de la carpeta del tema usando el parámetro screenshot dentro del fichero .info

Configuración de funcionalidades

Dentro de la configuración del tema visual, también deberemos gestionar las distintas configuraciones que podemos llega ra tener por defecto dentro del Tema a través de la configuracion en la administración. Las configuraciones típicas son:
features[] = logo
features[] = name
features[] = slogan
features[] = node_user_picture
features[] = comment_user_picture
features[] = favicon
features[] = main_menu
features[] = secondary_menu

Dichos valores serán los nombres de los ID de los div que deberíamos colocar el fichero page.tpl.php.

Ficheros de plantilla

Los ficheros principales de pantilla de Drupal 7 suelen tener una terminación en “tpl.php” cuando usamos el motor de plantillas “phptemplate”, y son los siguientes:

  • html.tpl.php: almacena la estructura principal del html
  • page.tpl.php: almacena la estructura principal del body de la página
  • region.tpl.php configura cómo salen las regiones de la plantilla
  • block.tpl.php: define cómo salen los bloques
  • node.tpl.php: define como salen los contenidos
  • comment.tpl.php: define cómo salen los comentarios
  • template.php: define hooks de temas
  •  logo.png. logotipo por defecto del tema

Todos los ficheros principales de pantilla estan disponibles en el directorio 2modules/system” y son los que podemos usar de base para la generación de un nuevo tema visual.

El fichero page.tpl.php

Copiamos el contenido completo del page.tpl.php, para así poder explicarlo paso a paso:
<div id=”page-wrapper”><div id=”page”>

    <div id=”header”><div class=”section clearfix”>

      <?php if ($logo): ?>
        <a href=”<?php print $front_page; ?>” title=”<?php print t(‘Home’); ?>” rel=”home” id=”logo”>
          <img src=”<?php print $logo; ?>” alt=”<?php print t(‘Home’); ?>” />
        </a>
      <?php endif; ?>

      <?php if ($site_name || $site_slogan): ?>
        <div id=”name-and-slogan”>
          <?php if ($site_name): ?>
            <?php if ($title): ?>
              <div id=”site-name”><strong>
                <a href=”<?php print $front_page; ?>” title=”<?php print t(‘Home’); ?>” rel=”home”><span><?php print $site_name; ?></span></a>
              </strong></div>
            <?php else: /* Use h1 when the content title is empty */ ?>
              <h1 id=”site-name”>
                <a href=”<?php print $front_page; ?>” title=”<?php print t(‘Home’); ?>” rel=”home”><span><?php print $site_name; ?></span></a>
              </h1>
            <?php endif; ?>
          <?php endif; ?>

          <?php if ($site_slogan): ?>
            <div id=”site-slogan”><?php print $site_slogan; ?></div>
          <?php endif; ?>
        </div> <!– /#name-and-slogan –>
      <?php endif; ?>

      <?php print render($page[‘header’]); ?>

    </div></div> <!– /.section, /#header –>

    <?php if ($main_menu || $secondary_menu): ?>
      <div id=”navigation”><div class=”section”>
        <?php print theme(‘links__system_main_menu’, array(‘links’ => $main_menu, ‘attributes’ => array(‘id’ => ‘main-menu’, ‘class’ => array(‘links’, ‘inline’, ‘clearfix’)), ‘heading’ => t(‘Main menu’))); ?>
        <?php print theme(‘links__system_secondary_menu’, array(‘links’ => $secondary_menu, ‘attributes’ => array(‘id’ => ‘secondary-menu’, ‘class’ => array(‘links’, ‘inline’, ‘clearfix’)), ‘heading’ => t(‘Secondary menu’))); ?>
      </div></div> <!– /.section, /#navigation –>
    <?php endif; ?>

    <?php if ($breadcrumb): ?>
      <div id=”breadcrumb”><?php print $breadcrumb; ?></div>
    <?php endif; ?>

    <?php print $messages; ?>

    <div id=”main-wrapper”><div id=”main” class=”clearfix”>

      <div id=”content” class=”column”><div class=”section”>
        <?php if ($page[‘highlighted’]): ?><div id=”highlighted”><?php print render($page[‘highlighted’]); ?></div><?php endif; ?>
        <a id=”main-content”></a>
        <?php print render($title_prefix); ?>
        <?php if ($title): ?><h1 class=”title” id=”page-title”><?php print $title; ?></h1><?php endif; ?>
        <?php print render($title_suffix); ?>
        <?php if ($tabs): ?><div class=”tabs”><?php print render($tabs); ?></div><?php endif; ?>
        <?php print render($page[‘help’]); ?>
        <?php if ($action_links): ?><ul class=”action-links”><?php print render($action_links); ?></ul><?php endif; ?>
        <?php print render($page[‘content’]); ?>
        <?php print $feed_icons; ?>
      </div></div> <!– /.section, /#content –>

      <?php if ($page[‘sidebar_first’]): ?>
        <div id=”sidebar-first” class=”column sidebar”><div class=”section”>
          <?php print render($page[‘sidebar_first’]); ?>
        </div></div> <!– /.section, /#sidebar-first –>
      <?php endif; ?>

      <?php if ($page[‘sidebar_second’]): ?>
        <div id=”sidebar-second” class=”column sidebar”><div class=”section”>
          <?php print render($page[‘sidebar_second’]); ?>
        </div></div> <!– /.section, /#sidebar-second –>
      <?php endif; ?>

    </div></div> <!– /#main, /#main-wrapper –>

    <div id=”footer”><div class=”section”>
      <?php print render($page[‘footer’]); ?>
    </div></div> <!– /.section, /#footer –>

  </div></div> <!– /#page, /#page-wrapper –>
como puede verse prácticamente todo el contenido del page.tpl.php está dentro del
<div id=”page-wrapper”><div id=”page”>

  </div></div> <!– /#page, /#page-wrapper –>
Éstos son los div princiaples de la página. todo está dentro de ellos.

Features

Como se ha comentado anteriormente las features u opciones configurables de un tema, son aquellas que van indicadas ene l fichero page.tpl.html como div con el id de la feature que indicamos, por ejemplo:
<?php if ($logo): ?>
        <a href=”<?php print $front_page; ?>” title=”<?php print t(‘Home’); ?>” rel=”home” id=”logo”>
          <img src=”<?php print $logo; ?>” alt=”<?php print t(‘Home’); ?>” />
        </a>
      <?php endif; ?>
de esta manera si desde la configuración nos han marcado sacar el logo tipo, el tema presentará ese DIV.

Regiones

Ya comentamos que las regiones definidas en el .info debían tener una representación en el fichero page.tpl.php, he aquí un ejemplo de uso:
<?php if ($page[‘sidebar_first’]): ?>
        <div id=”sidebar-first” class=”column sidebar”><div class=”section”>
          <?php print render($page[‘sidebar_first’]); ?>
        </div></div> <!– /.section, /#sidebar-first –>
      <?php endif; ?>
de esta manera estamos condicionando a que si hay bloques que presentar en la región ‘sidebar_first’ imprimimos el div id=”sidebar-first” en cuyo interior realizamos una llamada a la función render pasándole el array $page e indicándole el índice de la región que queremos sacar en ese div ‘sidebar_first’.
Este proceso deberíamos repetirlo con todos aquellos bloques que hayamos definido en el fichero.info.
Un ejemplo de bloque es el contenido que como pude observarse simplemente se llama a la función “render($page[‘content’])” sin condicionar su salida, ya que el contenido debería salir practicamente siempre en la página.

    Referencias:

    Curso de Drupal 7 (XII): Internacionalización

    Una de las tareas principales en el desarrollo e implantación de sitios web es la internacionalización del sitio web, bien por ser una empresa que quiere empezar a exportar a otros  paises sus productos y servicios o bien por ser una administración pública de una autonomía bilungüe. En cualquier caso, será necesario que la web pueda estar disponible en varios idiomas.
    La internacionalización tiene distintas partes dependiendo del tipo de sitio web que vaya a realizarse, pero pueden destacarse algunas de ellas:

    • Traducción del Interfaz: en este apartado todos aquellos mensajes que genere la aplicación web deberán estar traducidos.
    • Traducción de Páginas y Contenidos: para ello deberemos de ser capaces de traducir todos aquellos contenidos publicables en el sitio web.
    • Traducción de Menús: para así poder disponer de todos los elementos y títulos de menús traducidos.
    • Cambio/Selección de Idioma: manera en la cual el usuario decide cambiar el idioma en el que desea ver el sito web.

    Traducción del Interfaz

    Estamos de enhorabuena, Drupal 7 dispone en su núcleo de todo lo necesario para traducir el interfaz. Para ello utilizaremos fundamentalmente el módulo Locale. Podemos acceder a su configuración desde la Administración->Configuración de Drupal…

    Exacamente desde el apartado Regional e Idioma, donde podemos encontrar los enlaces: Idiomas y Traducir la Interfaz.
    Al acceder a idiomas veremos el listado de idiomas instalados en Drupal…

    como podemos ver en la captura el listado de idiomas en este caso dispone de 2 idiomas, Inglés y Español. En este caso el idioma predeterminado es el Español, pero podemos activar y desactivar idiomas y dejar otro idioma como predeterminado. Después de hacer la configuraciones pulsaremos sobre el botón Guardar Configuración.

    Añadir un idioma

    Pulsamos sobre el enlace Agregar idioma y nos saldrá un formulario de selección del idioma que queremos añadir…

    escogemos el idioma, por ejemplo Japonés y pulsamos en el botón Agregar idioma.
    Nos aparecerá otra vez el listado de idiomas donde aparecerá activado el idioma Japonés.

    Importación de las traducciones

    Una vez incluido el idioma en el sistema será necesario realizar la importación del fichero de traducciones. Para ello iremos a la configuración->Regional e idiomas->Traducir Interfaz y nos parecerá una pantalla similar a la siguiente…

    donde veremos el estado de las traducciones de los distintos idiomas instalados, como puede observarse el idioma Japonés no tiene ninguna traducción disponible así que deberemos de introducirla. Como el proceso de traducción puede ser tremendamente costoso, procederemos a importar las traducciones ya hechas para el idioma que deseemos.
    Para ello pulsamos en la pestaña importar y veremos el formulario de importación y nos facilitará el enlace a las traducciones de nuestra versión de Drupal: http://localize.drupal.org/ y veremos la página de gestión de traducciones de Drupal, donde podremos buscar los distintos proyectos de traducción existentes…

    en el menú lateral derecho buscaremos la traducción que queremos, en nuestro caso Japonés y pulsaremos en el botón “Go there”, lo que nos llevará la página principal del proyecto de traducción: http://localize.drupal.org/translate/languages/ja

    Descargamos el fichero de traducciones para nuestra versión (7.x)…

    Después volveremos al formulario de importación de drupal y pulsaremos el botón examinar para localizar y selccionar el fichero que deseamos subir, el de la traducción. Una vez seleccionado, pulsaremos sobre el botón importar.
    Nota: Este proceso puede alargarse mucho tiempo, sería recomensable ampliar el tiempo de ejecución maximo de pun proceso php, en el php.ini de 30 segundos a 300 por ejemplo para dar tiempo a este proceso de importación a realizarse correctamente…

    como puede verse en la captura se han importado correctamente todos los términos, pero quedan muchos por traducir. En este momento puedes hacer dos cosas, traducirlos tu mismo o aportar las traducciones al proyecto de tu idioma.
    Consejo: Elige siempre la segunda opción, así ahorrarás tiempo en el futuro.

    Cambiar el idioma por defecto

    Al volver sobre la gestión de idiomas, selccionamos el japonés como idioma por defecto y al volver a la configuración veremos una página similar la la siguiente…

     Como puede verse “claramente” el interfaz está ya traducido y es “perfectamente” entendible por un japonés.
    Para volverlo a poner en Español iremos a la ruta “admin/config/regional/language”, volvemos a cambiar el idioma por defecto a español y pulsamos en el botón “設定を保存” también llamado “Guardar Configuración”.

     Traducción de Contenidos, Menús y Títulos

    Para este apartado necesitamos los siguientes módulos:

    Una vez instalados y activados estos módulos ya podemos realizar las traducciones del contenido, los menús y títulos.
    También deberemos activar el módulo Content Translation si no lo hemos hecho previamente.

    Traducción de Tipos de Contenido

    En el caso de Drupal 7 será necesario la configuración de todos aquellos tipos de contenido que queramos que sean traducibles para ello iremos a la configuración a través de Administración->Estructura->Tipos de Contenido y veremos el listado de los tipos…

    pulsamos en el botón editar al lado del tipo de contenido y veremos en el apartado “Opciones de publicación” la parte de configuración de Multi-idioma…

    selccionamos la opción “Habilitado, con traducción” y pulsamos en el botón “Guardar este tipo de contenido”.
    Después necesitaremos poder traducir también los títulos de los tipos de contenido para ello, en el listado de tipos de contenido pulsamos el enlace de “gestionar campos” y aparecemos en el listado de campos, veremos que nos aparece un nuevo enlace al lado del campo title llamado “replace”…

    lo pulsamos y aparecemos en un mini formulario que premitirá convertir el campo Title en un campo “instance”…

    selccionamos la checkbox y pulsamos en el botón “Guardar la configuración” apareceremos otra vez en el listado de campos y el campo title ya será editable.
    Ahora podremos ir seleccionando tipo de campo a tipo de campo, para entrar a sus configuraciones, pulsando en el enlace del tipo de campo…

    donde debería aparecer un formulario con una checkbox…

    que si la pulsamos hará el campo traducible.

    Nota: Esta operación deberíamos hacerla con todos los campos para verificar que todos los textos son traducibles.

    Nota: Una vez salvados los datos de configuración, repetiremos esta misma operación con todos los tipos de contenido.

    Traducción de contenidos

    Así cuando vayamos a añadir o editar un tipo de contenido nos aparecerá un nuevo selector…

    como puede verse en la captura en el formulario nos añade este nuevo elemento con el selector de idioma, rellenamos el contenido en dicho idioma y seleccionamos el idioma por ejemplo, Español.
    Cuando terminemos de editarlo pulsamos en el botón “Guardar”.
    Después veremos la nueva página insertada tal como se ve publicada…

    como podemos ver, hemos introducido una página con el título Inicio y un pequeño texto. Y además nos salen dos pestañas encima del contenido: Editar y Traducir.
    Si pulsamos sobrel apestaña traducir, nos aparecerá lo siguiente…

    un listado con los distintos idiomas indicándonos cuales de ellos no se han traducido todavía, ya que nos coloca un enlace al lado del idioma, llamado “agregar traducción”, si lo pulsamos…

    nos aparece el formulario de edición de contenido con el idioma ya selccionado, sólo deberemos editar los campos y pulsar en el botón “Guardar”. Y nos aparecerá el contenido traducido en la pantalla…

    Con fines de práctica colocaremos esta página inicial como Página principal del sitio web, a través de la Configuración->Información del Sitio->Página principal y modificamos la página inicial predeterminada para poner “node/1” en el campo. Despues pulsamos en el botón Guardar.
    Si vamos a la página inicial, veremos la página en Español, pero podremos observar que existe un nuevo enlace debajo de la noticia con el título “English”…

    si lo pulsamos veremos la página en inglés con la traducción que hemos introducido anteriormente y un enlace pequeño llamado “español”, con el que podremos volver a la traducción al Español.
    De esta manera dispondremos de las traducciones de los contenidos y enlaces para ver las distintas versiones de los contenidos en el resto de idiomas.

    Selección automática de Idioma

     Para ello utilizaremos el bloque “Alternador de idioma (Contenido)”, iremos al apartado de Bloques y en el selector de región podremos por ejemplo “Primera barra lateral” y pulsamos en el botón “Guardar Bloques”.
    A continuación deberemos ir a la parte de configuración de la selcción autómática del idioma Configuración->Reginal e Idiomas->Idiomas->Detección y Selección o la ruta “admin/config/regional/language/configure”…

     donde podremos selccionar la manera en la cual Drupal quiere detectar gestionar la presentación del idioma, la mejor manera es inicialmente por URL, así antepondrá a cualquier contenido la ruta “es/” o “en/”. Seleccionamos en los dos casos URL y pulsamos en el botón “Guardar la configuración”.
    Cuando volvamos ala página principal veremos que nos aparecerá el selector de idioma en la región que indicamos anteriormente en la configuración de bloques.

    Traducción de menús

    La traducción demenús se realiza de una manera similar a las traducciones de los tipos de contenido. Antes de nada deberemos editar cada menú desde el listado de menús Administración->Estructura->Menús pulsando en el enlace “Editar menú” de esta manera nos saldrá el formulario de edición del menú con unos campso extra…

    como puede verse nos han colcoado un conjunto de botones de radio en el formulario. Seleccionaremos “Translate and Localize” y pulsamos en el botón “Guardar”. Apareceremos en el listado de enlaces del menú y nos aparece una nueva pestaña llamada “Traducir”, si pulsamos sobre ella…

    donde podemos ver las traducciones del menú, si pulsamos sobre un enlace “translate”…

    Cambiaremos los textos del título y de la descripción y pulsamos en el botón “Guardar traducción” (Save translation). Apareceremos en el listado de traducciones y veremos que la traducción ha sido guardada correctamente…

    pulsaremos sobre la pestaña listar enlaces y apareceremos en ese listado. Si pulsamos sobre el botón editar de un enlace iremos al formulario de edición y veremos los campos del enlace, así como un nuevo selector de idioma, parecido al de selccion de idioma del formulario de edición de contenidos…

    seleccionamos el idioma, por ejemplo Inglés, cambiamos los textos de los enlaces y pulsamos en el botón “Guardar”.
    Referencias:

    • Lullabot.com: http://www.lullabot.com/articles/localized-and-multi-lingual-content-drupal-7
    Licencia Creative Commons

    Curso de desarrollo de módulos de Drupal 7 (IV): Administración y Formularios

    Una de las tareas típicas a la hora de gestionar un módulo es el apartado de la Configuración. Siguiendo con el ejemplo mostrado en la primera y segunda entrega. Añadiremos las funcionalidades necesarias para poder gestionar un apartado en la administración Drupal.

    Recordatorios

    En las anteriores entregas ya tneíamos creada la carpeta del módulo, y los ficheros ejemplo.info y ejemplo.install. Donde introduciamos las funciones de ejemplo_install() y ejemplo _uninstall(). En el ejemplo.info ya habíamos colocado una ruta a la administración del módulo metiante el parámetro settings con el valor “admin/config/ejemplo/settings”.

    Gestionando las entradas en el Menú de Administración

    Para incluir las nuevas funciones será necesario añadir un nuevo fichero al módulo. Este fichero deberá llamarse nombredemodulo.module, en nuestro caso se llamará ejemplo.module. Lo creamos y colocamos la cabecera del fichero típica:
    <?php
    /**
    * @file
    * Fichero del módulo de ejemplo para drupal
    *
    * Descripción larga del fichero que entra dentro del módulo de ejemplo de Drupal
    */
    Por otra parte será necesario que incluyamos una referencia al fichero ejemplo.module en el fichero ejemplo.module, incorporando una nueva línea al fichero:
    files[] = ejemplo.module
    De esta manera dentro de la información del módulo se hará referencia a este nuevo fichero.

    Incluyendo la implementación del hook_menu()

    Entonces tenemos ya todo preparado para poder introducir la gestión de los nuevos elementos de menú. Para ello deberemos relizar una implementación del hook_menu(), en nuestro caso deberá llamarse ejemplo_menu(), por lo que incluiremos el siguiente código en el fichero ejemplo.module:
    /**
     * Implementation of hook_menu().
     */
    function ejemplo_menu() {
    }
    A continuación deberemos gestionar las entradas en el menú de administración, mediante la declaración de arrays asociativos con los datos necesarios para generar y gestionar la entrada de cada elemento del menú.
    Empezaremos por el elemento que se introduce en el menú de administración, para ello incluimos el siguiente código en la función ejemplo_menu():
    $items[‘admin/config/ejemplo’] = array(
        ‘title’ => ‘Node annotation’,
        ‘description’ => ‘Adjust node annotation options.’,
        ‘position’ => ‘right’,
        ‘weight’ => -5,
        ‘page callback’ => ‘system_admin_menu_block_page’,
        ‘access arguments’ => array(‘administer site configuration’),
        ‘file’ => ‘system.admin.inc’,
        ‘file path’ => drupal_get_path(‘module’, ‘system’),
      );
      return $items;

    como puede verse en el código rellenamos un array con los datos del enlace y hacemos que la función devuelva dicho array.
    El resultado de la inclusión de este código es la generación de un nuevo enlace en el menú de Configuración de Drupal, tras su activación, como puede verse en la siguiente captura…

    Si pasamos el ratón por encima del nuevo enlace en el menú veremos que apunta a la ruta “admin/config/ejemplo/settings” tal como hemos indicado en fichero de ejemplo.info. La gestión de la llamada a esa URL es la que incluiremos con el siguiente elemento del array asociativo $items.
    Expliquemos cuáles son los elementos que estamos definiendo en el array asociativo $items:

    • el índice que utilizamos en la ruta de inclusión del enlace, a efectos prácticos incluye el enlace en la parte de Configuración de la administración, porque hemos puesto como índice “admin/config/ejemplo”, ya que “admin/config” es la ruta hasta el menú de Configuración, y al incluir la siguiente ruta “/ejemplo” nos crea esa nueva entrada.
    • title: es el ítulo que debe aparecer en el enlace, este texto es automáticamente traducible, no hace falta hacer uso de la función t() para traducirlo.
    • description: es la descripción que aparece justo debajo del enlace.
    • position: indica la zona donde debe presentar el enlace, en este caso nos lo intenta presentar en la parte de la derecha ‘right’.
    • weight: es el peso que queremos darle al enlace, el peso es una variable que cuanto mayor valor tenga más arriba debería aparecer el enlace en el menú y viceversa. en este caso es -5 por lo que debería aparecer debajo de los elementos principales del menú.
    • page callback: es la función que debe manejar dicho enlace. en este caso al ser un enlace de la configuración debe ser una función de drupal la que sea capaz de gestionarlo, por lo que introducimos el valor ‘system_admin_menu_block_page’ que es la función del módulo system que gestiona ese bloque de menú.
    • access arguments: son los permisos necesarios para ver esa entrada de menú, como todo elemento de la configuración requiere del permiso ‘administer site configuration’, que lo introducimos como un elemento de un array, debido a que podemos tener combinaciones de permisos para poder acceder a distintos elementos del menú.
    • file: indicación del fichero que contiene la función de callback, en este caso es el fichero ‘system.admin.inc’, si nos damos cuenta la mayor parte de las funciones de callback de los menús se gestionarán desde ficheros de módulo que se llaman nombredemodulo.admin.inc.
    • file path: indicación de la ruta donde está presente el fichero system.admin.inc, en este caso la coseguiremos a partir de la función drupal_get_path(‘module’, ‘system’), que nos devolverá la ruta del directorio del módulo ‘system’, para su posterior inclusión y ejecución de la llamada a la función ‘system_admin_menu_block_page’.

     Ahora con el enlace ya en su sitio debemos gestionar la URL del formulario de configuración del módulo, por lo que debemos introducir otro elemento en el array $items antes de devolverlo, incluyendo el siguiente código:
    $items[‘admin/config/ejemplo/settings’] = array(
        ‘title’ => ‘Annotation settings’,
        ‘description’ => ‘Change how annotations behave.’,
        ‘page callback’ => ‘drupal_get_form’,
        ‘page arguments’ => array(‘ejemplo_admin_settings’),
        ‘access arguments’ => array(‘administer site configuration’),
        ‘type’ => MENU_NORMAL_ITEM,
        ‘file’ => ‘ejemplo.admin.inc’,
      );
    Debido a que queremos gestionar un formulario los elemento del array son ligeramente distintos a los que introdujimos anterioremente:

    • el índice es la URL que queremos gestionar, en este caso la que se pulsa desde el menú de Configuración para acceder a los “settings” del módulo ejemplo.
    • title: es el título que colocaremos a la página del formulario
    • description: es la descripción del formulario en sí mismo, podríamos verla desde la gestión de menús.
    • page callback: es la función a la que necesitamos llamar cuando nos llamen desde esa URL, en este caso en la función ‘drupal_get_form’ ya que debemos devolver un formulario.
    • page arguments: son los argumentos a pasar a esa función, en este caso es el nombre de la función del módulo que se invocará para coseguir dicho formulario, que se llamará ‘ejemplo_admin_settings’.
    • access arguments: como en el caso anterior necesitaremos los permisos de admisnitración del sitio para poder entrar a esta parte de la configuración.
    • type: indicamos que la entrada del menú es un MENU_NORMAL_ITEM, significa que será un elemento de un menú que el administrador debe ser capaz de poder mover u ocultar como desee.
    • file: nombre del fichero donde se almacenará la función ‘ejemplo_admin_settings’, en nuestro caso será el nuevo fichero ‘ejemplo.admin.inc’

    Como podemos ver necesitaremos un nuevo fichero llamado ‘ejemplo.admin.inc’ en el módulo para que pueda gestionar las funciones del formulario que hemos indicado en la entrada del menú que acabamos de introducir.
    Así que creamos dicho fichero ‘ejemplo.admin.inc’, lo colocamos en el mismo directorio del módulo y le incluimos la cabecera típica de los ficheros php del módulo:
    <?php
    // $Id$

    /**
     * @file
     * Administration page callbacks for the ejemplo module.
     */
    También será necesario incluir dicho fichero en el ejemplo.info mediante la inclusión de una nueva línea:
    files[] = ejemplo.admin.inc
    De esta manera el módulo sabrá todos los ficheros incluye.

    El formulario  de Administración del módulo

    Generando el Formulario de Configuración

    Ahora es cuando deberíamos introducir las funciones de generación, validación y envío del formulario.
    Antes de empezar será necesario hacernos una idea del formulario que queremos gestionar, para ello veamos una captura del formulario generado…

     como puede verse, en el formulario disponemos fundamentalmente de los siguientes campos:

    • Un listado de checkboxes que representan los distintos tipos de contenido presentes en ese momento en Drupal. Si es la primera vez que ocnfigurarmos el módulo, no deberían aparecer ninguno de los tipos de contenidos como “checked”, y que no los habíamos selccionado previamente. Pero si anteriormente los habíamos seleccionado sí deberían aparecer como “checked”.
    • Un grupo de botones de Radio que indican cuando deberían borrarse el valor del campo “anotaciones” hechos sobre esos tipos de contenido. El valor predefinido será que nunca debe borrar dichas anotaciones. Debería recordar la opción marcada en anteriores configuraciones.
    • Un cuadro de texto que permite la introducción de un número, que representa el número de campos ‘annotation’ que debería añadir a los tipos de contenido marcados.
    • Un botón de Submit para guardar las configuraciones.

    Teniendo estos datos presentes, procederemos a incluir el código en el fichero ejemplo.admin.inc de la función ejemplo_admin_settings:
    /**
     * Form builder. Configure annotations.
     *
     * @ingroup forms
     * @see system_settings_form().
     */
    function ejemplo_admin_settings() {
     
      $types = node_type_get_types();
      foreach($types as $node_type) {
         $options[$node_type->type] = $node_type->name;
      } 
     
      $form[‘ejemplo_node_types’] = array(
        ‘#type’ => ‘checkboxes’,
        ‘#title’ => t(‘Users may ejemplo these content types’),
        ‘#options’ => $options,
        ‘#default_value’ => variable_get(‘ejemplo_node_types’, array(‘page’)),
        ‘#description’ => t(‘A text field will be available on these content types to
           make user-specific notes.’),
      );
     
      $form[‘ejemplo_deletion’] = array(
        ‘#type’ => ‘radios’,
        ‘#title’ => t(‘Annotations will be deleted’),
        ‘#description’ => t(‘Select a method for deleting annotations.’),
        ‘#options’ => array(
          t(‘Never’),
          t(‘Randomly’),
          t(‘After 30 days’)
        ),
        ‘#default_value’ => variable_get(‘ejemplo_deletion’, 0) // Default to Never
      );

      $form[‘ejemplo_limit_per_node’] = array(
        ‘#type’ => ‘textfield’,
        ‘#title’ => t(‘Annotations per node’),
        ‘#description’ => t(‘Enter the maximum number of annotations allowed per
          node (0 for no limit).’),
        ‘#default_value’ => variable_get(‘ejemplo_limit_per_node’, 1),
        ‘#size’ => 3
      );
      return system_settings_form($form, TRUE);

    }
    Como vemos en el código no es necesario pasarle ningún argumento a la función, todos los datos del formulario que queremos generar los meteremos dentro de la propia función.
    los fundamentos de la función es que generaremos un array asociativo que pasaremos a la función ‘system_settings_form’ como parámetro principal y lo que nos devuelva esa función es lo que devolveremos nosotros en la función ‘ejemplo_admin_settings’, que será lo que le pasemos a la función ‘drupal_get_form’, que es la que finalmente calculará el formulario para poder presentarlo en la página de Drupal. Esta secuencia de llamadas será común en todos los formularios de configuración de módulos.
    Veamos cómo configuramos el formulario para cada uno de los elementos que debemos presentar.
    Empecemos con la configuración del listado de checkboxes:
    $types = node_type_get_types();
      foreach($types as $node_type) {
         $options[$node_type->type] = $node_type->name;
      } 
     
      $form[‘ejemplo_node_types’] = array(
        ‘#type’ => ‘checkboxes’,
        ‘#title’ => t(‘Users may ejemplo these content types’),
        ‘#options’ => $options,
        ‘#default_value’ => variable_get(‘ejemplo_node_types’, array(‘page’)),
        ‘#description’ => t(‘A text field will be available on these content types to
           make user-specific notes.’),
      );
    la parte del formulario que generaremos es la siguiente…

    como debemos presentar los distintos nombre de los tipos de contenido necesitamos generar un array para poder pasar a la generación de las checkboxes, que es lo que hacemos con la obtención del array $types y el foreach que hay justo debajo, donde indicamos como índice el tipo de contenido y en el valor el nombre del tipo de contenido.
    Ahora toca configurar los elementos del array asociativo para las checkboxes:

    • el índice es parte del nombre del elemento que generaremos en el formulario, com una identificación de la parte del formulario donde nos encontramos.
    • #type: indica el tipo de elemento del formulario que queremos gestionar, en este caso checkboxes.
    • #title: es la traducción del título que le damos a esa sección del formulario, como pude verse se usa la función traductora t()
    • #options: es el array que hemos generado anteriormente con el array $options, con los nombres de los tipos de contenidos presentes en el sistema.
    • #description: la descripción que aparecerá debajo del título, que debería ayudar a la rellenado de dicha parte del formulario, también se usa una llamada a la función t()
    • #default_value: define las opciones por defecto que deberían aparecer marcadas según se netra al formulario, en este caso se hace una llamada a la función ‘variable_get’, que es la función principal del drupal para coger variables que nos pasan, indicando el nombre de la variable que queremos capturar ‘ejemplo_node_types’ que coincide con el nombre del índice que hemos colocado, que es la que agrupa las opciones de tipos de contenido en el formulario. Y le pasamos también la opción por defecto, como segunda parámetro si no los pasan esa variable ‘ejemplo_node_types’, que como puede verse es un array con un único elemento que es el tipo ‘page’, que es el nombre que tiene el tipo de contenido página, que viene por defecto en Drupal 7 con la instalación Standard, y sin módulos extra activados.

    Ahora pasamos a la generación de los radio button…

    mediante el código:
    $form[‘ejemplo_deletion’] = array(
        ‘#type’ => ‘radios’,
        ‘#title’ => t(‘Annotations will be deleted’),
        ‘#description’ => t(‘Select a method for deleting annotations.’),
        ‘#options’ => array(
          t(‘Never’),
          t(‘Randomly’),
          t(‘After 30 days’)
        ),
        ‘#default_value’ => variable_get(‘ejemplo_deletion’, 0) // Default to Never
      );
    Como puede verse la estructra del array es muy similar:

    • el indice marca el nombre del apartado del formulario ‘ejemplo_deletion’
    • #type: en este caso el valor es ‘radios’ porque queremos generar radio buttons.
    • #descripción: con el texto traducido
    • #options: en este caso lo generamos de manera manual, con un array asociativo, donde incluimos los textos traducibles de las distintas opciones a presentar.
    • #default_value: indicamos que si no nos mandan la variable ‘ejemplo_deletion’ debemos marcar por defecto la primera opción (la 0) del listado de opciones, y si nos mandan la variable, cogemos el valor que nos hayan marcado anteriormente.

     A continuación generaremos la parte de la input text para recoger el número de anotaciones por tipo de contenido…

    incluyendo el siguiente código:
    $form[‘ejemplo_limit_per_node’] = array(
        ‘#type’ => ‘textfield’,
        ‘#title’ => t(‘Annotations per node’),
        ‘#description’ => t(‘Enter the maximum number of annotations allowed per
          node (0 for no limit).’),
        ‘#default_value’ => variable_get(‘ejemplo_limit_per_node’, 1),
        ‘#size’ => 3
      );
    En el array asociativo se definen los siguientes elementos:

    • el índice es el nombre de la zona del formulario que queremos gestionar ‘ejemplo_limit_per_node’
    • #type: nos indica que quiere generar un campo de texto ‘textfield’
    • #title y #description: nos dan las cadenas traducibles para el título y la descripción, como es habitual
    • #default_value: captura la variable ‘ejemplo_limit_per_node’ y coloca su valor si nos pasan la variable y sino coloca el valor 1 en el campo de texto.
    • #size: nos define el tamaño del cuadro de texto.

    No está de más recordar, que una vez definidos estos elementos del array $form necesitamos devolverlo a través de la llamada a la función ‘system_settings_form’ poniendo:
    return system_settings_form($form, TRUE);

    Validación del Formulario

    Una vez generado el formulario necesitaremos saber cómo validarlo, ésto lo realizaremos a través de una nueva función con un nombre similar a la anterior, en este caso ‘ejemplo_admin_settings_validate’, el nombre dela función depende del nombre que le hayamos dado a la función de callback de generación del formulario, es decir se añade la cadena ‘_validate’ al nombre de la primera función para poner el nombre a dicha función de validación, así que tendremos que incluir un código como el siguiente:
    /**
     * ejemplo_admin_settings_validate
     *
     * Update content types to reflect changes to the annotation configuration options selected by the site administrator.
     * If the administrator checked a box for a content type that did not previously have an annotation field then add it to the
     * content type.   If a content type is unchecked by the administrator and that content type had an annotation field, then
     * remove that field from the content type.
     */
    function ejemplo_admin_settings_validate($form, $form_state) {
    }
    Como puede verse a la función de validación se le pasan fundamentalmente dos parámetros, $form que es el formulario que queremos generar y $form_state que es el estado actual del formulario que estamos gestionando, es decir los datos.
    Esta función debería gestionar dos cosas:

    • Las validaciones del formulario: es decir todas aquellas cosas que tengamos que comprobar para que el fomulario valide. Si no valida será necesario generar los errores a presentar al usuario para decirle porqué no valida el formulario, mediante la función form_set_error() y realizar un return, para que no continue la validación.
    • Realizar las acciones necesarias cuando el formulario valida, es decir guardar todos aquellos valores del formulario en la configuración o donde sea necesario para que no se pierdan.

    Empecemos por la parte de validación:
    $limit = $form_state[‘values’][‘ejemplo_limit_per_node’];
      if (!is_numeric($limit)) {
        form_set_error(‘ejemplo_limit_per_node’, t(‘Please enter a number.’));
        return;
      }
     En este caso sólo estamos validando un campo, el ‘ejemplo_limit_per_node’, para verificar que su valor efectivamente es numérico. Para ello utilizamos la variable $form_state, donde através de su índice [‘values’] almacena los valores introducidos en el formulario por parte del usuario, luego indicamos el ‘campo’ que queremos comprobar, en este caso ‘ejemplo_limit_per_node’.
    Dependiendo del tipo de campo podemos enocntrarnos con valores simples o arrays de selección, el tratamiento a estos valores dependerá del ‘#type’ de ‘campo’ del formulario.
    En este caso simplemente hacemos un if para saber si el campo es numérico mediante la función ‘is_numeric’ y generarmos el error y hacemos el return sin devolver nada.

    En el caso de que el formulario valide, continuamos con la función haciendo lo que tengamos que hacer con los valores del formulario.
    En primer lugar intentamos añadir el campo ‘annotation’ a los tipos de contenido que nos han seleccionado en las checkboxes:
    // loop through each of the content type checkboxes shown on the form
      foreach ($form_state[‘values’][‘ejemplo_node_types’] as $key => $value) {

    // if the check box for a content type is unchecked, look to see whether this content type has the
    // annotation field attached to it using the field_info_instance function.  If it does then we
    // need to remove the annotation field as the administrator has unchecked the box.
         if (!$value) {
            $instance = field_info_instance(‘node’, ‘annotation’, $key);
             if (!empty($instance)) {
               field_delete_instance($instance);    
               watchdog(“Annotation”, “Deleted annotation field from content type: “.$key);
            } // end of annotation existed, need to remove it
         } else {
    // if the check box is checked for a content type look to see whether the field is associated with that
    // content type.  If not then add the annotation field to the content type
            $instance = field_info_instance(‘node’, ‘annotation’, $key);
            if (empty($instance)) {
                $instance = array(
                 ‘field_name’ => ‘annotation’,
                   ‘entity_type’ => ‘node’,
                   ‘bundle’ => $key,
                    ‘label’ => t(‘Annotation’),
                    ‘widget_type’ => ‘text_textarea_with_summary’,
                    ‘settings’ => array(‘display_summary’ => TRUE),
                    ‘display’ => array(
                    ‘default’ => array(
                         ‘type’ => ‘text_default’,
                     ),
                    ‘teaser’ => array(
                         ‘type’ => ‘text_summary_or_trimmed’,
                     ),
                    ),
                );
           
                $instance = field_create_instance($instance);
       
                watchdog(“Annotation”, “Added annotation field to content type: “.$key);
            } // end of annotation field didn’t exist so add it
         } // end of if
     
      } // end of foreach

    Por lo que hacemos un foreach sobre el ‘campo’ ‘ejemplo_node_types’ para recorrer el array de las opciones que nos devuelven.
    Si no está marcado el tipo de contenido, verfificamos si ese tipo de contenido tenía añadido ese campo y asi es así borramos la relación ($instance) de ese tipo de contenido.
    Por otra parte si está marcado ese tipo de contenido miramos si no existe la $instance de ese campo para ese tipo de contenido y si es así, no existe esa relación, rellenamos un array $instance para ese ‘bundle’ o tipo de cotenido y añadimos la $instance. Por lo que estará relacionado ese campo con ese tipo de contenido.

    Prueba de funcionamiento

    Una vez colocadas las funciones de generación validación del formulario, será necesario poder probarlas, para ello si hace falta desintalaremos y volveremos a instalar el módulo. E intentaremos acceder a la Configuración del Módulo. Debería aparecernos el formulario antes generado y debería realizar correctamente la validación y modificación de los campos de los tipos de contenido indicados.
    También deberíamos probar desde el formulario de edición de dichos tipos de contenido si efectivamente aparece el campo en el formulario, de una manera similar a la siguiente…

    Añadiendo el campo la presentación del Tipo de Contenido

    Una vez almacenada la información deberíamos modificar la presentación de ese campo de anotaciones sólo en el caso de que el usuario que ha introducido el nodo (contenido) es el que lo esté visualizando, para ello utilizaremos el hook_node_load() por lo que deberemos introducir una nueva función en el fichero ejemplo.module con el siguiente código:
    /**
     * implements hook_node_load
     */
    function ejemplo_node_load($nodes, $types) {

      global $user;
     
    // check to see if the person viewing the node is the author, if not then hide the
    // annotation
      foreach ($nodes as $node) {
         if ($user->uid != $node->uid) {
            unset($node->annotation);
         }
      }

    }
    De esta manera a través del primer parámetro $nodes, que es un array con los distintos nodos a presentar, y la objeto global $user (donde se alamacenan los datos del usuario) vamos cogiendo cada nodo como la variable $node con un foreach y vamos comprobando si el usuario que actualmente está visitando la página es el autor del nodo, si no es así realizamos un unset del atributo del objeto que almacena la anotación para que no se presente.

    Referencias

    Licencia Creative Commons

    Curso de desarrollo de módulos para Drupal 7(III): hook_schema explicado con el módulo User

    Para entender bien esta entrega será necesario tener conocimientos avanzados de uso de arrays en PHP.
    Como explicar el hook_schema requiere de un modelo de datos en concreto vamos a apoyarnos sobre el módulo User, que forma parte del nucleo de Drupal.
    Abrimos el fichero “modules/user/user.install” desde Aptana para ver su contenido, pulsando doble clic sobre él. Y nos encontramos con el siguiente código en la función user_schema():
    /**
     * Implements hook_schema().
     */
    function user_schema() {
      $schema[‘authmap’] = array(
        ‘description’ => ‘Stores distributed authentication mapping.’,
        ‘fields’ => array(
          ‘aid’ => array(
            ‘description’ => ‘Primary Key: Unique authmap ID.’,
            ‘type’ => ‘serial’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
          ),
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => “User’s {users}.uid.”,
          ),
          ‘authname’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 128,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => ‘Unique authentication name.’,
          ),
          ‘module’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 128,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => ‘Module which is controlling the authentication.’,
          ),
        ),
        ‘unique keys’ => array(
          ‘authname’ => array(‘authname’),
        ),
        ‘primary key’ => array(‘aid’),
        ‘foreign keys’ => array(
          ‘user’ => array(
            ‘table’ => ‘users’,
            ‘columns’ => array(‘uid’ => ‘uid’),
          ),
        ),
      );

      $schema[‘role_permission’] = array(
        ‘description’ => ‘Stores the permissions assigned to user roles.’,
        ‘fields’ => array(
          ‘rid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Foreign Key: {role}.rid.’,
          ),
          ‘permission’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 128,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => ‘A single permission granted to the role identified by rid.’,
          ),
          ‘module’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 255,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => “The module declaring the permission.”,
          ),
        ),
        ‘primary key’ => array(‘rid’, ‘permission’),
        ‘indexes’ => array(
          ‘permission’ => array(‘permission’),
        ),
        ‘foreign keys’ => array(
          ‘role’ => array(
            ‘table’ => ‘roles’,
            ‘columns’ => array(‘rid’ => ‘rid’),
          ),
        ),
      );

      $schema[‘role’] = array(
        ‘description’ => ‘Stores user roles.’,
        ‘fields’ => array(
          ‘rid’ => array(
            ‘type’ => ‘serial’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Primary Key: Unique role ID.’,
          ),
          ‘name’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 64,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => ‘Unique role name.’,
            ‘translatable’ => TRUE,
          ),
          ‘weight’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => ‘The weight of this role in listings and the user interface.’,
          ),
        ),
        ‘unique keys’ => array(
          ‘name’ => array(‘name’),
        ),
        ‘primary key’ => array(‘rid’),
        ‘indexes’ => array(
          ‘name_weight’ => array(‘name’, ‘weight’),
        ),
      );

      $schema[‘users’] = array(
        ‘description’ => ‘Stores user data.’,
        ‘fields’ => array(
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Primary Key: Unique user ID.’,
            ‘default’ => 0,
          ),
          ‘name’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 60,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => ‘Unique user name.’,
          ),
          ‘pass’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 128,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => “User’s password (hashed).”,
          ),
          ‘mail’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 254,
            ‘not null’ => FALSE,
            ‘default’ => ”,
            ‘description’ => “User’s e-mail address.”,
          ),
          ‘theme’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 255,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => “User’s default theme.”,
          ),
          ‘signature’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 255,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => “User’s signature.”,
          ),
          ‘signature_format’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 255,
            ‘not null’ => FALSE,
            ‘description’ => ‘The {filter_format}.format of the signature.’,
          ),
          ‘created’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => ‘Timestamp for when user was created.’,
          ),
          ‘access’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => ‘Timestamp for previous time user accessed the site.’,
          ),
          ‘login’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => “Timestamp for user’s last login.”,
          ),
          ‘status’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘size’ => ‘tiny’,
            ‘description’ => ‘Whether the user is active(1) or blocked(0).’,
          ),
          ‘timezone’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 32,
            ‘not null’ => FALSE,
            ‘description’ => “User’s time zone.”,
          ),
          ‘language’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 12,
            ‘not null’ => TRUE,
            ‘default’ => ”,
            ‘description’ => “User’s default language.”,
          ),
          ‘picture’ => array(
            ‘type’ => ‘int’,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => “Foreign key: {file_managed}.fid of user’s picture.”,
          ),
          ‘init’ => array(
            ‘type’ => ‘varchar’,
            ‘length’ => 254,
            ‘not null’ => FALSE,
            ‘default’ => ”,
            ‘description’ => ‘E-mail address used for initial account creation.’,
          ),
          ‘data’ => array(
            ‘type’ => ‘blob’,
            ‘not null’ => FALSE,
            ‘size’ => ‘big’,
            ‘serialize’ => TRUE,
            ‘description’ => ‘A serialized array of name value pairs that are related to the user. Any form values posted during user edit are stored and are loaded into the $user object during user_load(). Use of this field is discouraged and it will likely disappear in a future version of Drupal.’,
          ),
        ),
        ‘indexes’ => array(
          ‘access’ => array(‘access’),
          ‘created’ => array(‘created’),
          ‘mail’ => array(‘mail’),
        ),
        ‘unique keys’ => array(
          ‘name’ => array(‘name’),
        ),
        ‘primary key’ => array(‘uid’),
        ‘foreign keys’ => array(
          ‘signature_format’ => array(
            ‘table’ => ‘filter_format’,
            ‘columns’ => array(‘signature_format’ => ‘format’),
          ),
        ),
      );

      $schema[‘users_roles’] = array(
        ‘description’ => ‘Maps users to roles.’,
        ‘fields’ => array(
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => ‘Primary Key: {users}.uid for user.’,
          ),
          ‘rid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘default’ => 0,
            ‘description’ => ‘Primary Key: {role}.rid for role.’,
          ),
        ),
        ‘primary key’ => array(‘uid’, ‘rid’),
        ‘indexes’ => array(
          ‘rid’ => array(‘rid’),
        ),
        ‘foreign keys’ => array(
          ‘user’ => array(
            ‘table’ => ‘users’,
            ‘columns’ => array(‘uid’ => ‘uid’),
          ),
          ‘role’ => array(
            ‘table’ => ‘roles’,
            ‘columns’ => array(‘rid’ => ‘rid’),
          ),
        ),
      );

      return $schema;
    }

    Como puede verse en el ejemplo, básicamente está utilizando una variable llamada “$schema” que al final de la función devuelve. Dicha variable es un array que contiene un índice por cada tabla que necesita generar.
    Por ejemplo si queremos generar la tabla ‘users’, indicamos que sobre el array $schema metemos un nuevo elemento con el índice ‘users’ y como valor metemos otro array.
    El primer elemento del array valor de la tabla ‘users’ será la descripción de la tabla, cuyo índice será ‘description’ y cuyo valor será una cadena de caracteres con la descripción de la tabla.
    El segundo elemento tendrá como índice el nombre de ‘fields’ y contiene un array con las defniciones de los campos de la tabla.
    Así tendremos un array $schema definido de esta manera:
    $schema[‘users’] = array(
        ‘description’ => ‘Stores user data.’,
        ‘fields’ => array()
    );
    En dicho array de campos (‘fields’), vamos introducciendo cada campo como un elemento cuyo índice es el nombre del campo y su valor es otro array con los metadatos del campo que queremos meter en la tabla, como el caso del campo ‘uid’…
    $schema[‘users’] = array(
        ‘description’ => ‘Stores user data.’,
        ‘fields’ => array(
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Primary Key: Unique user ID.’,
            ‘default’ => 0,
          )
         )
    );
    En este caso, el campo uid, tiene los siguientes elementos definidos:

    • type: tipo de campo definido por drupal
    • unasigned: valor booleano que permite definir si el campo es con o sin signo. Sólo disponible para campos numéricos.
    • not_null: valor booleano que nos indica si es campo puede ser null o no
    • description: descripción del campo
    • default: valor por defecto del campo

    Como puede verse dicha definición de campo es una estructuración en forma de array de las típias configuraciones que solemos hacer en la propia base de datos cuando creamos un campo nuevo.
    De esta manera ñadiríamos todos los campos que queremos que tenga la tabla, como distintos elementos del valor del array ‘fields’.
    A continuación seguramente será necesario indicar de todos los campos cual (o cuales) es la clave primaria, en el caso de la tabla de users el campo ‘uid’ es el campo de clave primaria de la tabla, por lo que tenemos que insertar un nuevo elemento en el array principal $schema[‘users’] llamado ‘primary_key’ cuyo valor es un array con el nombre de los campos que son clave primaria. Por lo que el código quedaría así:
    $schema[‘users’] = array(
        ‘description’ => ‘Stores user data.’,
        ‘fields’ => array(
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Primary Key: Unique user ID.’,
            ‘default’ => 0,
          )
         ),
        ‘primary key’ => array(‘uid’)
    );
     En el caso de la tabla users, tambiñen necesita hacer uso de índices sobre campos, para ello introduce también dentro del array $schema[‘users’] un elemento cuyo índice es ‘indexes’ cuyo valor debería ser un array con el nombre del índice y su valor un array con los nombres de los campos que queremos introducir en el índice. Así quedaría el array de la siguiente manera…
    $schema[‘users’] = array(
        ‘description’ => ‘Stores user data.’,
        ‘fields’ => array(
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Primary Key: Unique user ID.’,
            ‘default’ => 0,
          )//aquí introduciríamos el resto de elementos para definir el resto de campos de la tabla
         ),
        ‘primary key’ => array(‘uid’) ,
        ‘indexes’ => array(
          ‘access’ => array(‘access’),
          ‘created’ => array(‘created’),
          ‘mail’ => array(‘mail’),
        )
    );
    En este ejemplo, define 3 índices, sobre los campos access, created y mail. Por otra parte podemos definir índices únicos sobre campos, para ello debemos introducir un elemento más con índice ‘unique keys’ cuyo valor es un array con los nombres del índice y el valor de ese elemento es otro array con los campos que pertenecen a dicho array, en el ejemplo…
    $schema[‘users’] = array(
        ‘description’ => ‘Stores user data.’,
        ‘fields’ => array(
          ‘uid’ => array(
            ‘type’ => ‘int’,
            ‘unsigned’ => TRUE,
            ‘not null’ => TRUE,
            ‘description’ => ‘Primary Key: Unique user ID.’,
            ‘default’ => 0,
          )//aquí introduciríamos el resto de elementos para definir el resto de campos de la tabla

         ),
        ‘primary key’ => array(‘uid’) ,
        ‘indexes’ => array(
          ‘access’ => array(‘access’),
          ‘created’ => array(‘created’),
          ‘mail’ => array(‘mail’),
        ),
        ‘unique keys’ => array(
          ‘name’ => array(‘name’),
        )
    );
    En el ejemplo crea un índice único llamado ‘name’  sobre el campo ‘name’. Dejamos para otro momento el índice ‘foreing keys’ por que tendríamos que explicar el modelo entero de users.

    Debemos pensar que Drupal utilizará este hook para crear las tablas necesarias en la base de datos para el módulo, pero seguiríamos necesitando modificar el hook_install para introducir los datos iniciales en las tablas definidas en el hook_schema, como en el ejemplo…
    // Insert a row for the anonymous user.
      db_insert(‘users’)
        ->fields(array(
          ‘uid’ => 0,
          ‘name’ => ”,
          ‘mail’ => ”,
        ))
        ->execute();
      Debido que al realizar la desinstalación del módulo user no hay que realizar más acciones que el borrado de las tablas generadas con la definición del hook_schema, el módulo user no necesita un hook_uninstall().

    Práctica

    1. Genera un nuevo módulo en base a unos requisitos de base de datos dados.
    2. Define el hook_schema definiendo el modelo de base de datos
    3. Define el hook_install para introducir los datos iniciales en las tablas
    4. Prueba a realizar la instalación e activación del módulo, comprueba si las tablas se han generado correctamente.
    5. prueba a desactivar y desinstalar el módulo y mira a ver si ha borrado correctamente las tablas en la BBDD.

    Referencias

    Licencia Creative Commons