[Tips] Como cambiar el ícono del cursor en una página web.

Una de las consultas que me han hecho es ver como se puede cambiar el cursor del mouse en una página web, tal cual lo hacen el Cut the Rope , el excelente juego hecho en HTML5 bajo IE9, si , como escuchas IE9 Sonrisa, puedes verlo en http://www.cuttherope.ie/

Vamos a copiar los cursores de la app cuttherope, para eso voy a ocupar el depurador de IE9, que obtenemos presionado f12

image

Busqué los archivos cur y obtuve los  archivos handcursor.cur y cursor.cur

image

<!DOCTYPE html>
<html>
       <head>
          <title>Ejemplo de Cursor</title>
          <style type="text/css">
              body{
                  cursor:url(cursor.cur);
               }

               #cara{

                 cursor:url(handcursor.cur);

               }

       </style>

       </head>

       <body>

           <h1>Trabajo con Cursor </h1>

           <img  id="cara" src="cara1.png" />

       </body>

</html>

Se ve que eo body tiene el cursor (cursor.cur) y la imagen el cursor handcursor. Ahora bien, al momento de ejecutar la página en un servidor web (no localmente como archivo html), vemos el resultado que presento en el siguiente video.

Espero que te sirva!, y de repente , quien sabes, te animas a hacer un juego Sonrisa

Saludos!!
@chalalo

[WP7 Tips] Guardar lista de objetos en el IsolatedStorage

Hola que tal, otro tips más para el desarrollo sobre Windows Phone. Les cuento a manera de historia lo que me paso.

Tenía la necesidad de guardar en una lista de objetos los resultados obtenidos en el Pseudo juego del que estaba hablando en los post anteriores, los cuales puedes ver acá:

La pantalla de “Logros” es la siguiente:

image

El tema es el siguiente, para guardar una par key-value, funcionaba impecable, revisemos como se guardar un valor y como podemos recuperarlo posteriormente. Primero para guardar un valor lo podemos hacer de la siguiente manera:

IsolatedStorageSettings.ApplicationSettings["clave"] ="algun valor";

o que es lo mismo:

IsolatedStorageSettings.ApplicationSettings.Add("clave", "algun valor");

Lo que se guardar, como decíamos es un par key-value, en donde el value puede ser un objeto.

Para recuperar el dato almacenado, primero debemos preguntar si existe la clave en la colección ApplicationSettings, lo podemos hacer de la siguiente manera:

if (IsolatedStorageSettings.ApplicationSettings.Contains("clave"))

{

  var valor =IsolatedStorageSettings.ApplicationSettings["clave"];

}

Recuerda que el objeto que se retorna no es tipado, por lo que vas a tener que hacer el cast explícito al lo que desees obtener.

Todo funcionaba perfecto, pero cuando quería guardar una lista de objetos, funcionaba en el emulador sin problemas, pero en el dispositivo no se almacenaban los datos, y yo que pensaba que mi aplicación estaba a minutos de poder ser publicada, asi que comencé la búsqueda de mi error.

La lista de objetos almacenaba puntajes, que tiene la siguiente estructura:

image

Que como vez, ocupo la propiedad cara para tener el nombre de la imagen que muestro, el constructor sirve para determinar mediante el puntaje que ruta de imagen se va a almacenar el cara. Además solo quería guardar los 10 últimos puntajes.

public void guardaResultados(int cont)
{

  List<Puntaje> puntajes = new List<Puntaje>();

  Puntaje p = new Puntaje(DateTime.Now.ToString("dd/MM/yyyy hh:mm tt"),
                          Convert
.ToString(cont));

  if (IsolatedStorageSettings.ApplicationSettings.Contains("puntajes"))

  {

      puntajes = (List<Puntaje>)IsolatedStorageSettings.ApplicationSettings["puntajes"];

   }

   puntajes.Insert(0,p);

   if (puntajes.Count > 10)

   {

      puntajes.RemoveAt(10);

    }

   IsolatedStorageSettings.ApplicationSettings.Remove("puntajes");

   IsolatedStorageSettings.ApplicationSettings.Add("puntajes", puntajes);

   IsolatedStorageSettings.ApplicationSettings.Save();

 

}

