Menú de opciones con bordes redondeados

Uno de los portales que se están desarrollando en la Fundación tenía una barrita de menú con botones redondeados. Los amantes del Flash dijeron rápidamente que ellos lo hacían fácilmente en Flash que no me preocupara; todo quedó así … hasta hoy. Hoy lo estuvieron pensando y se dieron cuenta que era muy difícil y dispendioso crear las imágenes y tratar de cambiar de color el botoncito según esté seleccionado o no. Por suerte yo ya estoy superando esta gripa y amanecí con ganas de un poco de carpintería.

Lo primero que hice fue buscar la librería de JavaScript Nifty Corners que ya había visto con anterioridad para tal fin, sin embargo en la búsqueda también encontré una segunda y dejé que la revisión de los ejemplos determinara cual iba a utilizar. Resulta que Curvy Corners si permite darle un borde al botón redondeado … o al menos si lo muestra en un ejemplo, así que la escogí.

Creo algunos DIV de clase miBoton con IDs boton1, boton2, boton3, … Ahí es donde entra a jugar Curvy Corners.

Se incluye la librería de Javascript.

<script type="text/JavaScript" src="rounded_corners_lite.inc.js"></script>

En la función onLoad de la página se especifican las propiedades de la redondez y se asocia esta con los elementos de la clase miBoton.

settings =
{
tl: { radius: 10 },
tr: { radius: 10 },
bl: { radius: 10 },
br: { radius: 10 },
antiAlias: true,
autoPad: true,
validTags: ["div"]
}

var myBoxObject = new curvyCorners(settings, "miBoton");
myBoxObject.applyCornersToAll();

Por mi parte creo la función cargarBotones() que establece la referencia global a los botones incluyendo el objeto HTML y enlace a donde se deberá dirigir el navegador cuando le hagan click.

Igualmente implementé la función colorearBotones() que le pone el fondo verde a los botones no seleccionados y naranja a los seleccionados.

La solución fue muy sencilla. Lo bueno es que hasta ahora cumple con lo que necesitaba el cliente y se desarrolló bastante rápido la solución. Lo malo fue que, como cosa rara, cuando verifiqué que funcionara con IE7 … todo se derrumbó.

Inicialmente sólo aparecía la cajita de los botones, vacía y cuadrada. Nada funcionaba. Lo probé en mi equipo que tiene instalado el JavaScript Debugger de Microsoft y se quejaba de una linea de la librería de CurvyCorners … ni idea porqué. Verifiqué el ejemplo y este funcionaba sin problemas así que comparé mi código con el del ejemplo … nada extraño. Hice una copia y empecé a convertir mi código en el código de ejemplo hasta que lo hice funcionar … no había nada raro … solamente el color de los botones. Había puesto que fueran green normalmente y orange si los habían seleccionado; pues parece que IE no entiende inglés, sólo hex.

Cambié la especificación de los colores y todo empezó a fallar extrañamente, a ratos, sin sentido. Quité los llamados de colorearBotones() y algunos botones se coloreaban y otros no. Obra de satanás. Comenté el código de la función y coloreamiento se detuvo.

Tuvo que pasar otro rato para que me diera cuenta que ese útil navegador no estaba refrescando bien la página cada vez que yo le daba F5 y me estaba mostrando versiones antiguas. Conclusión con IE es mejor darle siempre CTRL-F5 para que refresque completamente.

Se puede consultar el código fuente del ejemplo haciendo click en el siguiente enlace: test_curvycorners

Jugando con InnerHTML (2)

Bien, encontré una mejor forma de hacerlo … con DOM.

Insertar una nueva fila en una tabla (destino) es fácil, sólo es establecer donde se quiere e insertarla.

var nuevo_indice = destino.rows.length;
fila = destino.insertRow(nuevo_indice);

Después es necesario especificar el contenido de las celdas, para el ejemplo, un checkbox y dos valores de texto.

celda = fila.insertCell(0);
valor = document.createElement("input");
valor.type = "checkbox";
valor.id = "marca";
valor.name = "marca";
valor.value = nuevo_indice;
celda.appendChild(valor);

celda = fila.insertCell(1);
valor = document.createTextNode(nombres + " - " + nuevo_indice);
celda.appendChild(valor);

celda = fila.insertCell(2);
valor = document.createTextNode(apellidos);
celda.appendChild(valor);

Para remover las filas obtengo una referencia a los elementos que sean input y pertenezcan a la tabla.

var inputs = origen.getElementsByTagName("input");

