La dimensión temporal en Internet

Hace tiempo que me apetecía hacer una pequeña reflexión sobre "la dimensión temporal" en Internet, o "lo rápido que pasan las cosas" en Internet.

Yo crecí oyendo que el mundo de la tecnología era como los perros: 1 año equivalía a 7. En el mundo de Internet, eso llegó a ser real... pero ahora ya no es válido.

Con la llegada de la "Web 2.0" y sus redes sociales, los blogs, las RSS, etc. la proporción tiempo real/tiempo en Internet es tremendamente despropocionada.

En nuestra oficina, cuando pasa 1 hora desde que ha salido una noticia y la comentas, te miran con cara rara, como diciendo... "pero tú en qué mundo vives, eso ya lo sabíamos".

Hace poco estuve una semana de vacaciones en Linares de Mora (Teruel - España) consiguiendo lo que quería: absoluta tranquilidad, mucha naturaleza, poca TV y nada de PC/Internet... y ¡lo conseguí!

Cuando volví parecía el hombre desactualizado. Miles de cosas habían pasado, muchas pequeñas, pero otras tan gordas como las presentaciones de OpenSocial y Android o el nacimiento de songza.

El mundo de Internet es absolutamente frenético. Un vídeo en YouTube puede tener decenas de miles de visitas en un día, y al próximo fin de semana todos tus amigos lo han visto y lo consideran como "viejo" o "antiguo". Diariamente nacen grandes portales, que todos visitamos alguna vez, pero pocos nos quedamos a verlo.

El mismo Google es un gran ejemplo. En 1996 aún no tenían dominio propio. En 1998, sólo trabajaban 40 personas. En 2007 ya es el absoluto dominador de Internet.

Microsoft no se queda atrás... nace a principios de los 80, y en 2007 es tan omnipresente y antigua como la rueda.

Otro gran ejemplo es Panoramio. Nació en 2005, y en menos de dos años ya formaban parte de Google...

Otro ejemplo: ASP.NET 2.0 salió a la luz en Noviembre de 2005... no han pasado dos años y ASP.NET 3.5 vuelve a revolucionar todo el mundo de la programación Web.


Con todas estas divagaciones, quiero expresar que el mundo de Internet es increíblemente frenético. ¡¡Jamás había pasado esto en la historia de la humanidad!! Imagináos a los alumnos de una clase de historia dentro de 100 años... estudiarán cómo la Edad Media duró casi 1000 años, la revolución industrial 100 años, el PC revolucionó el mundo en 20 años, Internet se hizo imprescindible a los 5 de nacer...

¿Qué pasará si seguimos a este paso?, ¿Hay algún límite?

Menos mal que, actualmente, nuestros gobernantes y los dueños de las grandes empresas son todavía ajenos a la era digital. Es repugnantemente común que algunas empresas mantengan los mismo servidores que hace 10 años, y prefieran reparar un viejo PC a comprar uno nuevo (siendo que el trabajo diario depende de éste).

Por eso, los grandes emprendedores con "mentalidad 2.0" se están comiendo el mundo.


Sólo una cosa, todo esto que estoy comentando no es una queja... todo lo contrario: me encanta.



Kit de mejora: Improvement Kit

Hace un par de días escribía un artículo sobre cómo cambiar el Theme con javascript. En uno de sus párrafos, comentaba uno de las cosas a mejorar del App_Themes.

Describiendo un poco la funcionalidad del App_Themes, básicamente consiste en un directorio especial llamado "App_Themes" dentro del que se crea uno o varias carpetas. Cada una de ellas será un "Theme" particular.

La funcionalidad de esas carpetas es centralizar los estilos de toda la Web con imágenes, estilos .css, estilos .skin... realmente lo que hace es algo complejo, pero vamos a centrarnos en una de esas funcionalidades que yo mejoraría.

Es bastante típico que en el Theme haya varios archivos ".css", de modo que ASP.NET se encarga de añadir el correspondiente elemento "link" en el head de las páginas que definan ese Theme de forma automática.