Como se ve mi función en sencilla, en donde primero creo la lista de Puntaje, instancio el Objeto Puntaje , luego pregunto si ya existía una lista en el ApplicationSettings, si no existe es que se estaba jugando por primera vez, si no la recuperarnos para ingresarle el nuevo objeto p, insertamos en la primera posición, y si hay más de 10, la removemos.

Luego remuevo lo que estaba ya en la colección ApplicationSettings ,esto lo hice por que me levanto una excepción al querer guardar algo que ya tenia la clave, entonces, luego guardo la lista. El método Save no lo estaba ocupando, lei en la documentación que era el método que debía ocupar para guardar la lista, así que lo utilicé pero se levantaba una excepción que indica lo que estaba pasando, debía serializar las propiedades de mi objeto.

Así que rápidamente agregué en la clase Puntaje el namespace:

using System.Runtime.Serialization;

Luego agregué el atributo a la clase Puntaje:

[DataContract]

    public class Puntaje …

y a las propiedades el respectivo DataMember

[DataMember]

   public string cara { set; get; }

Ejecuté ahora mi aplicación en el teléfono y super! ya estaban funcionando la lista de puntajes de manera persistente, ya no se borraban en cada ejecución, justo lo que necesitaba.

Espero que te sirva este tip.

Saludos

@chalalo

Estudiantes de Chile, Gran oportunidad para ganar una Xbox desarrollando para Windows Phone 7!!

image

¿Quieres ganar una Xbox 360 con Kinect?
Tienes hasta el 29 de Abril para publicar al menos una aplicación de Windows Phone 7 en nuestro Marketplace.
Recuerda que como Estudiante tienes la posibilidad de registrarte en el Marketplace y utilizar las herramientas absolutamente sin costo.
¡Es muy sencillo programar y publicar aplicaciones! Sólo sigue los siguientes pasos:

image imageimage
image imageimageimage
image

image 
t-laurar@microsoft.com y ya estarás participando!

image

Vamos muchachos, a participar!!!

@chalalo.

Todos los links que necesitas de Windows 8 :)

Descarga para Desarrolladores

Descripción

URL

Windows 8 Consumer Preview Download

http://preview.windows.com

Descargas útiles para desarrollo bajo estilo Metro

http://msdn.microsoft.com/windows/apps/br229516

Guía de Diseño de Metro

http://design.windows.com

Contenido de desarrollo de aplicaciones estilo Metro

Descripción

URL

Home Windows Dev Center

http://dev.windows.com

Home de desarrollo de aplicaciones estilo Metro

http://msdn.microsoft.com/windows/apps

Guía de productos para desarrolladores

http://msdn.microsoft.com/windows/apps/hh852650

Documentación Oficial

http://msdn.microsoft.com/en-us/library/windows/apps/

Recursos de Diseño

http://design.windows.com

Información sobre la venta de Apps y Windows Store

http://msdn.microsoft.com/library/windows/apps/br230836

Descarga para desarrollo para aplicaciones estilo Metro 

http://msdn.microsoft.com/windows/apps/br229516

Ejemplo de Aplicaciones estilo Metro

http://code.msdn.microsoft.com/windowsapps/

Foro de desarrolladores

http://forums.dev.windows.com

Blogs para desarrolladores

Blog

URL

Building Windows 8 blog

http://blogs.msdn.com/b/b8/

Blog Windows Store para  developers

http://blogs.msdn.com/b/windowsstore

Windows 8 app developer

http://blogs.msdn.com/b/windowsappdev

IE blog

http://blogs.msdn.com/b/ie/

Visual Studio

http://blogs.msdn.com/b/visualstudio/

Windows Blog

http://windowsteamblog.com/

Canales en las redes sociales para desarrolladores.

Canal

URL

Facebook (developer)

http://fb.windows.com/developers

Twitter (Building Windows 8)

http://twitter.com/BuildWindows8

Twitter (Windows Dev Center)

[Tips WP7] Abrir ventana del Browser y compartir en redes sociales

Hola, como ya les había comentado , voy a estar compartiendo algunos tips que me sirvieron bastante al momento de comenzar a desarrollar algunas funcionalidades.

Abrir ventana del Browser

Una de las funcionalidades que tiene la app que hice, aunque sea muy poco visible , es que al hacer tap sobre @chalalo, voy a abrir una ventana del explorador a la versión móvil de de la pagina de twitter apuntando a mi cuenta Sonrisa

image

Entonces primero importamos:
using Microsoft.Phone.Tasks;

