Translate to

Buscar

17 de diciembre de 2010

Cómo personalizar la página site-offline

La página site-offline se muestra cuando ponemos el sitio en mantenimiento (admin/settings/site-maintenance), o cuando Drupal no puede conectarse a la base de datos.

Aquí me refiero al segundo caso.

Para probar esta característica, puede apagar el servidor de base de datos.

También puede cambiar temporalmente el archivo sites/default/settings.php poniendo parámetros erroneos de conexión. Por ejemplo:

sites/default/settings.php
...
#$db_url = 'mysqli://mydatabase:clave@localhost/mydatabase';
$db_url = 'mysqli://mydatabase:clave_erronea@localhost/mydatabase';
...

1
En sites/default/settings.php, indicar el nombre del tema que contendrá a maintenance-page-offline.tpl.php, la página personalizada.

Hay unas líneas que se pueden descomentar para eso, pero quizás lo más sencillo sea agregar las que necesitamos al final. Por ejemplo, usaré el tema minnelli, que viene por default con Drupal:

sites/default/settings.php
...
$conf['maintenance_theme'] = 'minnelli';
...

2
En el directorio del tema, crear, si no existe, el archivo maintenance-page-offline.tpl.php.
Esto se puede hacer copiando el archivo page.tpl.php del tema y renombrandolo.
O también, copiando el archivo maintenance-page.tpl.php del tema y renombrándolo.
En este caso, uso la segunda opcion.

3
Personalizar maintenance-page-offline.tpl.php.
Como se recuerda, esta página aparece cuando no hay acceso a la base de datos, así que hay que colocar algunas cosas en hardcode.
Por ejemplo, redefinir el valor de algunas variables y reemplazar el $content:

maintenance-page-offline.tpl.php
...
$site_name = 'My Drupal Site';
$logo = FALSE;
...
<div class="clear-block">
  <!--?php print $content ?-->
  <p>
    En este momento el site no está disponible debido a problemas técnicos.
    Por favor intente de nuevo más tarde.
    Gracias por su comprensión.
  </p>
  <hr />
  <small>
    Si es el administrador de este site, por favor verifique el acceso a la base de datos.
    Para más ayuda, puede revisar el <a href="http://drupal.org/node/258">manual</a>.
  </small>
</div>
...

Referencias

15 de diciembre de 2010

Debug con Xdebug y Aptana (y Notepad++)

Aunque quizás llevo bastante tiempo desarrollando aplicaciones web, no me había preocupado mucho por encontrar un modo de hacer depuración línea por línea (como lo hacía cuando estudiaba Turbo Pascal... cuantos años ya).

No creía que se podría hacer con comodidad. Suponía que el procedimiento era demasiado técnico o se requería de un IDE pesado o costoso.

Por ejemplo, en Java, no llegué a hacerlo. Ni siquiera con las aplicaciones no web. Quizás porque fueron muy pocas o muy pequeñas como para sentir la necesidad.

Con PHP, he sentido muchas veces la necesidad, pero hasta ahora había podido sobrevivir poniendo echo y print_r.

Pero ahora, trabajando con Drupal, aunque el código es relativamente claro, hay varios paradigmas de organización conviviendo y con frecuencia no sé por donde entrar. Hay documentación, pero muchas veces siento que sólo parece aumentar las sombras del bosque. Usar la técnica de echo y print_r es util, como dejar migajas en el camino, pero puede ser algo tedioso para una ruta larga o compleja.

Entonces me animé por encontrar un modo mejor de depurar PHP. Quizás ya era la hora. Encontré que se puede hacer usando Xdebug y Aptana (un IDE derivado de Eclipse). Incluso se puede usar Notepad++ (mi editor de textos favorito).

Base
Una PC con Windows 7, XAMPP 1.7.1 (PHP 5.2).
También lo pude hacer en una PC con Linux Centos 5, Apache 2 y PHP 5.2.

Xdebug
Xdebug es una extensión que hace que Apache genere información para la depuración.
La instalación depende de la plataforma (en Windows, las extensiones de Apache son .dll y, en Linux, son .so, por ejemplo).
Puede ubicar el dll directamente en http://xdebug.org/download.php, o usar el wizard http://xdebug.org/wizard.php (sin embargo ya no soporta PHP compilado con MSVC6). Allí aparece una caja de texto donde se puede pegar la selección copiada (Edit, Paste) de un phpinfo generado por el PHP que se tenga instalado.
Una pagina phpinfo es fácil de hacer. Simplemente guarde en el directorio web un archivo phpinfo.php con el siguiente contenido:
<?php phpinfo(); ?>
y luego acceda a http://localhost/phpinfo.php. Debe aparecer una página larga con muchas tablas pintadas de azul. El find-binary de Xdebug no requiere el código fuente de la página, sino que simplemente la seleccione toda (Edit, Select all, Copy).
Copiado el texto copiado del phpinfo en la caja, luego de pulsar el botón Analyze my phpinfo output() aparecerá un conjunto de instrucciones que hay que seguir para instalar Xdebug. Sígalas.

Por ejemplo, para mi caso en Windows, requerí bajar php_xdebug-2.1.0-5.2-vc6.dll que copié en el directorio php/ext/. Luego modifiqué php/php.ini:

[Zend]
;zend_extension_ts = "C:\bin\dev\xampp_171\php\zendOptimizer\lib\ZendExtensionManager.dll"
;zend_extension_manager.optimizer_ts = "C:\bin\dev\xampp_171\php\zendOptimizer\lib\Optimizer"
;zend_optimizer.enable_loader = 0
;zend_optimizer.optimization_level=15
;zend_optimizer.license_path =
; Local Variables:
; tab-width: 4
; End:

[XDebug]
;; Only Zend OR (!) XDebug
;zend_extension_ts="C:\bin\dev\xampp_171\php\ext\php_xdebug.dll"
zend_extension = "C:\bin\dev\xampp_171\php\ext\php_xdebug-2.1.0-5.2-vc6.dll"
xdebug.remote_enable=true
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.profiler_enable=1
xdebug.profiler_output_dir="C:\bin\dev\xampp_171\tmp"

Luego de reiniciar Apache, si se reconoce a la extensión Xdebug, al correr el phpinfo se podrá observar algo como:


Aptana
En Aptana instalé el soporte para PHP (PDT y PHP Debugger).
En Windows, Preferences, PHP, Debug, elegí PHP Debugger: XDebug, Server (localhost), PHP Executable.