El problema viene en ese momento: a ningún navegador le gusta que se linkeen varios archivos ".css". El que haya varios implica varias conexiones, y es bastante común que éstas no sean simultáneas, por lo que en ocasiones estamos bajando un fichero ".css" mientras otro está esperando.

Por otra parte, los ficheros ".css" contienen espacios, saltos de línea y comentarios que no son, para nada, necesarios para el navegador, aunque sí para el que está desarrollando esos estilos (si no, sería ilegible).

Conclusión: lo ideal sería que todos los archivos ".css" se juntaran en uno sólo y que, además, éste estuviera comprimido.

Así que, irremediablemente me puse a pensar e hice un HttpModule, de modo que una vez instalado, no hay más que que trabajar como siempre con estilos separados en ficheros, etc. Es el HttpModule el que se encarga de juntar todos los ficheros en uno y comprimirlo.

En líneas generales, su ciclo de vida es:
  • Si no hemos hecho cambios en los ".css" desde la última vez, no hacer nada (esto se hace una variable de cache y un CacheDependency).
  • Si hemos hecho cambios, y por tanto debemos (re)generar el fichero comprimido:
    • Cogemos todos los ficheros del App_Themes y lo metemos en una variables
    • Hacemos la compresión básica: quitar espacios, saltos de línea y comentarios.
    • Creamos el fichero comprimido.
  • En ambos casos, recorremos el HTML resultante y le quitamos los "link" que apuntaban a los diferentes ficheros del Theme, sustituyéndolo por nuestro propio "link" apuntado al fichero comprimido.

Cuando terminé de hacer el código, me di cuenta de que la estructura también me servía para hacer lo mismo con los ficheros javascript... y también con el propio HTML de la página resultante... Así que la solución fue crear un nuevo proyecto en Codeplex (y con éste van cinco) para mostrar lo que había hecho y animar a cualquiera a que me pudiera ayudar añadiendo nuevas cosas.

El proyecto se llama ImprovementKit, y su código podéis utilizarlo libremente como queráis (subgurim.net ya lo implementa). El propio código lleva una página de ejemplo, por lo que os será fácil instalar el módulo.

Desde aquí os hago un llamamiento a que participéis en el proyecto. A lo mejor os apetece que trabajemos juntos en algo, y este proyecto es ideal, porque es bastante sencillo, bastante mejorable y bastante interesante



Miembro cualificado del Google Advertising Professionals

He de confesarlo: todo lo que hace Google lo tengo que conocer. Me pasó con los mapas de google (y así nació google maps para ASP.NET), me pasó con GMail y el modo en que se adjuntaban los ficheros a los mails (así nació el FileUpload AJAX), me pasó con el protocolo sitemap que Google propuso (de ahí viene el Sitemap generator para ASP.NET), me pasó con el GTalk integrado en GMail (lo que me animó a hacer el chat para ASP.NET), me pasó con Adsense (todas mis páginas grandes lo tienen) y un largo etcétera.

Será por el "Don't be Evil" que Google representa, pero me encanta. En la oficina (un saludete a Javi Riera) me llaman GoogleMan, jeje

Bueno, pero fuera de bromas, una de las cosas que me quedaba pendiente era el mundo Adwords. Para los que no lo conozcan, Adwords es la parte en que los anunciantes gestionan sus anuncios tanto para la red de Google (buscador de Google, GMail, GoogleMaps, etc.) como para su red de contenido (todas las páginas que tienen Adsense, como por ejemplo ésta).

Mis Webs han tenido anuncios Adsense desde que éste nació, por lo que lo conozco bastante bien... pero para conocerlo mucho mejor, vale la pena meterse en el mundo de Adwords: el del anunciante.

Así que los ojos me hicieron chiribitas cuando en la empresa se comentó que estaría bien tener un par de personas certificadas como "miembros cualificados del Google Advertising Program", así que mi compi David Zaragoza y yo nos pusimos manos a la obra.

Ser miembro cualificado no es, para nada trivial. Aquí se comenta qué hace falta para obtener el certificado.

