Forzar download de archivos con .htaccess

Desde hace un tiempo que los browsers son más que sólo browsers. De hecho, Google desarrolla Chrome para que se convierta en el único software instalado en tu máquina. Más allá de eso, todos los browsers hoy en día ejecutan casi todo tipo de archivo (plugins mediante).

Un típico caso es el de un archivo .pdf, donde generalmente se ejecuta en una pestaña el visualizador. Pero a veces uno tiene la necesidad de querer obligar al usuario (o facilitarle) la descarga del archivo, para que disponga como más le guste luego.

Para eso, lo que hay que hacer es un archivo .htaccess en el directorio donde se aloje el archivo a descargar, con el siguiente contenido:


AddType application/octet-stream .pdf

De donde se puede cambiar el pdf por cualquier tipo de extensión.

Sí, así de simple.

Filezilla quita los saltos de línea

No estoy seguro si me había sucedido antes. Hoy ocurrió, y ya no olvidaré la solución.

Filezilla tiene distintos modos de transferencia de archivos: automático (viene elegido por defecto), ASCII y Binario. La teoría dice que si el archivo es de texto, el tipo de trasferencia adecuado es ASCII, y si es imagen, zip, etc, Binario.

FIlezilla (y asumo que otros softwares también), por algún motivo que también desconozco, en una transferencia ASCII hace de las suyas, asumo que para optimizar la transferencia, o algo similar. Lo cierto es que a mí me quitó los saltos de línea, y en un archivo .php eso se traduce en errores de programación.

¿Solución? menú Transferencia / Tipo de transferencia / Binario. Eso sube tu archivo tal cual como lo tengas en tu compu al servidor.

Fork me

En un ya antiguo post, expliqué una modificación hecha a un plugin de jQuery muy usado y genial, el jQuery.Validation. Hace poco recordé aquella modificación,  y se me ocurrió revisar si con los sucesivos updates del plugin habian incluído esa funcionalidad.

Tuvo varios, muy buenos por cierto, pero ninguno añadiendo mi funcionalidad. Entonces hice lo que todo programador debe hacer al menos una vez en su vida: fork, clone local, push, pull request.

Para los que recién empiezan en el mundo de la programación, trataré de resumir los conceptos.

GitHub es un sitio que pone a nuestra disposición distintos proyectos de la comunidad de programadores. No sólo están para descargar, sino para que todos mejoremos los productos, detectando errores y dando las posibles soluciones. Todo esto es posible gracias a GIT como sistema control de versión.  La curva de aprendizaje de GIT es mayor en comparación a SVN, pero es notablemente superior.

Como a mí se me ocurrió una nueva funcionalidad, puedo proponerla. El modo correcto es ir al proyecto en cuestión, clickear en Fork para crear una copia del mismo, pero en nuestro perfil, clonarlo en forma local, hacer los cambios y subirlos, y finalmente, avisarle al creador del original que uno tiene una propuesta nueva. Si le parece correcto, la modificación pasará a formar parte del proyecto original.

Hace un par de días realicé el pull request para mi versión del jQuery.Validation, pueden chequearlo desde acá.

UPDATE: mi aporte ya forma parte del plugin oficial 🙂

Seteo de FB.Canvas.setAutoGrow

Al desarrollar apps de Facebook, es de esperar que las distintas páginas que la componen tengan diferentes altos, como sucede con cualquier sitio web.

Como las apps se cargan dentro de un iframe (el cual siempre tiene un alto), necesitamos hacerle saber a Facebook que a medida que navegamos entre pantallas, debe actualizar el alto del iframe.

Es así como Facebook nos da el maravilloso FB.Canvas.setAutoGrow,  que promete ser la solución a las molestas barras de scroll vertical del iframe.

Pero no lograrán deshacerse de ellas al menos que pongan estas líneas de CSS:


html, body{
    overflow:hidden;
}

No estaría mal que incluyan este código en el developers.facebook.

Ajuste para jQuery.validationEngine

Uno de mis plugins favoritos de jQuery, es el jQuery.validationEngine. Un muy completo plugin para validación de formularios, muy customizable, desde el tipo de validación, hasta dónde debe aparecer el warning de error.