Para probar el depurador, en el directorio web creé un archivo test/test.php, con algún contenido simple como:

<?php
$a = 10;
for ($i=0; $i<$a; $i++) {
  echo $i;
}
?>

Luego, cree un nuevo proyecto PHP a partir del directorio test/. Luego seleccioné el archivo test.php e hice click en Debug as PHP Web Page. La depuración se detiene en la primera línea. Luego puede ir haciendo F5 para ir avanzando, o tambié F6. Podrá encontrar más opciones de depuración bajo el menú Run.


Notepad++
Descargué el plugin DBGP de http://sourceforge.net/projects/npp-plugins/files/DBGP%20Plugin/, extraje el dll (dbgpPlugin.dll) y lo coloqué en el directorio plugins.
Al iniciar nuevamente Notepad++, encontré el menú Plugins/DBGp.
Entré a Plugins/DBGp/Config e inquiqué los parámetros para el cliente DBGp.


Luego, en el menú del plugin, inicié el debugger.
Finalmente, entré a la dirección http://localhost/test/test.php?XDEBUG_SESSION_START=test. Noté que el botón de Notepad++ parpadeaba.



Ahora, a aprender a depurar módulos :-) Ojalá esta información le sirva de ayuda.

Referencias

1 de diciembre de 2010

Cómo quitar el historial del perfil del usuario

En el perfil del usuario (el mostrado en el url user) aparece "Historial, Miembro desde...".

A veces se desea que no aparezca esa información.

Ocultarlo
Una forma de hacerlo es ocultarlo. Por ejemplo localizando el elemento y aplicándole CSS para eso:

En algún .css del tema:
.profile h3,
.profile .user-member {
  display: none;
}

Quitarlo
Otra forma de hacerlo es quitarlo, que ni siquiera se genere el HTML.

Con el template
Una forma de quitarlo es copiando al tema el template /modules/user/user-profile-category.tpl.php y agregar una línea que prevenga la presentación. Por ejemplo:

themes/ttt/user-profile-category.tpl.php
<?php if ($title == t('History')) return; ?>
<?php if ($title) : ?>
  <h3><?php print $title; ?></h3>
<?php endif; ?>

<dl<?php print $attributes; ?>>
  <?php print $profile_items; ?>
</dl>

Con una función del template
Otra forma de quitarlo es modificando las variables de template en el archivo template.php del tema. Por ejemplo, para el tema ttt:

themes/ttt/template.php
function ttt_preprocess_user_profile(&$vars) {
  unset($vars['profile']['summary']);
  $vars['user_profile'] = implode($vars['profile']);
}

Con hook_user
Otra forma de quitarlo es usando el hook_user. Por ejemplo, para el módulo mmm:

function mymodule_user($type, &$edit, &$account, $category = NULL) {
  if ($type == 'view') {
    unset($account->content['summary']);
  }
}


Referencias

7 de noviembre de 2010

Un pr() para Drupal

pr() es un comando que suelo usar mucho en CakePHP para revisar el valor de una variable.

En Drupal, hay comandos como dsm() pero, a veces, lo que requiero es algo tan simple como lo que hace pr().

Al no encontrar un comando igual de práctico, que funcionara tanto en los templates como en los módulos, y sin instalar ningún módulo extra, uso una solución rápida que consiste en colocar el siguiente código al final del archivo sites/default/settings.php:

function pr($x, $return=TRUE, $set_message=TRUE) {
  $output = '<xmp>';
  $output .= print_r($x, TRUE);
  $output .= '</xmp>';
  if ($set_message) {
    drupal_set_message($output);
  }
  if ($return) {
    return $output;
  } else {
    echo $output;
  }
}

$return=TRUE permite asignar el valor de salida a una variable. $set_message permite mostrarlo con drupal_set_message.

6 de noviembre de 2010

Un año después

HACE COMO un año empecé a aprender Drupal, animado por las cosas buenas que escuchaba de él. Como que fue elegido para el portal de la Casa Blanca. Luego me enteré que además era usado por El Comercio, los Grammys, MTV, y en los portales de muchos artistas famosos.

Inspirado por esos ejemplos, empecé consultando algunos libros y tratando de hacer sites.

Tal vez soy un poco cabeza dura. Tal vez me cuesta algunas cosas más que a la mayoría. Recuerdo que tarde meses en comprender MVC. Ya va siendo un año y recién empiezo a sentir atisbos de comprensión en Drupal.

No es que no pueda hacer cosas con él. Puedo usarlo, configurarlo, definir tipos, hacer vistas, programar módulos, personalizar plantillas... pero el hecho de poder hacer algo no es lo mismo que comprenderlo.

Me gusta Drupal. Es realmente muy interesante. Cuando trabajo con él, me siento asombrado de la comodidad con que permite hacer algunas cosas que normalmente son complicadas, como la administración del sitio y la colaboración entre componentes. Pero también me siento frustrado por la dificultad que aparece para otras cosas que normalmente son sencillas, como la generación de html, el manejo de formularios, la aplicación de javascript.

Quizás me falte conectarme más con la comunidad, con otras personas que también se enfrenten al reto de aprender Drupal, para que el camino sea más llevadero. Quizás sea simplemente una fase y luego todo me parezca más claro. Mientras tanto, entiendo que tengo que leer mucho, bucear en la documentación para resolver los casos que quiero (sería genial una que hubiera una biblioteca de casos fácil de consultar), experimentar. Aceptar que hay cosas que tengo que volver a aprender, a la manera Drupal.

7 de septiembre de 2010

Hello SimpleTest

Este es un ejemplo trivial donde uso SimpleTest para comprobar el módulo HelloWorld.

Entorno
Procedimiento

Tengo creado el módulo HelloWorld:

helloworld.info
; $Id$
name = Hello World
description = Hello World simple module
core = 6.x
package = Hello

helloworld.module
<?php
// $Id$
/**
 * Implementation of hook_menu()
 */
function helloworld_menu() {
  $items = array();
  
  $items['helloworld'] = array(
    'title' => 'Hello World',
    'page callback' => 'helloworld_page',
    'access arguments' => array('access helloworld content'),
    'type' => MENU_NORMAL_ITEM,
  );
  
  return $items;
}

/**
 * Return content
 */
function helloworld_page() {
  $greeting = variable_get('helloworld_greeting', 'Hello World!');
  
  $content = '<div id="helloworld_block"><div class="greeting">' .
    $greeting . '</div></div>';
    
  return $content;
}

helloworld.test
<?php
// $Id$
class HelloWorldTestCase extends DrupalWebTestCase {
  protected $privileged_user;
  