Después filtro los que sean checkbox y sean los que me interesan (id=’marca’). Los almaceno en un arreglo.

for (i=0; i<inputs.length; i++)
{
if (inputs[i].type == "checkbox" &&
inputs[i].id == "marca" &&
inputs[i].checked)
{
checkboxes[chk_cuenta] = inputs[i];
chk_cuenta ++;
}
}

Después de filtrados procedo a eliminarlos.

for (i=0; i<checkboxes.length; i++)
{
origen.deleteRow(checkboxes[i].value - 1*i);
}

En este punto encontré un serio problema. El value de los checkboxes seleccionados me indica cuales son las filas que deseo remover, pero después de remover la primera de ellas la continuidad de las filas se altera ya que se eliminó un elemento. Para compensar esto se disminuye 1*i el índice de la fila, siendo 0 para el primer caso cuando no se ha alterado, 1 cuando se ha removido una fila y así sucesivamente.

La solución anterior soluciona el problema durante un procedimiento de eliminación de filas, sin embargo al terminar la tabla queda con sus índices alterados y por consiguiente va a fallar la siguiente eliminación y posiblemente la adición de filas también va a ser confusa. Para esto tuve que corregir los índices de las filas tan pronto como se termina la remoción.

for (i=0; i<inputs.length; i++)
{
if (inputs[i].type == "checkbox" &&
inputs[i].id == "marca")
{
inputs[i].value = i + 1;
}
}

El archivo fuente del ejemplo se puede descargar del siguiente enlace: test_inner2.html

Jugando con InnerHTML

Con este ejemplo muy sencillo es posible agregar filas de una tabla en el lado del cliente sin hacer un requerimiento al servidor y ahorrarse uno la consabida lentitud, útil en momentos de agregar items a una lista. Obviamente, a diferencia de Ajax, los elementos sólo viven en el lado del cliente hasta que se envían, en un sólo requerimiento, hacia el servidor donde son almacenados en la base de datos.

La idea es crear una tabla (info) que va a contener los datos.

<table id="info" name="info" class="tabla">
<tr class="titulos">
<td>
Nombres
</td>
<td>
Apellidos
</td>
</tr>
</table>

Un formulario desde el cual se agregarán nueva información.

<form id="datos" name="datos" onsubmit="return agregar_fila(this)">
Nombres: <input type="text" id="nombres" name="nombres" value="" size="25" />
Apellidos: <input type="text" id="apellidos" name="apellidos" value="" size="25" />

<input type="submit" id="agregar" name="agregar" value="Agregar" />
</form>

Y una función de JavaScript que agregará la nueva información (nombres y apellidos) a la tabla.

function agregar_fila(origen)
{
nom = origen.nombres.value;
apl = origen.apellidos.value;

document.getElementById("info").innerHTML += "<tr class='fila'>
<td>
" + nom + "
</td>
<td>
" + apl + "
</td>
</tr>";

return false;
}

El archivo de ejemplo puede descargarse desde el siguiente enlace: test_inner.html

Por fin un ejemplo de Box Model entendible (2da. parte)

Bueno, no me aguanté las ganas de seguir haciendo pruebas. Esta vez con Layout #13 de Layout Gala.

El ejemplo anterior tomado de Matthew Levine estaba elegante pero no quiso funcionar en IE.

Simplifiqué un poco el ejemplo y esto fue lo que obtuve.

test_div2a.gif

Me hace falta lograr que las columnas izquierda (azul) y derecha (rojo) ocupen tanto espacio como ocupe la columna central (salmón), sin embargo si utilizo height: 100% intentan tomar el alto de la pantalla.

Por ahora este es un gran paso para -mi- humanidad en la lucha contra las cajas.

El archivo de prueba puede descargarse del siguiente enlace: test_div2a.html.

Por fin un ejemplo de Box Model entendible

Hace hace muy poco no había querido trabajar con CSS. Siempre encontraba una rápida respuesta para mejor hacerlo de la manera convencional … es mas rápido, no tengo tantos problemas, no se complica tanto, etc. En el último par de meses en los que he estado de webmaster adaptando la presentación de algunos portales a la provista por el publicista de la Fundación he aprendido algunas cositas y como todo lo que aprendo, me ha terminado gustando mucho.

Una de las cosas de las que en realidad no he logrado entender completamente es el box model (modelo de cajas) de CSS. Yo quiero hacer lo que la teoría sugiere: en el HTML dejar sólo el contenido y en CSS especificar como se va a presentar esa información. Según esto, yo quiero que el layout (distribución) de mi página esté dada por el CSS con DIVs y no en el HTML con TABLE. Pero ah que cosa mas extraña para mi.