Lo primero que hay que hacer es estudiarse por completo el temario propuesto en el centro de aprendizaje... y si a primera vista te parece fácil, ¡¡te aseguro que no lo es!! Tras habértelo estudiado a conciencia, al mismo tiempo que practicabas con el programa de Adwords real, debes hacer un examen tipo test, de 100 preguntas y hora y media de tiempo.

Tanto David Zaragoza como yo somos ingenieros superiores de telecomunicaciones por la Universidad politécnica de Valencia, con lo que si tú también lo eres, o tienes una ingeniería similar, lo siguiente que voy a decir te sonará... y mucho:
  • Los test de aprendizaje son muchísimo más fáciles que el test del examen.
  • Aunque previamente se dice expresamente que el examen tiene 100 preguntas... es mentira, tiene 115.
  • A pesar de ello, el tiempo es el mismo: hora y media.
  • No se resta por pregunta fallada... pero da igual, se aprueba con un 75%.
  • Como es un test traducido del inglés, algunas preguntas no tienen ningún sentido. Por ejemplo te preguntan sobre el "multiplicador de pujas", algo que no se explica en ningún sitio, ni tan siquiera buscando el Google... menos mal que "bid multiplier" sí que existe.
  • Algunas preguntas están incompletas.
  • Algunas respuestas están incompletas.
  • Algunas preguntas requieren respuesta múltiple... y a veces ni te dicen cuántas respuestas has de marcar.
  • Cuando pasas de una pregunta a otra, se cambia el tipo y el tamaño de la letra.
  • Aunque no lo pueda demostrar, estoy seguro de que algunas preguntas tienen las respuestas correspondientes a otras preguntas.

Así que desde aquí protesto por ello a Google. Ese examen es demasiado importante como para tener tal cantidad de errores... lo bien que hacen algunas cosas y lo mal que hacen otras.

Por cierto, tanto David como yo aprobamos el examen , así que sólo nos queda cumplir el resto de requisitos y seremos miembros certificados



Cambiar el Theme dinámicamente... con javascript

Es muy posible que en alguno de vuestros proyectos hayáis tenido la necesidad de cambiar el Theme de forma dinámica.

Lo típico es poner una lista desplegable con el listado de Themes y que al seleccionar uno u otro hiciérais un PostBack para saber qué Theme había elegido el usuario y cambiárselo. En codeproject hay un interesante artículo al respecto.

Sin embargo, como siempre, el problema de los PostBack es la -ya desfasada- recarga de página.

Resulta ser que la funcionalidad de los App_Themes de ASP.NET es una de las cosas más sencillas que hay (de hecho tengo en mente una mejora de lo que hace ASP.NET, pero esa ya es otra historia), de modo que el resultado final no deja de ser los típicos elementos "link" con su atributo "href" correspondiente, ubicado dentro del elemento "head" de nuestra página (podéis echarle un vistazo al código fuente de esta misma página).

Sabiendo esto, y con un poquito de javascript, no resulta demasiado difícil conseguir el objetivo de este artículo: cambiar el Theme de forma dinámica sin recargar la página.

El código de nuestra página es el típico de:

.aspx
        <asp:DropDownList ID="DropDownList1" runat="server" onchange="ddl_SelectedIndexChange(this)" />

.aspx.cs
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            InitilizeControl();
        }
    }

    private void InitilizeControl()
    {
       // Si no hay un Theme, desactivamos el control
        if (string.IsNullOrEmpty(Page.Theme))
        {
            Visible = false;
            return;
        }

        DropDownList1.Items.Clear();
       // Rellenamos la lista desplegable con todos los Themes que tengamos en nuestro sitio Web
        foreach (string theme in GetThemes())
        {
            DropDownList1.Items.Add(theme);
        }

       // Dejamos elegido el Theme que estamos usando.
        if (DropDownList1.Items.FindByValue(Page.Theme) != null)
            DropDownList1.Items.FindByValue(Page.Theme).Selected = true;
    }

    // Recoge un listado de los Themes de nuestro sitio web
    public static StringCollection GetThemes()
    {
        DirectoryInfo dInfo = new DirectoryInfo(HttpContext.Current.Server.MapPath("~/App_Themes"));
        DirectoryInfo[] dArrInfo = dInfo.GetDirectories();
        StringCollection list = new StringCollection();
        foreach (DirectoryInfo sDirectory in dArrInfo)
        {
            list.Add(sDirectory.Name);
        }
        return list;
    }