  public static function getInfo() {
    return array(
      'name' => 'Hello World',
      'description' => 'Hello World simple module.',
      'group' => 'Hello',
    );
  }
  
  public function setUp() {
    parent::setUp('helloworld');
  }
  
  public function testHelloPage() {
    $this->drupalGet('helloworld');
    $this->assertText('Hello World!', 'Greeting in Page');
  }
  • La intención de la prueba es acceder a la dirección helloworld y comprobar que aparezca el texto 'Hello World!'.
  • La clase HelloWorldTestCase extiende a DrupalWebTestCase, que permite imitar una navegación.
  • getInfo() hace que el case sea accesible desde la interface de SimpleTest.
  • setUp() permite definir el entorno inicial para las prueba. Por ejemplo hay órdenes para crear usuarios y simular logins. En este caso, no se hace nada de eso, sólo lo mínimo.
  • El nombre del método de la prueba comienza con test, luego el nombre de la prueba.
  • drupalGet(url) permite simular un acceso a la página de url especificado.
  • assertText() comprueba si el el texto especificado está presente en la página abierta. Sólo el texto; el HTML es ignorado. Opcionalmente se puede asociar un mensaje a la prueba.
  • En tutoriales como http://www.lullabot.com/articles/drupal-module-developer-guide-simpletest aparece un paso donde se implementa hook_simpletest(). Sin embargo, en el tutorial oficial http://drupal.org/node/890654 no aparece este paso. Yo tampoco lo he puesto aquí. No sé si será necesario en otros casos.
    Tampoco fué necesario que creara un directorio test. Coloqué helloworld.test en el mismo directorio que helloworld.module.
Para correr la prueba se usa el administrador de Drupal, Site Building, Testing, Hello (el nombre del grupo de pruebas que especifiqué con getInfo()) y HelloWorld (el nombre del case).


En este caso aparecerá un fail en la línea del assertText. Tambiém puede comprobar manualmente que al intentar acceder a la página helloworld aparece un mensaje de acceso denegado.

Es porque en la definición de la página he indicado privilegios de acceso que no existen. Para corregir esto:

function helloworld_menu() {
  $items = array();
  
  $items['helloworld'] = array(
    'title' => 'Hello World',
    'page callback' => 'helloworld_page',
    'access arguments' => array('access content'),
    'type' => MENU_NORMAL_ITEM,
  );
  
  return $items;
}


Como he modificado el módulo, limpio el cache antes de volver a correr la prueba. Aparecerá todo OK.

Enlaces interesantes

6 de septiembre de 2010

Instalando SimpleTest

SimpleTest es un módulo que permite usar unit testing (test unitarios) en la programación de Drupal.

Entorno
Ingredientes
Procedimiento
  1. drush dl simpletest
  2. Antes de activar el módulo, revisé el archivo INSTALL.txt.
    Allí, indican que el primer paso es aplicar el parche D6-core-simpletest.patch a Drupal core.
  3. Para aplicar el parche, uso Aptana Studio.
    Como el directorio del site aún no era un proyecto de Aptana, lo ubico en la pestaña File, click secundario, Promote to Project...
    Una vez que el site ya figura bajo Projects, en la misma pestaña File, click secundario, Team, Apply Patch...
    Luego indico aplicar modules/simpletest/D6-core-simpletest.patch en el site.
  4. Usando el administrador de Drupal, indico activar el módulo SimpleTest.
    Obtengo un mensaje que dice que no se puede activar el módulo porque el php no tiene la biblioteca curl (uno de los requisitos).
  5. Edito el archivo de configuración de php (xampp/php/php.ini) y allí descomento la línea que declara curl como extensión:
    extension=php_curl.dll
    Luego reinicio el servidor apache (usando el panel de control de xampp).
  6. Vuelvo a intentar activar el módulo SimpleTest, esta vez con éxito.
    Ahora, en el administrador de Drupal, Site Building, aparece la nueva opción Testing.

11 de agosto de 2010

Actualizando el Drupal Core con Drush

Supongamos que mydrupal es un site drupal-6.17, cuyo core deseo actualizar a drupal-6.19.

El paso cero, es hace un backup adecuado del site. Eso significa parquear la aplicación (Administer, Site configuration, Site maintenance, Site status: Off-line), limpiar el caché (Administer, Site configuration, Performance, Clear cached data; ó drush cc all), hacer un backup del directorio mydrupal, y hacer un backup de la base de datos usada por mydrupal.

Luego, damos el primer paso :-)

La idea es sobrescribir mydrupal con el nuevo drupal y luego correr el script de actualización.

Una forma es bajando drupal-6.19.tar.gz, descomprimiéndolo, y luego copiando o moviendo sus subdirectorios dentro de mydrupal. Hecho eso, ejecutar el script de actualización, por ejemplo entrando a http://localhost/mydrupal/update.php

Pero, ¿cómo hacerlo con drush?. En la ayuda ví que hay una opción sup, pero no la llegué a entender, sorry.

Pero encontré este post, donde usan un truco que sí entendí.

Como se puede usar drush para descargar la versión de drupal que queramos, por ejemplo drupal-6.19, entonces, si antes de hacer la descarga hacemos un enlace simbólico que conduzca drupal-6.19 a mydrupal, los archivos descargados lo sobrescribirán.

Para crear el enlace simbólico (en el directorio que contiene a mydrupal):

ln -s mydrupal drupal-6.19

En Windows Vista/7 sería:

mklink /J drupal-6.19 mydrupal

Si usa Windows XP, será necesario descargar la utilidad Junction y usar:

junction drupal-6.19 mydrupal

Para descargar drupal-6.19:

drush dl drupal-6.19

Para eliminar el enlace simbólico:

rm drupal-6.19

En Windows Vista/7:

rd drupal-6.19

En Windows XP:

junction -d drupal-6.19
Para correr el script de actualización:

cd mydrupal
drush updb

Listo. A continuación se puede volver a colocar el site en línea (Administer, Site configuration, Site maintenance, Site status: Online).

Si todo resultó bien, después de un tiempo puede borrar el backup que hizo en el paso cero.

Un problema que yo tuve fue que se sobrescribió el .htaccess que yo había modificado. En ese caso, se puede buscar ese archivo en el backup y restaurarlo.

Resolviendo Drupal multisite

Aunque la instalación y configuración inicial de Drupal es relativamente sencilla y directa, configurarlo para que varios sites compartan el mismo motor fue, para mí, casi un dolor de cabeza...