Para luego asociar al texto TextBlock su handler:

private void tx_chalalo_Tap(object sender, GestureEventArgs e)

{

WebBrowserTask webbrowser = new WebBrowserTask();

webbrowser.Uri = new Uri("http://mobile.twitter.com/chalalo");

webbrowser.Show();

}

Y listo!

Compartir en redes sociales

Una de las funcionalidades que quería era compartir los logros en las redes sociales, primero comencé a revisar el código para ver alguna API de conexión hacia Twitter, después de leer me di cuenta el sdk de WP7 provee todo lo necesario para poder conectarnos a todas las redes sociales que están registradas en el teléfono.

image

Entonces, fue muuuy sencillo, primero  importamos:
using Microsoft.Phone.Tasks;

Para luego asociar al botón el handler

private void Button_Click_2(object sender, RoutedEventArgs e)

{

    ShareStatusTask shareStatusTask = new ShareStatusTask();

    shareStatusTask.Status = "He obtenido " + cont + " puntos! en el Juego Dead Dedo para Windows Phone 7, hell yeah!";

    shareStatusTask.Show();

}

Vemos que la propiedad Status el texto que se va a compartir.,

Luego aparecerá la pantalla para compartir el mensaje

image

Ten en cuenta que no va aparecer en el emulador, pero en el dispositivo físico no hay problema Sonrisa, genial!, muy fácil.

 

Ahh! y que no se me olvide, la primera vez que subí una app al store, fue rechazada por que los screenshot que tomé tenían los datos de debug, (que son los números que están al lado derecho del emulador), lamentablemente estos números aparecen en las imágenes.

image

Para desactivar estos números simplemente vas al archivo App.xaml.cs y comentas la siguiente línea:

//Application.Current.Host.Settings.EnableFrameRateCounter = true;

Y listo! ahora si podemos subir los screenshot sin que los rechacen!
Saludos!

En breve, más tips Sonrisa
@chalalo

[WP7] Crónicas de un Aprendiz de WP7 – The Beginning

Hola a todos, que tal? Acabo de subir mi sexta aplicación al Maket Place, ninguna es una gran maravilla, pero fueron parte de mi aprendizaje en el desarrollo de aplicaciones para wp7. Quiero compartir los tips y funcionalidades que anduve buscando en la documentación para poder hacerlo. Me gustaría también en el futuro, y si es que prende la idea en los lectores de este blog, hacer comparaciones entre el desarrollo de Android y WP7. (ya que antes de WP7, desarrollaba y aún desarrollo para Android)

En este proceso de aprendizaje, he creado un pseudo- juego que tiene que ver cuantas veces eres capaz de apretar una calavera en 33 segundos, la app  aún  está  en etapa de evaluación, las funcionalidades son obvias, dentro de los plus son:

  1. Compartir tu puntaje en las redes sociales registradas en el teléfono.
  2. Agregar sonido en Silverlight
  3. Hacer deploy con tipo de letra personalizado
  4. Guardar listas de objetos IsolatedStorageSettings
  5. Abrir IE desde la aplicación

Claro que estas son funcionalidades básicas, pero para los que estamos partiendo creo que serán de gran ayuda Sonrisa

Estos son parte de los screenshot de la aplicación, como dije antes, la hice para aprender y poder enseñar, así que de hoy en adelante, aparte de ASP.NET estaré compartiendo los tips Sonrisa

screenshot0screenshot1screenshot2screenshot3

Esto de hacer apps por jugar ha sido el mejor método que he tenido para aprender a desarrollar para una plataforma.

Nos vemos!!
@chalalo

Algunos tips para decidir entre NoSQL y un RDMS

image

Hola, últimamente he estado utilizando MongoDB , estoy muy entusiasmado con este tipo de repositorios, y buscando información acerca de como decidir si usar un RDBMS o un enfoque NoSQL encontré un interesante artículo que es parte de DZone NoSQL Portal. El artículo original está en inglés, voy a intentar traducirlo lo mejor posible.

— Cualquier persona que no vive en una caverna sabe que NoSQL es una tendencia en aumento para dar soluciones de almacenamiento a gran escala. Sin embargo una pieza que falta en todo esto, es una especie de guía sobre como determinar si la mejor solución es NoSQL o un RDBMS tradicional.