Le he sacado tiempo en varias oportunidades pero no lo he logrado. Lo mas cerca que he estado funcionaba perfecto en Firefox pero se corría inexplicablemente en IE … como siempre.

Hoy amanecí con ganas de volver a intentarlo y encontré un par de enlaces muy interesantes. Entre ellos me gustó In Search of the Holy Grail de Matthew Levine quien se toma su tiempo para explicar la implementación de una página con tres columnas, dos de ellas de ancho fijo y el centro líquido (consume el tamaño complementario) y con una estructura de DIVs simple, tal y como yo la quería.

<div id="header">Este es el header</div>
<div id="container">

<div id="center" class="column">Este es el centro</div>
<div id="left" class="column">Esta es la izquierda</div>
<div id="right" class="column">Esta es la derecha</div>
</div>
<div id="footer">Estos son los pies</div>

Este código HTML produce el siguiente resultado después de ser mezclado con el CSS correspondiente.

demo_css_div

Como siempre … en Firefox funciona perfecto, lo acabo de probar en IE7 y se enloquece, a pesar de que el autor menciona que funciona en los principales navegadores.

El archivo de prueba que utilicé se puede descargar del siguiente enlace: test_div.html.

Espero mas adelante tener un poco mas de tiempo y hacerlo funcionar ya que no quiero seguir haciendo páginas basadas en TABLEs.

Otro enlace relacionado con el tema que se ve prometedor es Layout Gala (http://blog.html.it/layoutgala/) en el que se presentan varios diseños basados en columnas.

Problemas entre capas y objetos Flash en HTML

Tenía que publicar en uno de los portales un bloque con un logo hecho en Flash. La puesta fue muy fácil, el problema apareció cuando los menúes desplegables empezaron a aparecer por debajo del objeto Flash.

Hice pruebas envolviendo el object en un div y modificando el z-index, pero nadita.

Los objetos Flash traen ahora una propiedad que se llama wmode a la cual si se le asigna el valor de transparent no solo hace el fondo del SWF transparente sino que hace que el objeto respete el índice de profundidad.

Hay que poner el valor sugerido como parámetro del object y dentro de la etiqueta embed para que funcione en los dos navegadores.

Ejemplo:

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="185"
height="205">
<param name="wmode" value="transparent">
<param name="movie" value="banner_iso9001_ntcgp1000.swf">
<param name="quality" value="high">
<embed src="banner_iso9001_ntcgp1000.swf" quality="high"wmode="transparent"
pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"
type="application/x-shockwave-flash"
width="185"
height="205"></embed>
</object>

Encriptando cositas con MCrypt

La fase final de mi experimento con XML-RPC suponía poder encriptar la solicitud que iba del cliente al servidor así como la respuesta de este último hacia el cliente. Como se sobreentiende, esta encriptación debería ser de dos vías. Para esto estuve revisando la API de PHP y encontré que la utilización de la librería mcrypt es bastante útil y que por suerte hasta el hosting mas antiguo de todos la soporta en PHP.

Para esto entonces desarrolle dos procedimientos: encriptar y desencriptar para separar su funcionalidad.

Encriptar.

Para encriptar información se requieren los siguientes parámetros.

  • El nombre del algoritmos de encriptación (Ej. blowfish).
  • El modo de encriptación (Ej. cfb).
  • El orígen del azar (Ej. MCRYPT_RAND).
  • La contraseña de encriptación (secreta).
  • Los datos a encriptar.
  • El vector de encriptación (IV) que actúa como semilla (opcional).

Se abre el módulo del algoritmo de encriptación y se especifica su definición.

$td = mcrypt_module_open($encryption_algorithm, ”, $encryption_mode, ”);

Se crea el vector de encriptación y se determina la longitud de la llave.

$client_iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), $random_source);
$ks = mcrypt_enc_get_key_size($td);

Se crea la llave basada en la contraseña de encriptación especificada por el usuario (mayor seguridad).

$key = substr(md5($encryption_key), 0, $ks);

Se inicializa la encriptación con la información establecida anteriormente.

mcrypt_generic_init($td, $key, $client_iv);

Se encriptan los datos.

$encrypted_data = mcrypt_generic($td, $raw_data);

Se termina el proceso de encriptación.

mcrypt_generic_deinit($td);

Al final se obtiene el vector de encriptación ($client_iv) y la información encriptada ($encrypted_data), ambos son necesarios para realizar el proceso inverso.