Continúe leyendo aquí.

8 de agosto de 2010

Instalando Drupal en un directorio con alias

Normalmente, los sites drupal que creo en mi XAMPP (1.7.1, bajo Windows 7) reconocen que pueden habilitar la opción de URL limpios.

Sin embargo, no fue ese el caso cuando creé un site drupal en un directorio con alias.

Cree el directorio con alias dropbox, indicando en el httpd.conf de apache la siguiente configuración:


...
<IfModule alias_module>
  ...
  Alias /dropbox "C:/Users/Rulo/Documents/My Dropbox/htdocs"
  ...
</IfModule>

<Directory "C:/Users/Rulo/Documents/My Dropbox/htdocs">
    Options Indexes FollowSymLinks Includes ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>
...



De ese modo, puedo navegar por http://localhost/dropbox.
Llevo usando Dropbox unas semanas y me gusta mucho cómo me permite manejar mis archivos. Configurando apache de esta forma en la casa y también en el trabajo, puedo continuar cómodamente el proyecto sin preocuparme mucho en los detalles de la sincronización, que Dropbox maneja automáticamente. Antes trabajaba usando un site online, pero la edición via FTP no es tan cómoda. Con Dropbox edito y pruebo localmente, con la confianza que encontraré lo mismo tanto en la casa como en el trabajo.
Pero, al crear un site Drupal en este directorio con alias, no aparecía habilitada la opción de usar URL limpios.

Después de un rato, encontré que, en algunos casos, Drupal puede requerir una configuración extra para esto. La solución fue editar el .htaccess del site:

  ...
  # Modify the RewriteBase if you are using Drupal in a 
  # subdirectory or in a
  # VirtualDocumentRoot and the rewrite rules are not working 

  # properly.
  # For example if your site is at http://example.com/drupal 
  # uncomment and
  # modify the following line:
  # RewriteBase /drupal
  RewriteBase /dropbox/mydrupal
  ...

1 de agosto de 2010

Drupal como framework genérico

El sábado 24 de Julio asistí al Drupal Camp de Invierno que organizó Drupal Peru.

Fue mi segunda asistencia a este tipo de evento. La primera fue en Mayo, para el Drupal Camp de Verano.

Llevo aprendiendo Drupal desde noviembre del 2009; aproximádamente 9 meses. Al usar Drupal como CMS he podido comprobar que algunas cosas que uno supone difíciles resultan fáciles (como la administracion de usuarios). Pero otras cosas resultan más difíciles de lo que uno espera (como el manejo de formularios). Pienso que es porque el framework recién está saliendo a flote y a veces hay que bucear un poco para encontrar las cosas que uno quiere ver.

En la época del Drupal Camp de Verano estaba un poco desorientado en el tema de usar Drupal como un framework genérico. En algún lugar había leído insinuar esa posibilidad, pero no había logrado encontrar pistas claras al respecto. En este evento oí que, de hecho, se usaba de ese modo en el diario El Comercio. Eso me alentó en la tarea.

En este Camp de Invierno, en lugar de sólo escuchar las exposiciones, decidí preguntar por el tema de parchar el código, que venían promocionando desde hacía algunas semanas en la lista. Eso me condujo a un ambiente aparte donde nos explicaron el procedimiento para bajar código, corregirlo y enviar propuestas. De pasada, observé el manejo rutinario de algunas herramientas como el control de versiones, lo cual es también bastante inspirador.

Escuché que hay planes para que Drupal 8 sea un framework genérico. Eso me aclaró un poco el panorama de lo que está sucediendo respecto Drupal.

Me parece que Drupal empezó como un CMS más entre otras alternativas como Joomla y PHPNuke. Pero la forma en que Drupal implementa la modularidad es muy especial y, con el tiempo, aparecieron módulos que demostraron las virtudes de su arquitectura. Otros CMS pueden verse mejor, o ser más amigables, pero Drupal tiene esa cualidad especial que lo conduce a un futuro diferente. La comunidad ha notado el framework que está debajo del CMS. Ya en Drupal 6, parece que el papel del CMS está siendo poco a poco opacado por el de las virtudes de su framework. Interesante.

5 de julio de 2010

Numberguess en Drupal

HAY ALGUNOS ejecicios básicos que me gusta practicar cuando aprendo un framework. Aunque los tutoriales hagan otros, más reales o útiles, estos me siguen pareciendo un buen punto de partida.

El primer ejercicio es Hola Mundo. Hacer aparecer el tradicional saludo "¡Hola Mundo!".

El siguiente ejercicio es Hola Tú. Recojer un nombre, por ejemplo "Antonio" y hacer aparecer el saludo "¡Hola Antonio!".

Luego está Numberguess, un poco más exigente en el manejo de formularios.

Hola Mundo

En Drupal no es obvio cómo hacer el Hola Mundo. Pero luego de un tiempo experimentando con el Drupal básico, uno puede encontrar que es posible definir un bloque y hacer aparecer el mensaje allí.

Pero eso es en un bloque, ¿y si quisiera que fuera en una página, con un url propio?

Para ese caso, se define un módulo. Eso ya es entrar en el terreno del Drupal intermedio. Hay que aprender el protocolo para definir módulos. Y, principalmente, lo que son los hooks.

El código a continuación juega tambien con los hooks de permiso, administración y bloques, por eso tiene más funciones que las necesarias para mostrar simplemente el mensaje.

helloworld.info
; $Id$
name = HelloWorld
description = Say Hello World!
core = 6.x
package = Hello

helloworld.module
<?php
// $Id$

/**
 * Implementation of hook_help
 */
function helloworld_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/help#helloworld':
      $output = '<p>' . t('Say Hello World!') . '</p>';
      break;
  }
  return $output;
}

/**
 * Implementation of hook_perm()
 */
function helloworld_perm() {
  return array('access helloworld content');
}

/**
 * Implementation of hook_block()
 */
function helloworld_block($op='list', $delta=0, $edit=array()) {
  $block = array();
 
  if ($op=='list') {
   
    $block[0]['info'] = t('HelloWorld');

  }
  elseif ($op=='view') {
   
    $block['subject'] = t('HelloWorld');   
    $block['content'] = _helloworld_content();
   
  }

  return $block;
}

/**
 * Implementation of hook_menu()
 */