Hasta aquí todo muy sencillo, y probablemente ya hayáis hecho algo similar. De hecho, para mejorar la eficiencia, se puede obviar el GetThemes y meter nosotros mismos los Theme a mano.

La "magia" la realiza el javascript que viene a continuación:

.js
        // Recoge el valor seleccionado y llama a la función principal
         function ddl_SelectedIndexChange(ddl)
        {
            theme = ddl.options[ddl.selectedIndex].value;
            changeTheme(theme);
        }
               
        function changeTheme(theme)
        {
           // Recoge todos los elementos "link"
            var links = document.getElementsByTagName('link');
           
           // Los recorremos uno por uno
            for (i = 0; i< links.length; i++)
            {
                link = links[i];
                themeConst = 'App_Themes/';
                hrefTheme = link.getAttribute('href');

                oldThemeAux = hrefTheme.substring(hrefTheme.indexOf(themeConst) + themeConst.length);               
               
                // Tras simples funciones javascript, obtenemos el Theme que teníamos activo hasta el momento
                oldTheme = oldThemeAux.substring(0, oldThemeAux.indexOf('/'));
               
                // Conseguimos el nuevo Theme
                newTheme = hrefTheme.replace('/'+oldTheme+'/', '/'+theme+'/');

                // Y finalmente asignamos el nuevo Theme.
                link.setAttribute('href', newTheme);
            }
        }


Realmente sencillo, ¿no?

Sí, sencillo, pero limitado. Con esto sólo conseguimos cambiar el Theme una vez. Si el usuario recarga la página, no se guarda la selección hecha. Pero eso otra cosa



Botón que sólo se puede clicar una vez

Una funcionalidad que he necesitado siempre mucho y que por un motivo u otro nunca había implementado, es la de hacer que cuando un usuario clique sobre un botón, éste se deshabilite para que no se pueda volver a hacer clic en él.

La gota que colmó el vaso ocurrió cuando comprobé que en Todoexpertos.com había una gran cantidad de usuarios que hacían clic sobre el botón de "enviar pregunta" o "enviar respuesta", hasta dos y tres veces. Naturalmente, ya habíamos implementado una funcionalidad que evitaba que un mismo item se insertara varias veces. Esta funcionalidad creaba un log informando algo parecido a "Intento de inserción de una pregunta ya creada".

Pero... ¿pudiendo evitar que ocurriera en cliente, por qué hacerlo en servidor?

Pues eso, vale ya de tanto rollo y vayamos al tema. Hacer que un botón se deshabilite en el momento se hace un clic, no es tan trivial como puede parecer. Básicamente hay que tener dos cosas en cuenta:
  • No hay que deshabilitar el botón si estamos haciendo una validación y esa validación no produce resultado positivo.
  • El botón NO puede ser un input-type-submit. Debe ser un input-type-button. Es curioso: si es submit, sí que funciona todo bien en Firefox, pero no funciona en Internet Explorer... y por primera vez en mucho tiempo, estoy de acuerdo con IE... pero eso ya lo explicaré en otro momento.
Teniendo ambas cosas en cuenta, pongámonos en esta situación:

.aspx
        <asp:TextBox ID="TextBox1" runat="server" ValidationGroup="SubgurimTest"></asp:TextBox>
       
        <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
            ErrorMessage="*" ControlToValidate="TextBox1" ValidationGroup="SubgurimTest">
        </asp:RequiredFieldValidator>
       
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="OnceClickMe" ValidationGroup="SubgurimTest"
            UseSubmitBehavior="false" OnClientClick="clickOnce(this, 'Cargando...')" />
           
        <br />
       
        <asp:Label ID="Label1" runat="server" Text=""></asp:Label>