Desencriptar.

Para desencriptar la información se requiere la siguiente información.

  • El nombre del algoritmos de encriptación (Ej. blowfish).
  • El modo de encriptación (Ej. cfb).
  • La contraseña de encriptación (secreta).
  • El vector de encriptación (IV).
  • Los datos encriptados.

Se abre el módulo del algoritmo de desencriptación y se especifica su definición.

$td = mcrypt_module_open($encryption_algorithm, ”, $encryption_mode, ”);

Se determina la longitud de la llave.

$ks = mcrypt_enc_get_key_size($td);

Se crea la llave basada en la contraseña de encriptación especificada por el usuario (mayor seguridad).

$key = substr(md5($encryption_key), 0, $ks);

Se inicializa la desencriptación con la información establecida anteriormente.

mcrypt_generic_init($td, $key, $client_iv);

Se desencriptan los datos.

$decrypted_data = mdecrypt_generic($td, $encrypted_data);

Se termina el proceso de encriptación y se cierra el módulo.

mcrypt_generic_deinit($td);
mcrypt_module_close($td);

Al final se obtiene la información desencriptada ($decrypted_data) la cual debe corresponder con la información original. Algunas veces la desencriptación deja espacios en blanco a los lados de la cadena desencriptada por lo que puede ser de utilidad pasar la información por un trim() como último paso.

La demostración de este código se encuentra siguiendo este enlace Demo #0. Este cuenta con los archivos config.php (variables necesarias), security.php (funciones para encriptar y desencriptar) y demo1.php (ejemplo funcional).

Mozilla Thunderbird + Lightning + GoogleCalendar

Ayer descubrí algo interesante. Por supuesto mi cliente de correo desde hace muchos años ha sido Thunderbird de los productos Mozilla. Hace un tiempo instalé el módulo Lightning para la gestión de calendarios (agendas), sin embargo apenas hace poco le empecé a dar un uso real. Ayer se me ocurrió que debería haber una forma de sincronizar este calendario con el web que provee Google … y por supuesto que la había.

Los siguientes pasos requieren se tenga instalado los siguientes paquetes de software:

Es necesario instalar el plugin de sincronización para Thunderbird, el cual puede descargarse de https://addons.mozilla.org/en-US/thunderbird/addon/4631.

De GoogleCalendar se requiere obtener el enlace privado del calendario.

Google Calendar > Configuración > Configuración > Calendarios > [calendario] >
Dirección privada: [XML] (copiar dirección del enlace)

En Thunderbird se asocia el calendario de Google a un calendario local.

Archivo > Nuevo > Calendario > En la red > Google Calendar >
Ubicación = [XML]
(pegar dirección del enlace)

Es posible mantener en Lightning varios calendarios provenientes de diferentes orígenes de datos, por ello es útil elegir un color diferente para cada una de estas fuentes.

Crear nuevas secciones en page.tpl de Drupal 5

Cómo agregar secciones nuevas a plantilla de un tema de Drupal además de las convencionales header, footer, left_sidebar, right_sidebar y content ?En el contenido de la plantilla (archivo page.tpl.php) agregar la impresión del contenido del nuevo bloque. En este caso links_section.

<?php if ($links_section):?>
<?php print $links_section; ?>
<?php endif; ?>

En el archivo de la plantilla (template.php) crear o editar la función TEMA_regions() especificando las regiones contenidas. En este caso, el tema se llama mitema y al final se incluye la sección nueva.

function mitema_regions()
{
return array('left' => t('left sidebar'),
'right' => t('right sidebar'),
'content' => t('content'),
'header' => t('header'),
'footer' => t('footer'),
'links_section' => t('links section'));
}

Para mas información consultar el siguiente enlace: http://drupal.org/node/29139.

“client denied by server configuration” en Apache 2.2.4

Esta vez estoy instalando Apache 2.2.4 y encontré un problema. Cuando ya estaba todo supuestamente listo e intentaba solicitar una página del Document_Root me respondía el servidor con un 403 (forbidden). Muy extraño. En los registros aparecían mensajes como el siguiente:[Tue Aug 07 17:06:14 2007] [error] [client 192.168.1.2] client denied by server configuration: /data/www/index.html

Encontré que la versión 2.2.x ajusta de una manera mas restrictiva los permisos sobre el directorio raíz (‘/’). En httpd.conf dice lo siguiente:

<Directory />
Options FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
</Directory>

Comentando la última línea: “Deny from all” se solucionó el inconveniente.