Pero hoy me pidieron algo que si lo trae por defecto, no lo encontré. El plugin ejecuta la validación de cada campo no sólo cuando el usuario hace el submit del formulario, también lo hace cuando el usuario termina de completar un campo y pasa al siguiente. De esta manera, permite corregir  lo erróneo en el momento.

Si se desea evitar esa validación al pasar de un campo al otro y dejarla activa sólo al hacer submit, uno debe hacer lo siguiente:


$(".registerform").validationEngine({
   bind: false
});

Hoy me pidieron que la validación al pasar de un campo a otro se haga sólo si el usuario empezó a escribir algo en el campo. Por defecto, si un campo es requerido, el usuario hace foco, no lo completa, pasa al siguiente, y se le muestra un error, ya que el campo es obligatorio y lo dejó sin completar.

Pero el pedido tiene sentido. No hace falta ejecutar esa validación del campo antes de siquiera empezar a completarlo. Y además, no debo perder la validación de campo requerido al hacer submit.

La mejor manera que encontré de hacer esta excepción fue editar jquery.validationEngine.js. Viendo el código, llegué a donde necesitaba editar:

_onFieldEvent: function(event) {
   var field = $(this);
   var form = field.closest('form');
   var options = form.data('jqv');
   options.eventTrigger = "field";
   // validate the current field

   window.setTimeout(function() {
      methods._validateField(field, options);
      if (options.InvalidFields.length == 0 && options.onSuccess) {
         options.onSuccess();
      } else if (options.InvalidFields.length > 0 && options.onFailure) {
         options.onFailure();
      }
   }, (event.data) ? event.data.delay : 0);
},

Aquí veo que se ejecuta la validación ante un evento del input (el cual se puede configurar mediante las opciones del plugin). Entonces, sólo debo ejecutar una verificación antes de validar:

_onFieldEvent: function(event) {
   var field = $(this);
   var form = field.closest('form');
   var options = form.data('jqv');
   options.eventTrigger = "field";
   // validate the current field

   if(field.val().length > 0){

      window.setTimeout(function() {
      methods._validateField(field, options);
      if (options.InvalidFields.length == 0 && options.onSuccess) {
         options.onSuccess();
      } else if (options.InvalidFields.length > 0 && options.onFailure) {
         options.onFailure();
      }
      }, (event.data) ? event.data.delay : 0);

   }
},

Y listo. De esta manera, primero chequeo que haya en verdad algo que verificar antes de entrar al proceso de validación. Y como esto está atado sólo a eventos del input, al hacer submit del formulario se ejecutan siempre las reglas de validación.

UPDATE: subí estos cambios a GitHub, más detalles acá.

Desarrollando para mobile

En las últimas semanas estuve desarrollando un sitio web mobile. Desde mi punto de vista, lo mejor que tiene el desarrollar/maquetar para mobile es que se pueden usar (y de hecho se fomenta) las ventajas de HTML5.

Con lo que me encontré a la hora de hacer esta webapp, es con pequeños yeites, que paso a poner a disposición de todos.

Un CSS para todos

Hace unos meses Jakob Nielsen, en contra de cualquier práctica realista, propuso que en lo que a desarrollo web mobile se refiere, lo mejor es olvidar las mediaqueries al hacer un único sitio que se adapta a todos los dispositivos, y en su lugar hacer sitios mobiles por separado del sitio full, tantos como hagan falta, e inclusive, crear tantos generadores de contenido como sean necesarios para permitir, justamente, esta proliferación de sitios.

Ridículo.

Yo uso mediaqueries por todos los motivos que se pueden encontrar googleando, y porque mis clientes y los de la agencia para la que trabajo no tienen presupuestos tan grandes para lograr lo que Nielsen propone. Entonces, un CSS con mediaqueries para todos.

Y la mejor manera que por el momento encontré para apuntar a las distintas pantallas, es la siguiente:


/* generales */
@media only screen and (min-width: 420px) and (max-width: 767px) {
}
/* iPad */
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
}
/* iPhone */
@media only screen and (max-device-width: 480px) {
}

Cabe notar que el de iPhone se superpone con el de dispositivos mobiles en general, por lo que esta diferenciación pocas veces hará falta, pero no está de más el saberla.

iPhone/iPad reescala al rotar

