Desde hace un tiempo, cuando tengo que entregar un proyecto que sólo incluye la capa de presentación de una web (archivos estáticos, HTML, CSS y JavaScript), uso un sistema de ensamblado (o renderizado) automático de las plantillas, de manera que pueda tener separados los archivos de estructura (layout) y el contenido (casi siempre carente de marcado). Un poco en la línea de lo que hace cualquier aplicación web como los CMSs. De esta manera es sencillo regenerar las plantillas cada vez que tengo que introducir un cambio de marcado. Para ello, hasta ahora estaba usando un sistema adhoc, que ha ido emergiendo de mis propias necesidades. Hace poco he descubierto que hay sistemas que trabajan directamente con esa filosofía en mente. La de generar los archivos estáticos de un sitio web completo (SSGs). De hecho, viendo como están actuando empresas importantes como 37Signals (creadores de Ruby in Rails) o DevelopmentSeed parece que hay un resurgir de los sitios estáticos. Así que he decidido abandonar mi solución propia y probar algunas de las de código libre que se pueden encontrar por la red.
Aunque para generar mi propio blog uso Octopress (Jekyll), que está implementado con Ruby, estoy investigando soluciones similares, y de hecho más flexibles, implementadas con node.js, una plataforma con la que, de momento, me siento más cómodo que con Ruby.
Tras investigar un poco por la red, he comparado los proyectos más destacados de generadores hechos con nodejs:
BlackSmith: Sistema de blogging en la línea de Jekyll de los maestros de Nodejitsu, que parece que tienen el proyecto un poco descontinuado ya que no hay actividad en el desde hace 3 meses (en el momento de hacer esta evaluación). Por otra parte, aunque tengo ganas de echarle un vistazo en profundidad a FlatironJS, el full stack framework de Nodejitsu, con el que está construido BlackSmith, yo estoy buscando algo más flexible y que no esté tan orientado a un sistema de weblog.
DocPad: Sistema de creación de sitios web completos. Orientado a un tipo de sitio más general en lugar de a un caso concreto como es un blog.
Es el que tiene más estrellas en Github. Esto puede ser porque es e mejor o por que su creador Benjamin Arthur Lupton también es el creador de historyjs que es un proyecto con cierta fama.
Puede actuar como servidor. Algo que pueden hacer casi todos estos sistemas. Aunque está más pensado para desarrollar o crear contenido y probarlo en tiempo real que para desplegar el sitio web, que suele hacerse sólo de los archivos estáticos generados
Escrita en CoffeeScript. En este contexto esto me parece una desventaja (pequeña), ya que, como comenta Jeremy Askenas, creador de coffeeScript, en JavaScript Jabber Podcast, este está pensado para compilarse a JavaScript para ponerse en producción, y por lo tanto no debería ser una dependencia del sistema.
Aunque pinta muy bien tiene algunos detalles que no me han gustado mucho y por los que he descartado de momento este producto:
La documentación es un poco confusa. Mucho rollo “marketiniano” mezclado con la información.
Al instalarlo me he encontrado con una lista de dependencias que parece interminable. Algunas de estas dependencias no se han podido instalar correctamente.
No se si por las dependencias que han fallado o por que el producto (módulo) en npm está roto, pero no he conseguido hacerlo funcionar correctamente.
WinterSmith: Inspirado por BlackSimth (aunque no he investigado que quiere decir con esto el creador).
Proyecto activo
Escrito en CoffeeScript
Templates en jade
Serie de características expuestas en este post que lo hacen atractivo.
Al iniciar un nuevo proyecto con WinterSmith obtenemos un blog con fake content.
Punch: este es el proyecto por el que me he decidido finalmente. Algunas de las razones son:
Proyecto nuevo pero muy activo y con un número de seguidores (en Github) bastante alto, y con rápido crecimiento.
Parece que es básico y no tiene muchos ideas establecidas de antemano. Prefiero tener que añadir que quitar para adaptarlo a mis necesidades.
Parece que tiene un buen sistema de plugins y que hay gente contribuyendo ya algunos.
Está pensado para incorporar diferentes fuentes de contenido. La mayoría de los sistemas que he visto de este tipo dan por sentado que se va a usar el sistema de archivos con alguna convención de nombres. Este permite incluir cualquier fuente en formato JSON.
Templates en Mustache.js.
Relativamente buena documentación y algunos screencasts que sirven para hacerse una idea rápida del potencial.
La experiencia del primer proyecto es más básica que con WinterSmith, pero ese era uno de mis objetivos en la evaluación. Por otra parte el proyecto base es un Tutorial muy original (un punto más a favor de este producto).
Aunque WinterSmith también es una opción interesante, Punch tiene una sintaxis de comandos más cómoda y está escrito en JavaScript, por lo que no tiene a CoffeeScript como dependencia. También parece tener mayor crecimiento en Github.
Hay otros productos similares que no he tenido en cuenta con profundidad por estar más alejados de los objetivos de un SSG como Wheat y Nog (Wheat2) o Calipso.
Recientemente he migrado el blog de sistema de gestión de contenidos de Drupal a Octopress, basado en Jekyll. A parte de permitirme simplificar de manera increíble el sistema, tenía ganas de usar el cambio de dominio (abandonando almadeweb.es) como excusa para probar los sistemas de generación de sitios web estáticos (SSGs).
These systems, sometimes referred to as “static site generators”, pre-process all content, applying templates before publication to generate web pages. Since pre-processing systems do not require a server to apply the templates at request time, they may also exist purely as design-time tools.
Sobre SSGs (Offline Processing CMSs) estoy empezando a investigar a hora y es todo un mundo. De momento he empezado por usar Jekyll que parece tener más aceptación y comunidad que el resto, aunque en principio parece muy orientado a crear blogs y difícil de adaptar a otros usos (hablo desde la poca documentación que he visto hasta el momento y prácticamente nula experiencia). Sin embargo hay muchas opciones en otros lenguajes, sobre todo los dinámicos. A mi me llaman especialmente la atención los que funcionan con nodejs
Aunque Jekyll tiene scripts para migrar desde diferentes sistemas, entre ellos Drupal, yo he preferido hacer la migración de contenido a mano y así aprovechar para familiarizarme con el sistema de gestión de contenido y hacer un poco de limpieza. De modo que lo más complejo de todo el proceso de migración, que he ejecutado siguiendo más o menos esta guía de seomoz para las migraciones, me gustaría resaltar la configuración de Apache para redirigir las páginas del sitio antiguo al nuevo. En el root del dominio antiguo he colocado un archivo .htaccess con la siguiente configuración:
123456789101112
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
RewriteEngine on
Redirect 301 /maquetación-html-para-correos-electrónicos http://alessmascherpa.com/blog/2012/11/22/maquetacion-html-para-correos-electronicos/
Redirect 301 /análisis-de-twitter-bootstrap-especificar-estilos-css-de-manera-recursiva-con-lesscss http://alessmascherpa.com/blog/2012/04/02/twitter-bootstrap-lesscss-css/
Redirect 301 /content/theming-de-un-módulo-propio-en-drupal-6 http://alessmascherpa.com/blog/2011/09/28/theming-modulo-drupal-6/
Redirect 301 /drupal-pecl-uploadprogress http://alessmascherpa.com/blog/2011/02/14/drupal-pecl-uploadprogress/
RewriteRule (.*) http://alessmascherpa.com/ [R=301,L]
Está configuración me permite redirigir algunas páginas específicas trasladadas del domino antiguo al nuevo, y las páginas restantes que puedan tener links externos repartidos por la red y que ahora se dirijan a páginas inexistentes, pero no en el nuevo, las redirige a la raíz del dominio nuevo. Para este paso en concreto me ha sido muy útil esta referencia de linode
Aunque existen intentos de estandarizar como se renderiza un correo electrónico en diferentes gestores de correo, como http://www.email-standards.org/, sigue existiendo el mismo problema que tenemos con los navegadores web. Hay que reducir la tecnología al mínimo común denominador (a seber: el caso más tonto). Lo cual, como siempre, complica el desarrollo y el mantenimiento de las plantillas que usemos para enviar nuestros e-mails con marcado HTML. Aquí apunto algunos detalles sobre este tema que he acumulado tras enfrentarme algunas veces con este problema:
Desarrollo
No usar javascript
Maquetar los grids con tablas (old style) en lugar de usar etiquetas div y estilos float & company
Usar estilos inline (atributos style), sobre todo para los estilos más importantes. Si puede ser para todos los estilos. Esto dificulta el desarrollo y el mantenimiento, y por lo tanto debería automatizarse. Se pueden usar etiquetas style para incluir los estilos menos importantes, yo lo desaconsejo. Gmail, por ejemplo, no lo admite. No usar nunca hojas de estilo externas.
1234567891011121314
// CSS Externo:
<head><linkrel="stylesheet"type="text/css"href="mystyle.css"></head>// CSS Interno:
<style>hr{color:sienna;}p{margin-left:20px;}body{background-image:url("images/back40.gif");}</style>// CSS Inline:
<pstyle="color:sienna;margin-left:20px">This is a paragraph.</p>
Rutas absolutas en atributos href y src. Enlaces e imágenes. La descarga de imágenes, que en algunos gestores de correo (especialmente los basados en web) puede estar condicionada a la decisión del usuario (para evitar ataques). Esto puede usarse para recoger datos estadísticos sobre apertura de emails. También implica tener muy en cuenta el incluir texto en los atributos alt.
No usar css para imágenes background, usar el atributo background en su lugar
Especificar siempre ancho y alto en las imágenes. Hacerlo como atributos de la etiqueta img.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">// o mejor:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Test
Probar el mail en diferentes gestores de correo. Tanto de escritorio como basados en web. Yo lo he probado usando plantillas de emails en Gmail y Apple Mail.
Comprobar que el email se visualiza correctamente sin imágenes, ya que no está garantizado que el usuario pueda verlas.
Usar algún validador de marcado para correo electrónico
Consejos
Limitar el ancho del diseño a 550-600px. Esto permite que se visualice mejor en dispositivos móviles. Envolverlo con una tabla que expanda al 100% que lo centre correctamente y permita controlar el background.
Colocar bien visible el enlace a la visualización del correo en el navegador web, del tipo: “Si no puedes visualizar correctamente… pincha aquí”. De esta manera podemos evitar problemas de visualización y también nos puede ayudar a conseguir estadísticas.
Si el mail es para un newsletter o similar creo que es una norma de etiqueta colocar bien visible (casi siempre se encuentra en el pie) un enlace para dar al usuario la posibilidad de darse de baja del servicio.
Automatizar al máximo el proceso. El caso ideal permitiría, a partir del trabajo que orientamos a la web, marcado moderno y estilos externos, etc., poder generar un archivo con grid estructurado con tablas, estilos inline y rutas absolutas, etc
Hace un tiempo me encontré con una pregunta en Stackoverflow sobre como realizar una iteración para construir los estilos necesarios para construir un grid con css y html, necesidad básica de cualquier layout web. Llegué a ella a través de una búsqueda para encontrar recursos que me ayudasen a crear mi propio framework css. La idea era estudiar o copiar los componentes necesarios de los frameworks más destacables en la actualidad. Sobre porqué crear un framework propio y como hacerlo ya hablaré otro día… si tengo éxito. El objetivo de este artículo es comentar lo que encontré estudiando Twitter Bootsrap, y que dió la casualidad de ser la respuesta a la pregunta que menciono al principio.
El caso es que todo framework css debería hacer uso de un sistema de grid para definir estructuras de páginas (layouts). En general, lo que hacen los diferentes frameworks es definir estructuras para un número determinado de columnas (generalmente 12, 16, etc). Sin embargo no es imposible que te puedas encontrar algún día con la necesidad de implementar (maquetar) diseños que sigán otras estructuras de página. Dejo para otro día la necesidad de discutir si esto es productivo, correcto, inteligente… usar otras estructuras de grid. El caso es que los frameworks tienden a encorsetar el diseño, perdiéndo flexibilidad a favor de productividad en el desarrollo, optimización del front-end… etc. Twitter Bootstrap, en lugar de imponer un número determinado de columnas, permite variar la estructura de grid recompilando el código less adaptando las variables a nuestras necesidades, y de esta manera personalizar el framework. Esta personalización es muy típico verlo con respecto a colorines y opciones menores, pero no tan facil de encontrar con detalles de estructura y menos con la elegancia con la que lo han hecho la gente de twitter. Vale de rollo y pasemos a analizar la solución:
En Twitter Boostrap construyen el grid del framework iterando, de manera recursiva, con un mixin de lesscss en lugar de picar el código para cada estilo que puedan necesitar. De esta manera, no solo es más elegante y comodo, sino que se puede reconstruir cambiando la configuración de la estructura con sólo cambiar las variables de configuración. La parte interesante del código está en el archivo mixins.less, en la carpeta less del framework, debajo del comentario “// The Grid”, en la línea 516:
… que representa el grid y se puede encontrar en bootstrap.css línea 170. Las variables implicadas, @gridColumnWidth o @gridGutterWidth y el resto se pueden ver en el archivo variables.less línea 184.
Aunque se pueden encontrar módulos que aporten únicamente funcionalidad que se use desde otros módulos, y por lo tanto no cuenten con hooks propios de Drupal, lo más habitual es que contengan hook_menu. Este es un buen sitio por donde empezar a construir un módulo. En este caso:
123456789101112131415161718192021
<?php/** * Implementation of hook_menu(). * * Define un enlace para visitar la página donde se muestran los * datos estructurados con las funciones theme. */functionmimodulo_menu(){$items=array();$items['mimodulo-bad']=array('title'=>'D6 bad demo','description'=>'D6 bad demo','page callback'=>'show_data_bad','access callback'=>TRUE,'menu_name'=>'primary-links',);return$items;}?>
En el hook_menu se define la función callback que se encargara de responder cuando se acceda a la ruta definida como clave del array asociativo que define los elementos de menú. Esta función será la encargada de reunir y procesar (si fuese necesario) los datos y darles formato para mostrarlos al usuario. La implementamos de la siguiente manera:
1234567891011121314151617181920212223242526272829
<?php/** * Función Callback que se encarga de mostrar la página con los * datos estructurados con la llamada desde menu. */functionshow_data_bad(){$data=mock_data(9);$output='';$output='<ul>';foreach($dataas$item){$output.='<li>';$output.='<strong>'.t('Image').'</strong>'.': '.'<img src="'.$item['image_path'].'" /><br />';$output.='<strong>'.t('Title').'</strong>'.': '.'<span class="mimodulo-title">'.$item['title'].'</span><br />';$output.='<strong>'.t('Body').'</strong>'.': '.'<span class="mimodulo-body">'.$item['body'].'</span>';$output.='</li>';}$output.='</ul>';return$output;}?>
En este ejemplo hemos separado el origen de datos en una función a parte:
<?php/** * Función que devuelve datos ficticios para hacer pruebas * * @param int $index * Número de elementos a devolver * * @return array() * Cada item contiene [1] imágen, [2] título y [3] cuerpo */functionmock_data($index){global$base_url;$data=array();$dataUnit=array('image_path'=>drupal_get_path('module','mimodulo').'/images/200x150.png','title'=>'ramdom title sin sentido alguno','body'=>'ramdom body sin sentido alguno ramdom body sin sentido alguno'.' ramdom body sin sentido alguno ramdom body sin sentido alguno ramdom body'.' sin sentido alguno ramdom body sin sentido alguno ramdom body sin sentido'.' alguno ramdom body sin sentido alguno ramdom body sin sentido alguno '.'ramdom body sin sentido alguno',);for($i=0;$i<$index;$i++){$data[]=$dataUnit;}return$data;}?>
En este punto ya tendríamos el objetivo cumplido. Mostramos un enlace en menu a una página que se encarga de extraer datos de un origen, puede tratarse de una base de datos, un servicio web, etc., y da formato a estos datos con html para mostrarlos al usuario de manera adecuada. Sin embargo la solución no es facilmente reutilizable. Esto se debe a que la estructura que se le da a los datos esta dentro de la función que responde a la llamada del usuario y por lo tanto para modificarla (adaptarla para su uso en otro sistema) habría que “hackear” el módulo, lo cual no nos interesa en absoluto. Drupal soluciona este problema a través del theme system como veremos a continuación.
Mejoramos la capacidad de extensión o modificación del módulo haciendo uso del theme system de Drupal
El theme system de Drupal nos permite definir funciones y plantillas en nuestros módulos de manera que se pueda conseguir esta separación entre datos y presentación y que a demás dichas funciones y plantillas se puedan sobreescribir en diferentes puntos del sistema para modificarlas sin necesidad de “hackear” los originales. Principalmente esta sobreescritura se realizará en el tema de nuestro sitio web. El theme system se puede usar de diferentes maneras.
Uso de las funciones theme de la api
Lo primero que deberíamos hacer para mejorar la extensibilidad de la capa de presentación de nuestro módulo será investigar si entre las funciones disponibles en la api de Drupal, que son bastantes, existe alguna que cumpla con nuestras necesidades actuales, ya que todas ellas se pueden modificar en el tema.
A modo de ejemplo podríamos usar la función theme_image de la siguiente manera:
1234567
<?php// En lugar de:$output.='<img src="'.$item['image_path'].'" />';// Podemos usar: $output.=theme("image",$item['image_path']);?>
Esto ocurre con todas las llamadas a funciones de la capa de presentación.
Implementar hook_theme, de manera que el sistema sea consciente de que existen las funciones theme.
¡IMPORTANTE!: Antes de probar si funciona o como quedan los cambios (si hemos hecho cambios en hook_theme o en las funciones) hay que vaciar cache. De esta manera se actualiza el registro de temas.
Si ninguna de las funciones disponibles en la API nos sirve, o no nos interesa que dichas funciones se tengan que sobreescribir en el tema, ya que afectaría al resto del sistema, podemos implementar nuestras propias funciones.
Para conseguirlo primero que nada hay que implementar el hook_theme. En el especificaremos el nombre de la función tal y como aparecerá en la llamada a la función theme, sin el “theme_” inicial. Tambien especificaremos los argumentos de la función. El orden en el que aparezcan es importante, y debe ser el mismo que el de los parámetros de la implementación de la función, que si llevará el “theme_” inicial, como veremos en el siguiente punto.
1234567891011121314151617
<?php/** * Implementation of hook_theme(). * * Aquí se definen las funciones theme y si son template o no, * de manera que el sistema conozca su existencia. */functionmimodulo_theme(){$themef=array();$themef['mimodulo_item']=array('arguments'=>array($item=>NULL),);return$themef;}?>
Para conseguirlo primero que nada hay que implementar el hook_theme. En el especificaremos el nombre de la función tal y como aparecerá en la llamada a la función theme, sin el “theme_” inicial. Tambien especificaremos los argumentos de la función. El orden en el que aparezcan es importante, y debe ser el mismo que el de los parámetros de la implementación de la función, que si llevará el “theme_” inicial, como veremos en el siguiente punto.
Implementar la función theme
1234567891011121314151617181920212223
<?php/** * Renderiza un data item de mimodulo. * * @ingroup themeable */functiontheme_mimodulo_item($item){$output='';if(isset($item)){$output.='<strong>'.t('Image').'</strong>'.': '.theme("image",$item['image_path']).'<br />';$output.='<strong>'.t('Title').'</strong>'.': '.'<span class="mimodulo-title">'.$item['title'].'</span><br />';$output.='<strong>'.t('Body').'</strong>'.': '.'<span class="mimodulo-body">'.$item['body'].'</span>';}else{drupal_set_message(t('Empty item in theme_mimodulo_item()'),$type='error',$repeat=FALSE);}return$output;}?>
Que ahora se llamará así en el callback del menú:
123456789101112131415161718
<?php/** * Función Callback que se encarga de mostrar la página con los * datos estructurados con la llamada desde menu. Igual que la de * antes pero con llamadas a función theme para estructurar los datos */functionshow_data_good(){$data=mock_data(9);foreach($dataas$item){$output.='<li>';$output.=theme('mimodulo_item',$item);$output.='</li>';}return'<ul>'.$output.'</ul>';}?>
De esta manera hemos conseguido separar el marcado de los datos a estructurar y podemos sobreescribir la función modificando el marcado para adaptarlo a otras necesidades, como veremos en el siguiente punto.
Modificar la función en el tema
En el caso de queramos sobreescribir un función en un tema podemos hacerlo, en el archivo template.php del tema, de dos maneras, cambiando el “theme_” de la implementación por:
“phptemplate_” + el nombre de la función definido en hook_theme
“nombredeltema_” + el nombre de la función definido en hook_theme. En el caso del ejemplo “garland_”
Cuando llamemos a la función con theme() está buscará primero las que tiene registradas en el tema con “nombredeltema_”, luego “phptemplate_” y por último “theme_”.
<?php/** * Renderiza un data item de mimodulo. * * @ingroup themeable */functiongarland_mimodulo_item($item){$output='';if(isset($item)){$output.='<div class="mimodulo-image-label">'.t('Image').': '.'</div>'.'<div class="mimodulo-image-field">'.theme("image",$item['image_path']).'</div>';$output.='<div class="mimodulo-title-label">'.t('Title').': '.'</div>'.'<div class="mimodulo-title-field">'.$item['title'].'</div>';$output.='<div class="mimodulo-body-label">'.t('Body').': '.'</div>'.'<div class="mimodulo-body-field">'.$item['body'].'</div>';}else{drupal_set_message(t('Empty item in theme_mimodulo_item()'),$type='error',$repeat=FALSE);}return'<div class="mimodulo-item">'.$output.'</div>';}?>
Implementar la función con plantilla tpl.php
Con Drupal también se puede implementar una función theme con una plantilla “.tpl.php”, en lugar de con una función “theme_”. Los pasos a seguir en este caso son:
Actualizar el hook_theme para incluir la nueva función
Incorporamos un nuevo item al array que devuelve el hook_theme.
La función preprocess se encarga de procesar los datos antes de cargarlos en la plantilla, de manera que en esta no haya que ejecutar lógica más alla de bucles, condicionales y impresión de los valores de las variables.
12345678910111213141516
<?php/** * Process variables for mimodulo-view.tpl.php. * * The $variables array contains the following arguments: * - $data * * @see mimodulo-view.tpl.php */functiontemplate_preprocess_mimodulo_view(&$variables){$variables['items']=array();foreach($variables['data']as$item){$variables['items'][]=theme('mimodulo_item',$item);}}?>
Implementar la plantilla
123456789101112131415161718
<?php/** * @file mimodulo-view.tpl.php * Renderiza una lista de items. * * - $items : Ya renderizados en preprocess. * * @see mimodulo_preprocess_mimodulo_view() */?><div class="mimodulo-items-list"> <ul><?phpforeach($itemsas$item):?> <li class="mimodulo-list-item"><?phpprint$item;?></li><?phpendforeach;?> </ul></div>
Si quisieramos que en un tema concreto los datos se visualizasen de otra manera, por ejemplo en forma de grid, habrá que repetir el último paso anterior, y posiblemente el penúltimo también. En nuestro casos repetiremos los dos reescribiendo la plantilla, que guardaremos en la carpeta del nuevo tema y la función preprocess que incluiremos en el archivo template.php del nuevo tema donde se quiera rediseñar la presentación de los datos:
1234567891011121314151617181920212223242526
<?php/** * @file mimodulo-view.tpl.php * Renderiza una lista de items. * * - $items : Ya renderizados en preprocess. * - $gridnum : Num de celdas del grid. * - $length : total de items. * - $rows : total de filas. * * @see mimodulo_preprocess_mimodulo_view() */?><div class="mimodulo-items-list"> <table><?phpfor($i=0;$i<$rows;$i++):?><tr><?phpfor($j=0;$j<$gridnum;$j++):?><tdclass="mimodulo-list-item"><?phpif(($i*$gridnum+$j)<$length){print$items[$i*$gridnum+$j];}?> </td><?phpendfor;?> </tr><?phpendfor;?> </table></div>
1234567891011121314151617181920212223
<?php/** * Process variables for mimodulo-view.tpl.php. * * The $variables array contains the following arguments: * - $data * * @see mimodulo-view.tpl.php */functionbluemarine_preprocess_mimodulo_view(&$variables){$variables['items']=array();$variables['gridnum']=4;$variables['length']=0;foreach($variables['data']as$item){$variables['items'][]=theme('mimodulo_item',$item);$variables['length']++;}$round=floor($variables['length']/$variables['gridnum']);$variables['rows']=($variables['length']%$variables['gridnum'])>0?$round+1:$round;}?>
La manera de nombrar las funciones preprocess y el orden de ejecución que siguen se puede encontrar aquí
Funciones para filtrar la salida de datos
Algo que es importante mencionar es que se deberían “limpiar” siempre los datos que se muestran al usuario. Esto se consigue, principalmente, con 3 funciones alternativas:
check_plain: Comprueba que el contenido que se le pasa contenga únicamente texto plano.
check_markup : Más compleja, permite filtrar de manera selectiva el contenido con los filtros definidos por Drupal de manera que la salida sea texto con marcado HTML que siga las especificaciones del filtro.
filter_xss: Protege contra ataques XSS (crosScripting). Útil con el contenido que entra desde fuentes no controladas.
Generalmente se usan en las funciones theme o en las preprocess.
Añadir archivos CSS y JS específicos del módulo
Aunque se pueden añadir estilos CSS o comportamientos JS en el tema, puede que necesitemos incluir algunos por defecto. Esto se puede llevar a cabo, a demas de escribiendo los archivos CSS y JS con los contenidos adecuados e incluyendolos en el directorio del módulo incluyendolos en el código cuando vayan a resultar necesarios. Esto se hace con dos funciones de la API de Drupal:
drupal_add_js permite incluir archivos JS de nuestro módulo de la siguiente manera:
Estos archivos se incluirán en la cabecera antes que los del tema y por lo tanto los estilos tendrán menos pesos que si los reescribimos en las hojas de estilo del tema.
Como instalar la librería PECL para poder mostrar el progreso de subida de un archivo. Estos pasos los he probado en Ubuntu 10.04 pero “deberían” funcionar en cualquier distribución UNIX (Linux/MacOSX). En Windows supongo que, a parte de modificar algún paso, en lugar de uploadprogress.so habría que especificar uploadprogress.dll. Esto último no lo he comprobado, pero es lo que dice la teoría. Se espera que todos los pasos aquí especificados se ejecuten desde una terminal y con accesso como root del sistema, aunque algunos no lo precisen.
Nos colocamos dentro del directorio descomprimido (cd) para luego ejecutar los siguientes comandos:
$ phpize
$ ./configure
$ make
$ sudo make install
Comprobar que el directorio para las extensiones es el correcto. La ruta la devuelbe la ejecución del último comando: make install command En mi caso la ruta es: /usr/lib/php/extensions/no-debug-non-zts-20060613/.
Abrir php.ini (con vim, vi, nano, o el editor que sea) y editar la directiva extension_dir, descomentandola si es necesario y sustintuyendo su valor por la ruta anterior.
$ vim php.ini
Añadir la extensión a php.ini añadiendole la línea extension=uploadprogress.so
Reiniciar apache
$ sudo /etc/init.d/apache2 restart
Si todo ha ido bien a partir de ahora veremos la librería activa en el informe de estado:
Y podemos especificar barra con medidor de progreso (progress bar) cuando creemos un campo de tipo archivo (file field) en lugar de Throbber:
De esta manera conseguimos instalar la librería y hacer que se puedan visualizar el progreso de subida del archivo.
NOTAS:
Funciona perfectamente en Drupal 6, pero en Drupal 7, aunque en el informe de estado reconoce que la librería está correctamente instalada, no funciona correctamente, a fecha del 15/02/2011. Supongo que esto último no tardará en solucionarse.
Parece que sólo funciona con una instlación estandar (PHP corriendo en Apache con mod_php) y no funciona si se utiliza FastCGI. Más información sobre este tema aquí.