function helloworld_menu() {
  $items = array();
 
  $items['helloworld'] = array(
    'title' => 'Hello World',
    'page callback' => 'helloworld_page',
    'access arguments' => array('access helloworld content'),
    'type' => MENU_NORMAL_ITEM,
  );
 
  $items['admin/settings/helloworld'] = array(
    'title' => 'HelloWorld',
    'description' => t('Configuración del módulo HelloWorld'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('helloworld_admin'),
    'access arguments' => array('access administration pages'),
    'type' => MENU_NORMAL_ITEM,
  );
 
  return $items;
}

/**
 * For page content
 */
function helloworld_page() {
  return _helloworld_content();
}

/**
 * Admin setting form
 */
function helloworld_admin() {
  $form = array();

  $form['helloworld_greeting'] = array(
    '#type' => 'textfield',
    '#title' => t('Saludo'),
    '#default_value' => variable_get('helloworld_greeting', 'Hello World!'),
    '#size' => 60,
    '#maxlength' => 60,
    '#description' => t("Saludo que se mostrará en el bloque."),
    '#required' => TRUE,
  );

  return system_settings_form($form);
}

// PRIVATE

/**
 * Return content
 */
function _helloworld_content() {
  $greeting = variable_get('helloworld_greeting', 'Hello World!');
 
  $content = '<div id="helloworld_block"><div class="greeting">' .
    $greeting . '</div></div>';
   
  return $content;
}

Hola Tú

Luego de hacer el módulo para Hola Mundo, avanzar al caso Hola Tú resultó más difícil de lo que pensaba. Tuve antes que estudiar algo del Form API (FAPI) de Drupal.

helloyou.info
; $Id;
name = Hello You
description = Say Hello to you
core = 6.x
package = Hello

helloyou.module
<?php
// $Id$

/**
 * @file
 * Hello for name
 */

/**
 * Implements hook_menu().
 */
function helloyou_menu() {
  $items = array();
 
  $items['helloyou'] = array(
    'title' => 'Hello You',
    'description' => 'Hello You form',
    'page callback' => 'helloyou_page',
    'access arguments' => array('access content'),
  );
 
  return $items;
}

/**
 * Menu callback
 */
function helloyou_page() {
    $output = t('What is your name?');
    $output .= drupal_get_form('helloyou_nameform');
    return $output;
}

/**
 * Define the form
 */
function helloyou_nameform(&$form_state = NULL) {
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
        '#default_value' => isset($form_state['storage']['name'])?$form_state['storage']['name']:'',
    '#description' => t('Please enter your name'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

/**
 * Validate
 */
function helloyou_nameform_validate($form, &$form_state) {
    if ( trim($form_state['values']['name']) == '' ) {
        drupal_set_message(t('Are you anonymous?'));
        $form_state['storage']['name'] = 'Anonymous';
        $form_state['rebuild'] = TRUE;
    }
    else {
        unset($form_state['storage']['name']);
    }
}

/**
 * Submit
 */
function helloyou_nameform_submit($form, &$form_state) {
    $name = $form_state['values']['name'];
    drupal_set_message(t('Hello %name!', array('%name'=>$name)));
}

Numberguess

El siguiente ejercicio fué hacer Numberguess. Es un juego en donde la computadora genera un número entre 1 y 100 y el jugador trata de adivinarlo. A cada intento la computadora dice 'muy alto' o 'muy abajo', hasta que eventualmente se acierta. La idea es hacerlo en el menor número de intentos.

Resolver Numberguess me ha ayudado a entender varias cosas del FAPI. Ojalá este artículo pueda servir de ayuda a alguien.

numberguess.info
; $Id$
name = Numberguess
description = Numberguess game
core = 6.x
package = Hello

numberguess.module
<?php

/**
 * @file
 * Numberguess game
 */

/**
 * Implementation of hook_menu()
 */
function numberguess_menu() {
    $items = array();
  
    $items['numberguess'] = array(
        'title' => 'Numberguess',
        'description' => 'Numberguess game',
        'page callback' => 'drupal_get_form',
        'page arguments' => array('numberguess_multiform'),
        'type' => MENU_NORMAL_ITEM,
        'access arguments' => array('access content'),
    );
  
    return $items;
}

function numberguess_multiform(&$form_state = NULL) {
    $form = array();
    // if win, not form
    if (isset($form_state['storage']['win'])) {
        return $form;
    }
    // if guess ko
    if (isset($form_state['storage']['guess'])) {
        $n = (int)$form_state['storage']['n'] + 1;
        $x = (int)$form_state['storage']['x'];
        $guess = (int)$form_state['storage']['guess'];
        if ($guess < $x) {
            $description = 'Greater than ' . $guess;
        }
        else {
            $description = 'Lower than ' . $guess;
        }
    }
    // if validation error
    elseif (isset($form_state['post']['guess'])) {
        $n = (int)$form_state['post']['n'];
        $x = (int)$form_state['post']['x'];
    }
    // 1st time
    else {
        $n = 1;
        $x = rand(1, 100);
    }
  
    // the base form
    $form['help'] = array(
        '#value' => 'What integer between 1 and 100 I think?',
    );
    $form['x'] = array(
        '#type' => 'hidden',
    );
    $form['n'] = array(
        '#type' => 'hidden',
    );
    $form['guess'] = array(
        '#type' => 'textfield',
    );
    $form['submit'] = array(
        '#type' => 'submit',
        '#value' => 'submit',
    );
  
    // the variable parts
    $form['n']['#value'] = $n;
    $form['x']['#value'] = $x;
    $form['guess']['#title'] = 'Guess ' . $n;
    $form['guess']['#description'] = $description;
  
    return $form;
}

function numberguess_multiform_validate($form, &$form_state) {
    $guess = $form_state['values']['guess'];
    if (!is_numeric($guess)) {
        form_set_error('guess', t('Must be a number'));
    }
    elseif ((int)$guess != $guess) {
        form_set_error('guess', t('Must be an integer'));
    }
    elseif ($guess < 1 || $guess > 100) {
        form_set_error('guess', t('Must be between 1 and 100'));
    }
}

function numberguess_multiform_submit($form, &$form_state) {
    $x = (int)$form_state['values']['x'];
    $n = (int)$form_state['values']['n'];
    $guess = (int)$form_state['values']['guess'];
    $form_state['storage']['x'] = $x;
    $form_state['storage']['n'] = $n;
    $form_state['storage']['guess'] = $guess;
  
    if ($guess < $x) {
        drupal_set_message(t('Too lower, try again'));
        return;
    }
    elseif ($guess > $x) {
        drupal_set_message(t('Too greater, try again'));
        return;
    }
    else {
        drupal_set_message(t('Congratulations. You win after ' . $n . ' times'));
        $form_state['storage']['win'] = 1;
        return;
    }
}



Extra
Puede leer un poco más sobre el mapeo de acciones en: http://www.scribd.com/doc/35307481/Programando-con-Drupal-Mapeo-de-acciones

11 de mayo de 2010

Mostrando imágenes grandes con Zoomify

Imagine que necesita mostrar una imagen muy grande en su página web. Una imagen jpeg de 640x480 mide alrededor de 100 KB y puede tardar un par de segundos en cargar en una conexión típica. Una imagen de 2000x3000 mide más de 1 MB y tardaría como un minuto.

Zoomify permite navegar sobre una imagen muy grande usando un componente flash que carga sólo las imágenes necesarias para mostrar cierta porción de ella. Es posible desplazarse, acercarse y alejarse.

Puede ver un ejemplo de uso directo de Zoomify aquí.

Instalación
Descargué ZoomifyExpress (disponible gratuitamente tanto para Windows como para Mac... pero anímese, más abajo describo cómo se puede usar con Drupal en Linux), y lo descomprimí. La carpeta resultante, ZoomifyExpress4, se puede colocar en cualquier lugar de su disco; no necesita instalación. Contiene tanto el visor flash ZoomifyViewer.swf, como el preprocesador de imágenes Zoomify Converter.exe, usado para generar los mosaicos que serán cargados por el visor según se requiera.

Uso
Al arrastrar el icono de la imagen sobre Zoomify Converter.exe, se creará, en el directorio que contiene los mosaicos de la imagen, una carpeta del mismo nombre, conteniendo los mosaicos que se necesitan.

Por ejemplo, la imagen Blossom.jpg es un jpeg de 1998x3024 y mide 1.42 MB. El directorio generado fué Blossom, que contiene al archivo ImageProperties.xml y al directorio TileGroup0, con 129 mosaicos de entre 2 y 14 KB.

TemplateWebPage.htm es una página cuyo código fuente muestra cómo se usa el componente. Hay dos lugares donde hay que colocar el nombre del directorio generado. Así, creo una página que contiene el código que se muestra en esta captura:


En Drupal
Está disponible el módulo Zoomify.

Luego de instalarlo, en la configuración verá 3 opciones para la generación de mosaicos: PHP, Python, o Ninguno.

Con PHP, se requiere que el sistema tenga GD, que es lo usual.

Con Python, se requiere que el sistema tenga instalado Python, el paquete Zoomifymage y PIL, la biblioteca Python para el proceso de imágenes. Además hay que bajar la biblioteca gcc.
Además, si elige esta opción hay que colocar la ruta de la carpeta que contiene a ZoomifyImage (por ejemplo /var/www/html/album/sites/all/modules/zoomify/ZoomifyImage).

Con Ninguno, el formulario presentará un campo extra para que podamos subir los mosaicos generados por nosotros mismos. Puede ser una opción si las imagenes son demasiado grandes para preprocesarlas en web.

Luego, puede indicar los tipos de contenido a los que se asociará Zoomify.

Cuando en alguno de esos tipos de contenido se intente mostrar una imágen más grande que la especificada en la configuración, aparecerá la pestaa adicional Zoomify.

Más memoria
Eligiendo PHP, encontré que en ciertos casos, al cargar una imagen, aparecía una pantalla en blanco. Después de un rato buscando y probando, encontré que se debía a que el proceso de imágenes grandes puede requerir de PHP más memoria que la que normalmente se le asigna (16 MB). Subí el valor a 96 MB (lo sugerido por ImageCache) y funcíonó bien a partir de allí.

Navegador oculto
Algunos usuarios prefieren que no se muestre el navegador. Para hacerlo, hay que indicar zoomifyNavigatorVisible=false

29 de abril de 2010

Moviendo Drupal

Pensaba que mover un sitio drupal era siempre fácil, pero no necesariamente.

El procedimiento báse consiste en:
  1. Preparar el sitio de origen (desactivar Clean URL y hacer Clear Cache)
  2. Hacer un backup de la base de datos del sitio de origen.
  3. Preparar el sitio destino (un sitio drupal básico) .
  4. Importar ese backup en la base de datos del sitio de destino.
  5. Copiar los archivos de los directorios modules y files que sea necesario.
Sin embargo, hay algunas cosas importantes que tener en cuenta. Principalmente si se trabaja con un sitio tipo multisite.
Multisite es un tipo de configuración de drupal que permite que varios sitios compartan el mismo motor drupal. Puede encontrar un poco más de información sobre esto en el post Resolviendo Drupal Multisite.
Cuando un sitio drupal sube un archivo, por ejemplo una imagen, guarda su ubicación en la base de datos, incluyendo el nombre del sitio. Así que si el sitio de destino tiene un nombre diferente, después de la importación tendrá problemas para ubicar los archivos. Por ejemplo, al tratar de mover un sitio tipo multisite llamado localhost.portal a otro llamado com.portal.

Además, en la tabla variable, el campo file_directory_temp guarda el valor del directorio temporal. En Windows puede ser algo como s:20:¨"C:\bin\dev\xampp\temp", pero en Linux es más bien algo como s:4:"/tmp".

Para resolver estas cuestiones, se puede agregar un paso al procedimiento:
2.5 Corregir en el backup de la base de datos las referencias site/nombre_origen por site/nombre_destino, y el valor de file_directory_temp. Algunas veces, una cadena va precedida de s:xx, donde xx representa la longitud de la cadena, y hay que tener cuidado de que xx tenga el valor correcto (aunque no me he puesto a investigar que pasa si no es así =P ).

Una forma de trabajar

Ahora tengo un poco más de cuidado al nombrar mis sitios localmente, para que sea más fácil moverlos a producción.

Por ejemplo, si mi sitio drupal es tipo multisite y su dirección es www.kobaonline.com/drupalab/portal/ , entonces requeriré un directorio sites/com.drupalab.portal.

Para que en mi Apache local pueda tener un sitio de esas características, necesito un dominio. Quizás podría usar definir un virtualhost, pero también se puede hacer con la configuración de Windows. Lo que yo hice fué agregar en C:\Windows\System32\drivers\etc\hosts la línea:

127.0.0.1        example.com
Por convención [RFC2606], example.com, example.net y example.org son dominios libres, así que parece que no hay problema si lo usamos localmente.
Después de hacer eso, puedo desarrollar localmente un sitio tipo multisite cuya dirección sea example.com/drupalab/portal/, que también requiere un directorio sites/com.drupalab.portal.

De ese modo, cuando mueva mi sitio a kobaonline.com, que está en un hosting Linux, sólo tendría que preocuparme por el valor del directorio temporal.

    27 de abril de 2010

    Resolviendo HTTP request status Fails

    Usando Drupal 6.16 con XAMP 1.7.1 en Windows 7 Home Basic obtengo este error:


    HTTP request status Fails
    Your system or network configuration does not allow Drupal to access web pages, resulting in reduced functionality. This could be due to your webserver configuration or PHP settings, and should be resolved in order to download information about available updates, fetch aggregator feeds, sign in via OpenID, or use other network-dependent services.

    Al parecer, Drupal no logra encontrarse a sí misma. Pude solucionarlo entrando a C:\Windows\System32\drivers\etc\hosts y asegurándome que estuviera descomentada la línea:

    127.0.0.1       localhost

    Referencia: HTTP request staus fails

    Drush en Windows 7

    Drush es una utilidad Drupal que funciona en línea de comandos y permite descargar módulos, activarlos y desactivarlos, entre otras cosas.

    Aunque algunas personas no se sienten muy bien frente a una consola de comandos, en realidad puede ser tu mejor amiga. Cuestión de buscar conocerla un poco mejor.

    Una de las ventajas de los comandos escritos es que son muy fáciles de registrar. Basta con ir copiándolos a un archivo de texto. Luego, puedo reproducir fácilmente todos los pasos que hice. Es una manera de automatizar. Con los clics es un poco más complicado.

    Instalando Drush en Windows 7

    Usando Windows 7 Home Basic 64, Drupal 6.16, Drush 3.

    Drush es un conjunto de scripts que se tienen que colocar en un directorio que sea parte del path de ejecutables (la que en Windows está definida por la variable de entorno Path).

    Por ejemplo, descomprimo drush-All-versions-3.0.tar.gz y lo coloco en C:\bin\dev\drush\

    Me aseguro de que ese directorio esté en el path (WIN+PAUSA para información del sistema, Configuración avanzada del Sistema, Opciones avanzadas, Variables de entorno, Variables del sistema, Path), agregando al valor de Path el separador punto y coma (;) seguido del nombre del directorio:

    Path: %SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;%JAVA_HOME%\bin;C:\bin\dev\xampp\php;C:\bin\system\gnuwin;C:\bin\dev\drush

    Observe que también está en el Path la ruta del PHP. Asegúrese de que también la tiene, porque Drush necesita a llamar a PHP en la línea de comandos.

    En Linux, Drush encuentra fácilmente algunos utilitarios que necesita para funcionar (como tar y wget), pero estos no están en Windows, así que hay que instalarlos.

    Estos se pueden buscar en la lista GNUWin32 Packages. Son BsdTar/LibArchive, Gzip, Wget, OpenSSL y algunas dependencias.

    En cada caso, uno puede elegir bajar un ejecutable para instalarlo, pero me pareció más simple bajar los zip y sacar de ellos sólo las cosas que necesito. Los enlaces de los zip son:
    En cada una encontré un directorio bin de donde extraje los exe y dll que me parecieron convenientes.

    De libarchive se obtienen bsdcpio.exe, bsdtar.exe y libarchive2.dll.
    De gzip, gzip.exe.
    De wget, wget.exe.
    De openssl, libssl32.dll.
    De las dependencias, bzip2.dll y zlib1.dll

    En las páginas oficiales de Drupal no menciona a OpenSSL, pero en mi caso el sistema pedía el libssl32.dll que ese paquete contiene.
    Otra cosa importante que hay que tener en cuenta en renombrar bsdtar.exe como tar.exe.

    En una máquina Windows 7 Home Professional con esto quedó listo, pero en la que tiene Windows 7 Home Basic tuve que darle a mi usuario full acceso al directorio drupal (clic derecho, Propiedades, Seguridad, elegir el usuario, Editar, elegir el usuario, en Control total, marcar Permitir).
    Si todo salió bien será posible colocarse en algún directorio drupal (por ejemplo cd C:\bin\xampp\htdocs\drupal) y ejecutar algún comando drush.

      Algunos comandos drush

      drush
      muestra las opciones
      drush st
      muestra el estatus de la aplicación drupal en la que estemos localizados
      drush dl nombre_modulo
      descarga el módulo nombre_modulo (la última versión pertinente) y la coloca en sites/all/modules
      drush en nombre_modulo
      habilita el módulo nombre_modulo

      En el caso de una aplicación multisite se usa algo como:

      drush -l localhost.site1
      drush -l localhost.site1 st
      drush -l localhost.site1 dl nombre_modulo

      y así por el estilo.

      Ojalá le sirva de ayuda.

      28 de marzo de 2010

      Al fín, Hello World!

      Siguiendo la guía de un libro había hecho un par módulos, pero sin entender realmente lo que hacía.

      Ayer asistí al Drupal Camp Perú 2010. En la conferencia de Fernando P. García, "Cómo hacer tu primer módulo en Drupal 6", él siguió un enfoque algo diferente al que había visto en los libros. Partió de lo que quería que apareciera en la página, dada una ruta, y cómo sería el módulo para lograrlo. Usó el hook_menu().

      Hello World!
      Basado en el módulo que mostró Fernando, practiqué en casa hacer un módulo que simplemente dijera 'Hello World!'. Por alguna razón, cuando eso me resulta me siento más seguro para poder continuar :)

      Lo que deseo es un módulo que, al ingresar a la ruta helloworld, muestre 'Hello World!'.

      En el directorio modules apropiado, creé el directorio helloworld, y dentro los archivos helloworld.info y helloworld.modules.

      helloworld.info
      name = Hello World
      description = Hello World module
      core = 6.x

      helloworld.module

      /**
       * Implementation of hook_menu().
       */
      function helloworld_menu() {
          $items['helloworld'] = array(
              'title' => 'Hello World',
              'page callback' => 'helloworld_page',
            'access arguments' => array('access content'),
          );
          return $items;
      }

      function helloworld_page() {
          return 'Hello World!';
      }

      Me interesó revisar la lista de módulos (admin/build/modules) mientras se hacía esto. Para que aparezca el item correspondiente al nuevo módulo es necesario tener el .info con su contenido y el .module, no importa vacío.
      Una vez definido el .module, es necesario limpiar el cache (por ejemplo entrando a admin/settings/performance, Clear cached data), para que se refleje el cambio al acceder al url http://localhost/drupal/helloworld, donde drupal es el nombre de mi site en este caso.

      Hello You!

      Lo que deseo es un módulo que, al ingresar a la ruta hello/Antonio, muestre 'Hello Antonio!'.

      En el directorio modules apropiado, creé el directorio hello, y dentro los archivos hello.info y hello.modules.

      hello.info
      name = Hello
      description = Hello module
      core = 6.x

      hello.module

      /**
       * Implementation of hook_menu().
       */
      function hello_menu() {
        $items['hello'] = array(
          'title' => 'Hello',
          'page callback' => 'hello_page',
          'access arguments' => array('access content'),
        );
        return $items;
      }

      function hello_page($name='World') {
        return 'Hello '.$name.'!';
      }

      Una vez definido el .module, es necesario limpiar el cache (por ejemplo entrando a admin/settings/performance, Clear cached data), para que se refleje el cambio al acceder al url http://localhost/drupal/hello/Antonio, donde drupal es el nombre de mi site en este caso.

      Frameworks y Drupal

      Framework... con qué se come?
      Recuerdo que, cuando empecé a programar en PHP, navegando por Internet encontraba algunas veces la palabra framework.

      Primero pasaba de largo, pensando que todavía estaría lejos el momento en que estuviera listo para abordar algo que, seguramente por complejo, no había visto en los tutoriales o libros introductorios de PHP. Quizás deberían hablar más sobre eso.

      Alguna vez bajé un framework e intenté hacer algo, pero lo dejaba después de un rato, frustrado por no entender dónde poner las cosas para lograr las cosas que quería.
      Ya había desarrollado algunas aplicaciones web, principalmente usando el JSP del mundo Java. Con PHP todo era similar pero más simple y ligero, más directo y más rápido, y me empezaba a gustar. Pero no entendía lo de los framework.

      Luego, estuve encargado del desarrollo de una aplicación web que debía ser hecha con Struts 1.1. Tardé como un mes en darme cuenta que el framework determinaba una secuencia de trabajo. Eso era. Por eso se llamaba framework. Quizás suene gracioso, pero sinceramente pienso que muchas personas usan frameworks sin entender realmente lo que son.

      Después, aprender Struts en Java me ayudó a que fuera luego más fácil volver a intentarlo en PHP, y aprendí CakePHP y otros frameworks más.

      Ahora, después de haber desarrollado sin esos frameworks y con ellos, en solitario y en equipo, sin y con estándares declarados, y después de haber reflexionado algún tiempo, tengo algunas ideas y opiniones sobre el tema.

      Todos son frameworks
      Un framework es, básicamente, una forma de trabajar.

      Muchas personas piensan, casi de inmediato, que ser framework es algo exclusivo de Struts, CakePHP, CodeIgniter, Zend, o similares, publicados en un site, con enlaces para descargar y documentación que leer y practicar.

      Esos son frameworks famosos pero, en realidad, cada persona y cada equipo de trabajo determina un framework cada vez que hace un desarrollo. Lo que funciona bien se puede volver a usar y lo que no funciona bien se puede probar de otro modo. Con el tiempo se van incorporando nuevos casos y se va puliendo el esquema que, descubrimos un día, se llama framework.

      Incluso cuando se usa un framework estandar se determina otro framework al desarrollar, ya que cada equipo lo puede usar de diferente modo para resolver el mismo problema.
      Pienso que un framework debería ser entendido de un modo amplio. No sólo como una manera de organizar el código, sino como la manera de trabajar de la gente sobre ese código. Porque ambas cosas están relacionadas. Por ejemplo, si un equipo no comprende MVC, puede tomar un framework MVC como base y en el proceso de desarrollo desfigurarlo hasta llegar a una solución con las mismas, o más, complicaciones de un proyecto no-MVC.

      Es decir, un framework tiene un workflow asociado.

      Adoptar un framework significa aprender a producir de modo sostenible código que siga cierto esquema. Aprender cómo se debe ver el código, pero a la vez también cómo podría ser un flujo de trabajo que permita hacerlo indefinidamente.

      Qué vendría a ser Drupal?
      Digamos que desarrollas aplicaciones web habitualmente. Sería bueno que pudieras agregar y quitar funcionalidades fácilmente. Que tuviera una naturaleza modular. ¿Cuántos proyectos has desarrollado que permita incorporar una funcionalidad de ese modo?.

      Además, sería muy bueno que esas cosas se pudieran hacer desde la misma interfaz web. ¿Cuantos proyectos has desarrollado que permitan instalar y desinstalar funcionalidades desde la misma página?

      Si pudieras hacerlo, llegarías a algo similar a Drupal. Luego, podrías empezar a desarrollar módulos para manejo de usuarios, manejo de contenido, acceso a contenido, subir imágenes, etc. Pero, un momento, Drupal ya tiene módulos para eso y mucho más.

      Entonces, incluso aunque pudieras hacer por tí mismo una aplicación a la Drupal, es bastante conveniente usar Drupal y descubrir qué otras cosas más podemos hacer con él.
      La web marcó un hito cuando permitió enlazar páginas de información, democratizando la publicación. El wiki marcó otro hito cuando permitió hacerlo usando la misma página para editar, democratizando un poco más las cosas.

      Actualmente, gran parte de las aplicaciones web se desarrollan de modo similar a como antes se desarrollaban las páginas web; usando editores externos y profesionales especializados. Pero Drupal nos está acercando más a la posibilidad de hacer el desarrollo completo usando la misma página, y que dependamos menos de otros profesionales para lograr la aplicación web que queremos.

      Creo que no hay muchos frameworks que se preocupen de proveer un desarrollo modular o desde la misma página.
      Posiblemente, en el futuro, vayan apareciendo más entornos a la Drupal, o tratando de ser más drupables, de modo similar a la explosión web, o la explosión de wikis que hubo después que apareció el primero.
      Sí, también me parece buena idea. Así que desde hace un tiempo estoy estudiando Drupal. Felizmente hay bastante material para consultar y gente dispuesta a darnos una mano.

      Ya puedo hacer sites con cierta funcionalidad, pero aún no me siento seguro. En especial con el desarrollo de módulos. Me suelo sentir como cuando intentaba aprender MVC por primera vez. Un poco perdido y sin ningun texto ni nadie con una explicación simple y clara.

      Entonces, voy escribiendo esta bitácora de mi exploración en Drupal, la Drutacora. Quizás a alguien le ayuden estas notas en su propio viaje.

      Más artículos