Para comenzar, vamos a decir que debes asumir que un RDBMS es probablemente la apuesta más segura. Casi cualquier cosa se puede hacer con un almacén de este tipo, con la excepción de no poder escalar y/o llevar a cabo tareas con la misma performance que una solución NoSQL. Una de las grandes ventajas de las soluciones RDBMS es que ellos ya cuentan con un enorme ecosistema de herramientas, documentación y dba con experiencia.

Dado lo anterior, ¿por que deberíamos entonces, mirar hacia una solución NoSQL? Los siguientes son algunos indicadores que nos pueden servir para decidir que una solución NoSQL puede ser la mejor opción para solucionar un problema:

  • Estamos almacenando simples pares clave/valor. Si nuestra solución RDBMS termina siendo solo una tabla con un par de campos clave y otro CLOB XML, … entonces probablemente estaremos utilizando la herramienta equivocada para ese trabajo.
  • Estamos almacenando estructuras de datos complejas las cuales son no-relacionales. si almacenamos estructuras con datos jerárquicos en donde cada “Maestro” tiene diferentes “Detalles”, sin un esquema estándar, estaremos en problemas con un RDBMS.
  • Necesitamos escalabilidad masiva y la distribución, y la economía de esa escala es importante para nosotros. Muchas soluciones RDBMS ofrecen partición de esquemas y una muy buena escalabilidad, pero a un costo elevado( licencias y gastos de implantación), que generalmente son mucho más costas que escalar con una solución NoSQL.

Bueno, eso, corta y buena Sonrisa

@chalalo

[Evento] Migrando de HTML 4 a HTML 5 sin quebrar la compatibilidad de tus sitios

Hola!, los quiero invitar a un evento que se va a realizar en las oficinas de Microsoft Chile Sonrisa

Migrando de HTML 4 a HTML 5sin quebrar la compatibilidad de tus sitios

Existe un amplio consenso en que HTML 5 es la plataforma de facto para la nueva Web. Cada día vemos como nuevos browsers, smartphones, tablets y e-readers incorporan soporte para ciertas capacidades del estándar en desmedro de los tradicionales plug-ins. Sin embargo, es también una realidad que una parte importante de nuestros clientes todavía visitan nuestros sitios Web a través de tecnologías más antiguas, sin soporte para muchas de las capacidades que hoy ya son parte del estándar. En esta sesión conoceremos el estado actual del estándar HTML 5 y su soporte en Internet Explorer 9, discutiremos las estrategias disponibles para la transición y mostraremos las herramientas de ASP.NET que nos pueden permitir abordar de mejor manera la evolución hacia HTML 5, manteniendo la compatibilidad con la versión anterior del estándar.
Oradores:
Gonzalo Pérez, Hans Nemarich y Pablo Berton.

Microsoft Chile – Sala Farellones y Colorado
Mariano Sánchez Fontecilla 310 Piso 6
Las Condes Santiago Región Metropolitana Chile

Fecha:
Jueves 26 de Abril desde las 18:30 horas a las 21:30 horas.

Regístrate acá
https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032509406&Culture=es-CL

Nos vemos allá!
@chalalo

Tutorial Asp.net Web API con MongoDB – Parte 2.3

Hola!, y seguimos con el tutorial de MongoDB + ASP.NET Web API , apoyándonos siempre en jQuery para la funcionalidad del front-end, los post anteriores son:

Esta vez vamos a realizar la funcionalidad de Editar, que encierra la acción de buscar un registro en particular y luego editarlo.

He hecho unos cambios pequeños al ejemplo que hemos estado desarrollando, el primero es que para mostrar el modificar, voy a agregar en la función de mostrar el código para mostrar el botón Modificar, pensé hacer lo mismo que hice con el botón borrar para referenciarlo, pero wait!, no puedo, debido a que en el botón borrar lo tengo asociado como Id, por lo que voy a crear un atributo data-clave al cual voy a asociar el id en el botón modificar:

function muestraFarmacias(farmacias) {

   var li = "";

   $("#lista").empty();

   var href = null;

   $.each(farmacias, function (index, farmacia) {

       li += "<li>" + farmacia.Nombre +

    "<button  class=’botonEliminar’ id=’" + farmacia.Id +"’>Borrar</button>
     <button  class=’botonModificar’ data-clave=’"
+ farmacia.Id +
                                 "’>Modificar</button></li>"
;

   });

    $("#lista").append(li);

    $("#cargando").empty();

}