.aspx.cs
    protected void Button1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000);
        Label1.Text = DateTime.Now.ToString();
    }


Como vemos, no se trata más que de un Textbox, un Button y un Validator. Al presionar sobre el Button, siempre y cuando se supere la validación, se ejecutará el método "Button1_Click" que esperará un segundo y asignará un valor a una Label.

Cabe destacar que el botón debe tener el atributo UseSubmitBehavior a false (esto hará que el Button se convierta en el input-type-submit que queremos), así como el OnClientClick enlazado a la función javascript que será la que haga la magia.

Y la función javascript es la siguiente:

        function clickOnce(btn, msg)
        {
            // Comprobamos si se está haciendo una validación
            if (typeof(Page_ClientValidate) == 'function')
            {
                // Si se está haciendo una validación, volver si ésta da resultado false
                if (Page_ClientValidate() == false) { return false; }
            }
           
            // Asegurarse de que el botón sea del tipo button, nunca del tipo submit
            if (btn.getAttribute('type') == 'button')
            {
                // El atributo msg es totalmente opcional.
                // Será el texto que muestre el botón mientras esté deshabilitado
                if (!msg || (msg='undefined')) { msg = 'Loading...'; }
               
                btn.value = msg;

                // La magia verdadera :D
                btn.disabled = true;
            }
           
            return true;
        }







Controles Web de Subgurim en OpenSource

Por fin he tenido tiempo para convertir cuatro de mis proyectos a OpenSource y publicarlos en CodePlex.

Esto quiere decir que podéis acceder al código fuente para curiosear, aportar mejoras, reportar bugs, hacer sugerencias, etc. Sobretodo sería muy interesante que os animarais a haceros desarrolladores de algunos de esos proyectos, así podríamos trabajar mano a mano

Los proyectos de los que hablo son:
- FileUpload AJAX.
- Chat.
- Sitemap Generator.
- Multisearch API.

A mí sobretodo me gusta el FileUpload AJAX, pues tiene un potencial increíble y no hay nada parecido ni tan fácil de usar.

Permitidme que os diga que estos proyectos los comencé hace muchísimo tiempo, y, además, los he tenido que hacer bastante rápido y sin demasiado tiempo. Por tanto, puede que el código que os encontréis no sea una maravilla... no me lo tengáis demasiado en cuenta .

Es mi primera experiencia OpenSource, y la verdad es que no sé si va a resultar un completo "desastre" (que nadie se interese por ayudar a mejorar) o un éxito (crear una interesante comunidad de desarrolladores), pero si funciona bien, tengo varias ideas en mente que podría llevar a cabo junto con la comunidad Open Source.



Mostrar un entero con separador de miles

He de confesarlo: he vuelto a necesitar un entero con su separador de miles... lo he hecho un montón de veces, ¡¡pero siempre se me olvida!!

Así que me ha tocado pasar unos largos 15 minutos buscando cómo lo había hecho antes. Ahora ya no se me va a olvidar: cadenas con formato numérico estándar.

Y para ser consecuente con el título del miniartículo, mostremos en una Label el entero con su separador de miles:
int N = 25368;
Label1.Text = N.ToString("N0");
Como véis, lo que se debe hacer es pasar como parámetro al "ToString" la letra "N" que es la que indica que queremos que se muestre en formato "Number" (es decir, con nuestros deseados separadores de miles), y el 0 (cero, el número, no la letra O ) que es el que indica el número de decimales.

Sí, aunque sea un entero, con el ToString se sacan decimales. De hecho, por defecto te saca 2.




TusEncuestas 2.0

Desde hoy mismo comienzo a trabajar con la segunda versión de Tusencuestas.com