Este es un curioso problema, cuya solución, si bien la entiendo, no me gusta al 100%. El problema es que al cargar una página en un iPad, y luego cambiar la rotación, la escala «cambia» y nos vemos forzados a hacer el ajuste, que si bien el gesto con los dedos lo hace facilísimo, no deja de ser molesto. El problema se origina en código que se suele encontrar incompleto, como el siguiente:

<meta name ="viewport" content="initial-scale=1.0, width=device-width">

En la ardua tarea de copypastear código hasta que funciona, pocas veces nos detenemos a pensar para qué realmente sirve cada parámetro. El meta «viewport» nos permite indicarle al browser cómo se debe comportar nuestro contenido respecto del dispositivo. Podríamos entonces setear content=»width=200px», que lo que hará será forzar el ancho del contenido a un máximo de 200px. No se me ocurre un ejemplo práctico en donde querramos setear un ancho de esta manera, pero lo que si sabemos es que si podemos setear ese ancho de forma proporcional, entonces podemos poner nuestra web al ancho de cualquier dispositivo. Y eso es lo que hace width=device-width.

También se puede setear la escala a la que se debe ver el contenido con initial-scale=1.0. Pero lo que en verdad necesitamos para corregir las rotaciones de iPhones/iPods/iPads es lo siguiente:

<meta name ="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
<meta name="apple-mobile-web-app-capable" content="yes"/>

De este modo seteamos la escala incial, la escala máxima, y le quitamos la posibilidad al usuario de reescalar, al mismo tiempo que seteamos nuestro ancho de página a todo el dispositivo. Y con la instrucción que sigue le indicamos al dispositivo que nuestra aplicación corre en full screen.

Lo que veo de negativo de esto es que se le quita la posibilidad al usuario de escalar, y eso no me gusta. Pero se trata de equilibrio, si como usuario voy a estar cambiando la escala, entonces no puedo pretender que la rotación de mi dispositivo me recalcule todos los tamaños a como me es a gusto. O quizás debiera? en fin… será problema de los ingenieros.

Por nuestra parte, nos queda saber diseñar/maquetar para que los usuarios roten y puedan tener la mejor experiencia posible.

Les dejo un link a stackoverflow, de donde pude empezar a ilustrarme sobre el tema, y otro a la documentación oficial de Safari, de donde pueden leer sobre «apple-mobile-web-app-capable».

Favicons

El tema con los favicons en mobile se reduce a crear un archivo .png por cada tipo de pantalla que puede guardar la webapp como favorito. No me voy a extender demasiado en esto, porque simplemente seguí el paso a paso que encontré en esta página. El único tip que sí voy a dar es que los .png no pueden tener transparencia en IOS (android si los lee), confirmado esta vez en la documentación para desarrolladores de Apple.

Debugging

Por último, un plugin que no conocía, y permite cambiar el user agent en el Firefox o Chrome. Es algo que parece tonto, hasta que te das cuenta que todos los sitios usan el user agent para detectar si es o no un dispositivo móvil el que se conecta.

Altura 100% de un DIV

Setear un div al 100% de altura, o bien que siempre se estire según el contenido del div de al lado, es algo que sólo se puede cumplir en CSS mediante trucos. El viejo template de WordPress (creo que el que estaba en el 2.0), usaba una imagen para simular el alto 100%, seteando a la misma como fondo del div contenedor de los otros dos. El color de ambos divs estaba creado entonces por una imagen.

El truco que uso cuando me piden este tipo de funcionalidades, es el siguiente:


.contenedor{
 overflow:hidden;
 width:680px;
 margin:0 auto;
 }
 .left{
 float:left;
 width:100px;
 padding:20px;
 padding-bottom:9999em;
 margin-bottom:-9999em;
 background:#555555;
 }
 .main{
 float:left;
 width:500px;
 padding:20px;
 background:#bbbbbb;
 }

Mi .contenedor, sin height seteado, con overflow:hidden. Esto es importante porque en el .left seteamos un padding-bottom monstruoso, extremadamente grande, junto con un margin-bottom negativo igual de grande, que lo único que harán será estirar el contenido de ese div hasta el final de .contenedor, pero como el mismo tiene la instrucción de «ocultar» todo lo que sobresalga de él, nunca vemos el excedente.

Mejor es ver el html, no?