El cambio que hice sobre sobre el la acción de click sobre el botón Enviar/Modificar la paso a explicar en breve.

Entonces, manos a la obra Sonrisa , el funcionamiento es mega básico: hacer click en el botón modificar de la lista, vamos a obtener los datos y dejarlos en las cajas de texto para poder modificarlos, el botón que antes decía Guardar , debemos setearlo a Modificar, luego de presionarlo, se debe obviamente modificar el registro y volver al estado inicial, el de ingreso de registros.

image

Veamos la acciones del controlador FarmaciaController que nos van a permitir obtener los datos de una farmacia en particular a partir de su Id y el siguiente, que nos va a permitir modificar un registro.

public Farmacia ObtFarmacia(string id)
{ 
try
{   
    FarmaciaModels fm = new FarmaciaModels();
    Farmacia farmacia = fm.GetFarmacia(id);
    if (farmacia != null)
     {
         return farmacia;
     }
    throw new HttpResponseException(HttpStatusCode.NotFound);
}
catch (Exception){
      
throw new HttpResponseException(HttpStatusCode.InternalServerError);

  }

}

 

 

public HttpResponseMessage PutFarmacia(Farmacia farmaciaMod)

  {

     try

        {

           FarmaciaModels fm = new FarmaciaModels();

           fm.PutFarmacia(farmaciaMod);

           return new HttpResponseMessage(HttpStatusCode.OK);

         }

            catch (Exception)

            {

        throw new HttpResponseException(HttpStatusCode.InternalServerError);

            }

}

Como puedes ver la primera acción la utilizo para obtener una farmacia, su nombre comienza con Obt, y es que para no crear otro mapa de rutas, voy a utilizar el que permite agregar el nombre del controlador, acción e Id.

Para la segunda acción voy a utilizar el verbo Http PUT ,que tiene el significado de agregar o reemplazar, en nuestro caso es claramente reemplazar para ejecutar la acción del updatear el registro.

Revisemos ahora los métodos del modelo, que nos van a permitir realizar las tareas que las acciones exponen:

public Farmacia GetFarmacia(string id)

{

    MongoDatabase db = Db();

    return db.GetCollection<Farmacia>("farmacias").FindOneById(id)
}

 

public void PutFarmacia(Farmacia farmaciaMod)

{

  MongoDatabase db = Db();

  var farmacias = db.GetCollection<Farmacia>("farmacias");

  Farmacia farmacia = farmacias.FindOneById(farmaciaMod.Id);

  farmacia.Nombre = farmaciaMod.Nombre;

  farmacia.Direccion = farmacia.Direccion;

  farmacia.Telefono = farmacia.Telefono;

  farmacia.Comuna = farmacia.Comuna;

  farmacia.Latitud = farmacia.Latitud;

  farmacia.Longitud = farmacia.Longitud;

  farmacias.Save<Farmacia>(farmacia);

}

Destaca la simplicidad de los métodos, el primero para obtener una farmacia en particular, en donde instanciamos una colección de farmacias y sobre ella ejecutamos el método FindOneByID que permite buscar un elemento en la colección de MongoDB mediante el Id especificado para el objeto farmacias. Esto se hace con “carga demorada”  y es una consulta compilada, es decir, no se obtiene toda la colección y sobre ella luego se filtra, si no que se hace la consulta directamente y muy optimizada de parte del motor para obtener un único registro.

Ahora para Modificar una farmacia, lo que hago es pasar el objeto que se va a modificar, el cual viene con el Id original y los datos modificados desde el formulario, para el Id cree una variable oculta (hidden field) dentro del formulario para guardar el valor. Luego obtengo el objeto y sobre escribo sus propiedades y luego lo guardo con el método Save.

Ahora que ya vimos el BackEnd, vamos a ver los métodos en el frontEnd que nos van a permitir llamar a las acciones:

Funciones Javascript, utilizando jQuery por su puesto, la primera para asociar el evento Click sobre el botón modificar, el cual llama a la acción del controlador ObtFarmacia,una vez que se obtienen los datos, se llama la funcion muestraFarmacia, que sirve para mostrar los datos en el formulario y setear el Id en el campo oculto, para tener disponible el Id para ejecutar el update.

    $(".botonModificar").live("click", function () {

            var id = $(this).attr("data-clave");
              $.ajax({
                 url: "http://localhost:51753/Api/Farmacia/ObtFarmacia/"+id,

                 type:"GET",

                contentType: "application/json;charset=utf-8",

                statusCode: {

                    200: function (farmacia) {

                        muestraFarmacia(farmacia);

                    },

                    400: function () {

                        $("#mensaje").text("Error en la Petición");

                    },

                    404: function () {

                        $("#mensaje").text("Datos no Encontrados");

                    },

                    500: function () {

                        $("#mensaje").text("Error en el servidor");

                    } 

                }

            });

        });

 

        function muestraFarmacia(farmacia) {

            $("#Id").val(farmacia.Id);

            $("#Nombre").val(farmacia.Nombre);

            $("#Direccion").val(farmacia.Direccion);

            $("#Telefono").val(farmacia.Telefono);

            $("#Comuna").val(farmacia.Comuna);

            $("#Latitud").val(farmacia.Latitud);

            $("#Longitud").val(farmacia.Longitud);

            $("#Fecha").val(farmacia.Fecha);

            $("#boton").val("Modificar");

        }

Ahora veamos la modificación que se hizo sobre el manejador del Submit del formulario, acá es donde se hizo la modificación, primero definimos una variable verbo, que según el botón esta en estado modificar, será PUT o de caso contrario, será POST, esto es para determinar a que acción del controlador vamos a llamar , PostFarmacia para ingresar o PutFarmacia para actualizar, recuerda que el tema de ingreso y borrado están en los post anteriores.

También seteo una variable acción de manera de poder enviar un msg adecuado al contexto la cual puede ser “Insertar” y “Modificar”.

Una vez que ya ejecutamos la llamada ajax mediante jQuery, limpiamos los elementos input, con lo que la variable oculta que contiene el Id también se limpia, también al botón lo volvemos a su texto original “Guardar”

 

$(‘#formulario’).submit(function (event) {

        var verbo = "POST"

        var accion = "Insertar";

        if ($("#boton").val() == "Modificar") {

            verbo = "PUT";

            accion = "Modificar";

        }

            event.preventDefault();

            $.ajax({

                url: "http://localhost:51753/Api/Farmacia",

                type: verbo,

                contentType: "application/json;charset=utf-8",

                cache: false,

                data: JSON.stringify($(this).serializeObject()),

                statusCode: {

                    200: function (farmacia) {

                        listaFarmacias();

                     

                    },

                    500: function () {

                        alert("Error al " + accion);

                    }

                }

            });

            limpiar();

        });

 

    function limpiar() {

        $(":input").val("");

        $("#boton").val("Guardar");

    }

 

Finalmente el funcionamiento queda de la siguiente manera, recuerda que no hemos hecho nada por el diseño, es solo funcionalidad, y si, de nuevo no arreglé lo de Regitros Triste

Espero que te sirva!, luego seguimos con el tema de seguridad Sonrisa
Saludos

@chalalo

Elegido nuevamente MVP ASP.NET/IIS -2012

En Hora Buena! Sonrisa, he sido elegido nuevamente MVP ASP.NET/IIS, ya son 7 años , y cada año siempre vuelve el nerviosismo de si es que calificaste o no.

El año pasado fue particularmente difícil, pero gracias a Dios, pude concretar proyectos de Webcast, ScreenCast, charlas y artículos. He tratado de responder todos los correos, pido las disculpas por si he olvidado alguno. Siempre digo que el MVP es la consecuencia de lo que me apasiona, no es el el solo hecho de ser MVP.  Debido a que hay un amplia tendencia al mundo del desarrollo móvil, en el cual he estado trabajando bastante, tuve menos tiempo para dedicarle a mi pasión, el desarrollo Web, pero bueno, creo finalmente todo salió bien.De hecho, además de ASP.NET próximamente estaré posteando tips para Windows Phone 7.

También estoy muy contento sobre la noticia de que ASP.NET MVC 4 con Razor y ASP.NET Web API son OpenSource, no lo sabías? mira este artículo, me gusta mucho el OpenSource (aunque algunos crean que no), esto abre muchas nuevas posibilidades de cooperación y mejora de estos  excelentes productos.

Y Bueno, por ultimo agradecer a todos las personas que leen este humilde blog, trato de aportar con lo que pueda servir, muchas gracias!!!

image

@chalalo.