Hacer una web de encuestas es algo que tengo en mente desde hace muchos años. Hace casi año y medio cumplí mi "sueño" y creé tusencuestas.com. La lógica de la aplicación está preparada no solo para encuestas, sino también para cuestionarios, valoradores, etc... sin embargo me parece que voy a centrarme únicamente en las encuestas, y más adelante ya veremos.

Para los que no conozcáis la web, tusencuestas.com es una aplicación Web desde la que cualquier puede crear encuestas y ponerlas en su Web. Es gratuito, 100% AJAX, no tiene publicidad, es multiplataforma (como se basa en javascript, da igual que tengas ASP.NET, PHP, JSP, o simple HTML), no tiene límites de número de preguntas, respuestas, tiempo de publicación, etc. Además, desde que conoces la página hasta que tienes un sistema de encuestas en tu Web no pasan más de 3 minutos, pues su configuración es tremendamente sencilla.

No quiero pecar de prepotencia, pero en mi más sincera opinión, tusencuestas.com es el mejor sistema de creación y gestión de encuestas que hay en la red. Sólo tenéis que probarlo. Que yo sepa, ningún otro sistema de gestión de encuestas es gratuito, AJAX y encima sin publicidad. Pero realmente eso no es más que la punta del iceberg. De hecho, pienso que es más fácil crear encuestas en tusencuestas.com que tener un sistema propio, sobretodo si tienes varias Webs, porque con el sistema de perfiles (similar al de GoogleAnalytics) puedes gestionar rápidamente encuestas de varias Webs.

Los otros sistemas de gestión de encuestas suelen ser más caros, algunos son AJAX, algunos no tienen límites, algunos son gratuitos... pero ninguno (que yo conozca) tiene todas las cosas a la vez. Y estoy hablando tanto de castellano como de inglés.

Sin embargo, aún queda muchísimo por mejorar y de ahí que me vaya a poner a trabajar en una nueva versión. Algunas pinceladas de lo que voy a hacer:
1.- Corregir pequeños bugs, sobretodo relacionados con la codificación.
2.- Mostrar cuando se está cargando (ya sé que tendría que estar hecho...)
3.- Llevar la configuración al máximo. Hasta ahora, casi todo se puede configurar: colores, tiempo de duración de la encuesta, se pueden quitar y poner preguntas, etc. Pero irá más allá, y se podrá configurar incluso si en el botón debe poner "Votar" o debe poner "Vótame!", o si debe poner "Cargando" o "Loading", "Mostrar respuestas" o "Show answers".
4.- Un diseño más enfocado a la Web 2.0. Como nos pasa a todos los programadores natos, el diseño siempre está por debajo del potencial de la lógica de la aplicación. Trataré de hacer todo más sencillo y navegable, que se sepa qué se puede hacer en cada momento con un sólo vistazo.
5.- Que se puedan archivar las encuestas en .csv. Me lo pidieron en el foro y me parece correcto.
6.- Socialización del portal... buf, esto es difícil de explicar, pero sin duda será el mayor cambio. La idea es que cualquiera puede crear un encuesta pública, de forma que otros puedan tener esa encuesta en su Web. Mejor con un ejemplo:
  • Por una parte están los que crean una encuestas públicas en el "canal" de fútbol.
  • Por otra parte están decenas de webmasters con webs de temática futbolera.
  • Esos webmasters podrán coger el código del "canal" fútbol, de forma que cada semana tienen una encuesta sobre su temática, que recibe un montón de votos.
Cuando desarrolle mejor la idea lo veréis mejor.
7.- Servicio premium. La web siempre tendrá máxima funcionalidad de forma gratuita. Sin embargo hay algunos servicios que se deben hacer premium pues implican un coste. Por ejemplo los votos que requieren el email para votar, o la protección de votos Spam no sólo por cookies sino también por IP.
8.- Hacer la Web multiidioma... esto me gusta, sobre todo teniendo en cuenta lo mucho que ha triunfado en Webs como mi control de GoogleMaps, que ya está traducida a 9 idiomas (y subiendo).

Pero bueno, ya no digo nada más. Me esperan varias semanas de trabajo, así que ya os contaré cuendo termine