Noticias Weblogs Foros Wiki Código

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin

Variable not found

Procesar peticiones a acciones inexistentes en ASP.NET MVC

Noviembre 29th, 2009 - [Enlace local]

Los controladores ASP.NET MVC que heredan de la clase Controller permiten procesar muy fácilmente las peticiones realizadas a acciones no definidas. Para ello, lo único que hay que hacer es sobrescribir el método HandleUnknowAction() e implementar la lógica que queremos que se ejecute en estos casos.

En el siguiente código, las peticiones realizadas a /Home/Index y /Home/About serán procesadas normalmente, pero /Home/BeberCerveza  será procesada por HandleUnknowAction, cuya  implementación mostrará la vista “Index” con un mensaje personalizado:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";
        return View();
    }
 
    public ActionResult About()
    {
        return View();
    }
 
    protected override void HandleUnknownAction(string actionName)
    {
        ViewData["Message"] = "¿Estás intentando " + actionName + "?";
        View("Index").ExecuteResult(this.ControllerContext);
    }
}

image

Publicado en: Variable not found.



» Leer más, comentarios, etc...

Ingenieria de Software / Software Engineering

Project 2010 Beta

Noviembre 28th, 2009 - [Enlace local]

Ya post atrás había hablado de Project 2010, bien pues se ha liberado la versión Beta, la cual puedes descargar del siguiente sitio

http://www.microsoft.com/project/2010/en/us/default.aspx?cid=RXEML2010BETA

» Leer más, comentarios, etc...

HardBit

Estandares Web MX

Noviembre 28th, 2009 - [Enlace local]

Basta ya de tablas sobre tablas. Este post es para presentales el proyecto que  hace poco iniciaron @juwe, @DiegoHerrera y @FullMental, llamado Estándares Web México

¿Pero que es?

Cito textualmente del sitio

EstandaresWeb.com.mx nace con la intención de promover en México el uso y enseñanza de los Estándares Web, buscando que se construyan más sitios que los cumplan, aumentando potencialmente la inversión realizada en ellos, a través de garantizar el acceso desde distintos tipos de dispositivos y plataformas, posicionamiento en Internet a través de optimización para motores de búsqueda -SEO, Search Engine Optimization-, accesibilidad, facilidad de mantenimiento y disminución de inversión en ancho de banda, entre otras características.

Creo que este tipo de iniciativas son muy interesantes, ya que es bien sabido que las aplicaciones y sitios web cada día toman mas relevancia en la vida diaria, así que, que mejor que tener conocimiento sobre estándares para tener una mejor experiencia como usuarios y saber como ofrecerla como desarrolladores/diseñadores.

» Leer más, comentarios, etc...

4 bits blog

Los modelos de django

Noviembre 27th, 2009 - [Enlace local]

En el anterior post de introducción a django, no se usaron bases de datos, así que en este se verá cómo trabaja django con las bases de datos, es algo realmente sencillo.

Los modelos

Al basarse django en el MVC, divide la aplicación en dos partes la vista (que ya se pudo ver en el anterior post) y el modelo. El modelo define los datos que utilizará la aplicación, como podrían ser los datos de un usuario (nombre, apellidos, dirección, …).

En el caso de django, los modelos serán clases de python que heredarán la clase Model y que utilizarán unos tipos de datos especiales para definir sus atributos. Más tarde, django, gracias a su ORM, transformará estas clases en tablas de la base de datos del proyecto.

Creando un modelo

Los modelos se definen en el archivo «models.py» de la aplicación de django que los vaya a utilizar. Cada modelo será una clase de python que heredará la clase django.db.models.Model, los atributos de las clases de cada modelo deberán ser instancias de unas clases que ofrece django para poder traducir estos atributos en campos de las tablas de la base de datos. Por ejemplo, para crear el modelo de un usuario:

# models.py
# -*- coding: utf-8 -*-

from django.db import models

class Usuario (models.Model):
    """Datos del usuario utilizados en la aplicación."""

    nombre = models.CharField (max_length=40)
    apellidos = models.CharField (max_length=60)
    email = models.EmailField (unique=True)

    def __unicode__ (self):
        return '%s %s (%s)' % (self.nombre, self.apellidos, self.email)

Una breve explicación sobre el ejemplo anterior:

Para conocer los tipos existentes para los atributos de los modelos, siempre se puede consultar la lista de los tipos de los modelos de django.

Una vez se tengan los modelos creados, se deberán sincronizar con la base de datos utilizada.

Configurando la base de datos

Configurar la base de datos a utilizar en django es muy sencillo, sólo hay que editar el archivo «settings.py» del proyecto. Concretamente las siguientes variables:

Aviso: El usuario, la contraseña, la dirección y el puerto del servidor no se usan con sqlite3, ya que no son necesarios.

Una vez configurada la base de datos y creados los modelos necesarios, se podrá sincronizar los modelos de las aplicaciones del proyecto con la base de datos, sólo hay que ejecutar:

$ python manage.py syncdb

Nota: Hay que recordar que las aplicaciones del proyecto aparecen en la variable INSTALLED_APPS del archivo «settings.py», yo recomendaría (para estos ejemplos) quitar o comentar las que vienen de manera predeterminada.

Trabajando con la base de datos

Esta es una de las mejores cosas que tiene django, y es que para acceder (ya sea para consultar, insertar, actualizar o borrar una entrada) a la base de datos, gracias al ORM sólo se usan las clases definidas como modelos.

Para probar todo lo que se va a explicar sin tener que crearse vistas (que todavía no se han explicado en profundidad) siempre se puede ejecutar la consola de django:

$ python manage.py shell

Insertar, actualizar y borrar entradas

Tomando como ejemplo la clase Usuario anterior, se podría realizar:

from proyecto.aplicación.models import Usuario

# Insertar un nuevo usuario
usuario = Usuario (nombre='Pepe', apellidos='Garcia', email='pepe@garcia.es')
usuario.save ()

# Actualizar los datos del usuario
usuario.nombre = 'Jose'
usuario.save ()

# Borrar el usuario
usuario.delete ()

Como se puede ver es tan sencillo como trabajar con cualquier clase de python.

Los «QuerySets»

Los «QuerySets» son conjuntos de objetos de la base de datos, es decir, los resultados de una consulta a la base de datos en forma de objetos de python.

Las clases de los modelos disponen de una instancia del gestor de objetos de la base de datos Clase.objects, en esta instancia se definen los diferentes métodos para realizar las consultas sobre la base de datos, que devolverán un «QuerySet» que contendrá los resultados.

De este modo, tomando el ejemplo de la clase Usuario:

from proyecto.aplicación.models import Usuario

# Insertar varios usuarios
usuario = Usuario (nombre='Pepe', apellidos='Garcia', email='pepe@garcia.es')
usuario.save ()

usuario = Usuario (nombre='Maruja', apellidos='Perez', email='maruja@perez.es')
usuario.save ()

usuario = Usuario (nombre='Pedro', apellidos='Garcia', email='pedro@garcia.es')
usuario.save ()

# Conseguir todas las entradas de la base de datos
todos_los_usuarios = Usuario.objects.all ()
print 'Todos los usuarios %s' % todos_los_usuarios

# Conseguir los usuarios que se apelliden Garcia
garcias = Usuario.objects.filter (apellidos='Garcia')
print 'La familia Garcia %s' % garcias

# La familia Garcia ordenada por nombre
print 'La familia Garcia ordenada por nombre %s' % garcias.order_by ('name')

Como siempre esto no es más que una mota de polvo de lo que en realidad se puede hacer, para profundizar lo mejor es consultar la documentación de django sobre los «QuerySets».

» Leer más, comentarios, etc...

Picando Código

Konqueror en The Big Bang Theory

Noviembre 27th, 2009 - [Enlace local]

A falta de entradas en el blog, una imagen curiosa del último episodio de la tercera temporada de The Big Bang Theory: The Vengeance Formulation. Ya lo anunciaron en KDE News, Leonard Hofstadter usó en este episodio una remera de Konqueror:

Leonard - Remera de Konqueror

Leonard - Remera de Konqueror

Es raro, porque generalmente las interfaces de las computadoras en la serie muestran alguna versión de Windows. En esta misma escena, la laptop de Sheldon Cooper está usando Windows 7 aparentemente. Capaz que usan doble booteo…

Ya había visto KDE en Héroes, y en el artículo enlazado a KDE News comentan de otras series: House, 24, Alias, Dexter y Shar.

The Big Bang Theory… me sigue resultando cada episodio mejor que el anterior.

» Leer más, comentarios, etc...

HardBit

Inno Setup Unicode con ISSkin

Noviembre 25th, 2009 - [Enlace local]

Los que usan Inno Setup, sabran que hay una actualización de Inno Setup 5.3.6 y tambien que existe una version Unicode de este generador de instaladores, para ponerle skins (pieles!!) a nuestro instalador pueden hacer uso de una extension gratuita que ofrece CodeJock llamada ISSkin, bien el punto de este post es que la version que ofrece en el sitio solo funciona con la version ANSI y no la Unicode de Inno Setup, asi que les dejo la DLL para que funcione en Unicode ISSkin Unicode

Es todo.

» Leer más, comentarios, etc...

Joan Garnet :: Arquitectura y desarrollo RIA

Herramientas para Mac(book)

Noviembre 25th, 2009 - [Enlace local]

Apple_transparentAprovechando que mi buen amigo Daniel Viedma se ha pillado un Mac y me ha pedido la tradicional lista de must have apps la voy a trasladar a este post para quién guste.
Me ahorro la lista de aplicaciones de Adobe, que las tengo todas en forma de Master Collection + Flex Builder 3 / Flash builder 4.


Aplicaciones

Utilidades

ManuBar apps

Si me dejo alguna actualizaré la lista
…o si se ocurre algo que valga la pena mencionar podéis comentar :)

Sugerencias de otros usuarios

» Leer más, comentarios, etc...

MonoCaffe

Monocaffe Connections Manager 0.7

Noviembre 25th, 2009 - [Enlace local]

Monocaffe Connections Manager 0.7 está lista para ser descargada. Esta nueva versión contiene:

Monocaffe Connections Manager 0.7 is ready for download. This new version adds:





ScreenShots!!!







» Leer más, comentarios, etc...

Fetishcode...Thinking in objects

Revisión de la demo de ADF 11g, FOD.

Noviembre 25th, 2009 - [Enlace local]

A

» Leer más, comentarios, etc...

Variable not found

Borrado de registros con jqGrid y ASP.NET MVC

Noviembre 24th, 2009 - [Enlace local]

En un post anterior dedicado a jqGrid y ASP.NET MVC vimos lo sencillo que resultaba implementar un potente grid para mostrar datos tabulares, permitiendo paginación, ordenación y redimensionado de columnas.

Pero, como ya comenté entonces, jqGrid es mucho más que eso. En este artículo estudiaremos la implementación de la funcionalidad de borrado de filas integrada en el propio componente, utilizando intercambio de datos Ajax con el lado servidor para actualizar el modelo.

Partiremos del ejemplo que desarrollamos anteriormente, y nos centraremos únicamente en introducir los cambios necesarios para introducir esta nueva capacidad. También, como en el caso anterior, encontraréis al final un enlace al proyecto de demostración, para Visual Web Developer Express 2008 SP1 y ASP.NET MVC 1.0.

1. Preparamos la infraestructura

Selección de módulos en jqGridAntes de nada, y es algo que es importante recordar cuando trabajemos con jqGrid, debemos pensar qué módulos de este componente necesitamos en nuestro proyecto.

En el post anterior descargamos exclusivamente los necesarios para implementar la visualización del grid; ahora, dado que vamos a utilizar más funcionalidades del componente, debemos seleccionar en la herramienta de descarga aquellos que nos harán falta, el base y los relativos a la edición de datos.

En teoría deberíamos seleccionar los módulos estrictamente necesarios para nuestros fines, pero en la práctica no es fácil adivinar cuál de ellos implementa justamente lo que estamos buscando. De hecho, es bastante frecuente encontrarse con errores de script cuando no acertamos con el módulo exacto, al que siguen bastantes iteraciones prueba-error hasta que conseguimos averiguar cuál debemos indicar en la descarga.

Por eso en este caso, seleccionaremos todos los módulos relativos a la edición; eso nos permitirá, además, seguir implementando funcionalidades como el alta o la modificación sin tener que volver a descargar componentes.

Aparte de los módulos a incluir en la descarga, el resto de los pasos de preparación de la infraestructura son idénticos a los descritos en los puntos 1 al 3 del post inicial:

2. El modelo

Vamos a ampliar la clase GestorDeAmigos anterior para que sea capaz de emular el almacenamiento en una base de datos, pero utilizando como soporte una colección en memoria que haremos persistir en una variable de aplicación. Además, aprovecharemos para añadirle un método Delete() que nos permita eliminar del almacén la persona cuyo “Id” le pasemos como parámetro.

public bool Delete(int id)
{
    Amigo amigo = DatosAmigos.FirstOrDefault(a => a.Id == id);
    if (amigo == null)
        return false;
 
    DatosAmigos.Remove(amigo);
    return true;
}
 
No profundizaré más en el modelo, pues el código es de lo más convencional. El que tenga curiosidad por ver cómo se implementa el almacén en una variable de aplicación, que acuda al fuente del proyecto de demostración.

3. La vista

En la vista debemos hacer muy pocos ajustes para permitir la eliminación de los datos. Básicamente, tendremos que habilitar un panel de botones en el grid, indicar que deseamos que aparezca el botón de eliminación, y configurar el comportamiento de éste, para lo cual invocaremos al método navGrid() después de la llamada de inicialización del grid que ya vimos el otro día:

<script type="text/javascript">
    jQuery(document).ready(function() {
    
        jQuery("#list").jqGrid({
            [...] // OMITIDO. Idéntico al del post anterior.
        });
 
        $("#list").navGrid(
                null,
                { refresh: true, add: false, edit: false, del: true, search: false },
                null, // parámetros para el alta
                null, // parámetros para la edición
                {     // parámetros para la eliminación
                    url: '<%= Url.Action("Eliminar") %>',
                    width: 500,
                    afterSubmit: function(r, d) {
                        return [r.responseText=="", r.responseText];
                    }
                }
        );
});

Los parámetros que estamos pasando al método navGrid() son los siguientes:

4. El controlador

Dado que la lógica la tenemos implementada en el modelo, y que la vista ya está preparada para ponerse en contacto con el controlador vía Ajax y para recibir el feedback sobre el éxito de la operación, sólo nos queda implementar muy rápidamente el método de acción:

public ActionResult Eliminar(int id)
{
    if (!gestorDeAmigos.Delete(id))
        return Content("Error eliminando el registro");
 
    return null;
}

Podréis observar que si se produce un error, retornamos un ContentResult describiendo el problema; en otros casos, devolvemos un nulo.

Para probar el funcionamiento de una eliminación errónea podéis, por ejemplo, abrir dos navegadores contra la aplicación, borrar un registro desde uno de ellos e intentar borrarlo también desde el otro.

Y… ¡Voilá!

Una vez habiendo implementado el modelo, la vista y el controlador, sólo nos queda probar la aplicación, que veremos que funciona perfectamente ;-P

jqGrid+ASP.NET MVC en acción

Resumiendo, en este post hemos seguido profundizando en las capacidades de jqGrid, implementando paso a paso la funcionalidad de eliminación de registros. Hemos podido observar también el escaso código (¡a pesar de la longitud del post!) que hay que añadir para disponer de esta funcionalidad, y de lo sencillo y reutilizable que resulta su implementación.

Descargar proyecto de demostración:

Publicado en: Variable not found.



» Leer más, comentarios, etc...

xailer.info (esp)

Los asistentes de Xailer 2

Noviembre 24th, 2009 - [Enlace local]

Estimados usuarios de Xailer,

La versión definitiva de Xailer 2 incorpora un gestor de asistentes que facilita enormemente la creación de formularios rutinarios y/o repetitivos. Un ejemplo típico de un formulario de este tipo sería aquel que creamos cada vez que queremos realizar la edición de todos los campos de una tabla en cualquier tipo de mantenimiento.

Primero vamos a ver lo fácil que resulta utilizar cualquier asistente, para luego adentrarnos en la modificación o creación de nuestros propios asistentes, que es igualmente fácil.

Para acceder a la opción de Asistentes utilizaremos la opción de menú Componentes->Asistentes y se nos mostrará el siguiente diálogo:

Wizards dialog

Observe que todos los asistentes están incluidos de alguna forma dentro de una DLL. Dicha DLL ha sido creada por el propio Xailer, y de hecho podrá incluso analizar su código fuente en el directorio \samples\Wizards. Por supuesto también podrá modificarla y reconstruirla para que los asistentes se adapten a su forma de trabajo.

Las DLL de los asistentes deben de estar ubicadas necesariamente en los directorio \Xailer\Template o \Xailer\User.

Una vez seleccionado un asistente en concreto entra en funcionamiento el código interno del mismo que o bien puede directamente realizar el trabajo requerido o es posible que le muestre algún diálogo para pedirle algún dato adicional.

Si selecciona el asistente DBF table assistant que permite crear un formulario de mantenimiento de tablas DBF, se le mostrará el siguiente diálogo:

DBF editor wizard/>

Simplemente indique como quiere que se comporte el asistente para cada tipo de datos que pueda tener el fichero DBF y pulse el botón Run y podrá observar como de forma instantánea el formulario es creado por usted

La creación o modificación de asistentes es igualmente sencilla, y si le echa un vistazo al ejemplo \samples\wizards\Basic se dará cuenta inmediatamente de ello. Básicamente todo el trabajo consiste en crear una clase descendente de TWizard que tan sólo necesita sobrecargar el método Run(). Eso es todo.

Lo más sencillo es ver un pequeño ejemplo:

  1. CLASS TWzdSample FROM TWizard
  2.  
  3.    METHOD Run()
  4.  
  5. END CLASS
  6.  
  7. METHOD Run() CLASS TWzdSample
  8.  
  9.    LOCAL oForm
  10.    LOCAL oObj
  11.  
  12.    oForm := ::AddForm()
  13.  
  14.    oForm:SetBounds( 246, 223, 307, 137 )
  15.    oForm:SetClientSize( 299, 103 )
  16.  
  17.    WITH OBJECT oObj := TLabel():New( oForm )
  18.       :SetBounds( 12, 12, 26, 14 )
  19.       :cText := "User:"
  20.    END
  21.  
  22.    ::AddComponent( oObj, oForm )
  23.  
  24. RETURN Nil

Este pequeño ejemplo lo único que hace es crear un nuevo formulario en el IDE y añadir un objeto TLabel. Observe las llamadas a AddForm() y AddComponent() que son métodos de la propia clase TWizard.

Para que el IDE sea capaz de reonocer ese código dentro de la DLL es necesario indicarle su existencia en un INIT PROCEDURE dentro de la propia DLL y utilizando la función RegisterWizard. Como suele ocurrir, un ejemplo es lo más descriptivo para entenderlo:

  1. INIT PROCEDURE WzdInit()
  2.  
  3.    RegisterWizard( "A sample",;
  4.       {|| TWzdSample():New() },;
  5.       "comments",;
  6.       cBitmap )
  7.  
  8. RETURN

A continuación os dejo el prototipado de la clase TWizard cuya documentación se encontrará en la ayuda del propio IDE para que veáis todos los miembros de dicha clase cuyo uso es prácticamente auto explicativo.

  1. CLASS TWizard FROM TComponent
  2.  
  3.    PROPERTY aForms
  4.    PROPERTY oActiveForm
  5.  
  6.    METHOD SetActiveModule( cModule ) // –> Nil
  7.    METHOD AddForm( lShow ) // –> oForm
  8.    METHOD AddModule( cType )  // –> cModule
  9.    METHOD AddComponent( oObject, oParent ) // –> lSuccess
  10.    METHOD AddCode( cCode ) // –> lSuccess
  11.    METHOD AddMethod( cMethod, cParams, cBody, cReturn ) // –> lSuccess
  12.    METHOD AddFunction( cMethod, cParams, cBody, cReturn, lStatic ) // –> lSuccess
  13.    METHOD AddProcedure( cMethod, cParams, cBody, lStatic ) // –> lSuccess
  14.    METHOD AddProperty( cName, cClauses ) // –> lSuccess
  15.    METHOD AddEvent( cName, cParams ) // –> lSuccess
  16.  
  17.    METHOD TabWidth() // –> nTabWidth
  18.    METHOD ProjectDir() // –> cDir
  19.  
  20.    METHOD Run() VIRTUAL
  21.  
  22. ENDCLASS

Un cordial saludo,

[El equipo de Xailer]

» Leer más, comentarios, etc...

Cerebro en la Sombra » Técnico

De artículo a proyecto explicado paso a paso: acorta URLs con IraUrl.me

Noviembre 24th, 2009 - [Enlace local]

Hace un par de semanas me encontré con la necesidad de utilizar uno de esos sistemas que hay por ahí para acortar URL’s. Necesitaba enviar una dirección por SMS y tenía que ocupar la menor cantidad de caracteres posible por aquello de optimizar el texto del mensaje. Mientras lo utilizaba pensaba en lo ingenioso de utilizar un sistema de numeración base36 para reducir exponencialmente el número de caracteres de la redirección. Esto iba a ser, pues, un artículo sobre las ventajas de los sistemas de numeración distintos al decimal para determinados proyectos, pero se acabó convirtiendo en un proyecto completo. Cuando estaba comenzando la explicación teórica pensé, ¿por qué no hacerlo? ¿por qué no demostrar lo rápido y fácil que se puede montar algo en Internet hoy en día?

Así, tras unas 15 horas de trabajo os presento IraUrl.me, un acortador de URL’s al estilo de TinyUrl o Bit.ly. Me ha costado más escribir el artículo que hacerlo realidad, curioso ¿eh?. En realidad a medida que iba preparando la aplicación se me iban ocurriendo más cosas que sería interesante montar, por lo que las 8 horas iniciales, más o menos, se convirtieron en 15.

iraurl

La teoría

Para el que no lo sepa, un acortador de URL se basa en encontrar un dominio lo más corto posible y crear redirecciones HTTP 301 a otras URL’s. El truco está en optimizar los parámetros que añadiremos a la URL para que sean lo más cortos posible, no queremos que éstos nos penalicen lo corto del dominio.

¿Cómo funcionan entonces estos acortadores de URL’s? Mucho más fácil de lo que parece y seguramente como a ti se te habría ocurrido. Simplemente tenemos una base de datos donde vamos añadiendo registros a una tabla a medida que se van creando nuevas URL’s cortas.  Esta tabla tiene un campo autonumérico, la clave de la tabla, que para cada nueva URL nos devuelve un identificador único, con lo que cada dirección podría ser accesible de la manera habitual:

http://dominio.com/1

http://dominio.com/1000000

Esa es exactamente la idea, lo único que hacemos es cambiar el identificador en cuestión de base10 (la de nuestro sistema métrico decimal) a base36 o base62 en mi caso. Otros sistemas de numeración conocidos son el hexadecimal (base16) y base64.

Vale, ya has hablado en chino. ¿De qué va esto? Veamos.

Sobre bases de numeración

El sistema decimal utiliza diez dígitos (de ahí lo de decimal :P ) para formar todas las combinaciones de números posibles. Lo que ya conocemos, vamos. El binario utiliza dos dígitos (0 y 1), el hexadecimal 16 (0..9ABCDE), base36, como su nombre indica, treinta y seis (0..9a..z) y base62 utiliza los 62 dígitos que comprenden los números del 0 al 9 y las letras de la A a la Z en mayúsculas y minúsculas (0..9a..zA..Z). Veamos unos ejemplos:

Binario Decimal Hexadecimal Base36 Base62
0 0 0 0 0
1 1 1 1 1
10 2 2 2 2
1010 10 A a a
1100100 100 64 2s 1c
1000000 F4240 lfls 4c92
10000000 989680 5yc1s FXsk

Se puede observar de un vistazo cómo a medida que aumenta el número, cuanto mayor sea la base que manejamos menos dígitos tendrá . Los números, a fin de cuentas, son combinaciones continuas entre todos los dígitos posibles.Así, en función de la base y del número de dígitos, el mayor número representable representable sería:

Num. dígitos
Decimal Base62
1 10 62
2 100 3844
3 1000 238328
4 10000 14776336
5 100000 916132832
6 1000000 56800235584
7 10000000 3521614606208
8 100000000 218340105584896
9 1000000000 13537086546263552

O lo que es lo mismo, base(número de dígitos), 629 contra 109.Espero que se entienda la teoría. Como curiosidad:

Decimal: 10000000000000000000000

Base62: 36aHo5IWaicak

La pregunta ahora sería, ¿Por qué Base62 y no Base64, por ejemplo, mucho más conocida? Sencillo, porque además de los 62 caracteres de Base62, Base64 utiliza dos adicionales, generalmente + y / además del =, lo que convierten la cadena en no web safe, es decir, los caracteres especiales debieran traducirse para que su transporte no diese problemas, con lo que estaríamos perdiendo las ventajas de nuestro cifrado corto. Los 62 caracteres utilizados en Base62 son totalmente seguros, sólo letras (mayúsculas y minúsculas) y números.

Sabiendo ya cómo funciona el sistema, veremos cómo crear nuestra aplicación. Obviamente no contaré todo paso a paso ya que sino tardaría mucho más en escribir el artículo que en hacer la aplicación, me meteré sólo en las cosas que considere más importantes.

Para codificar/decodificar de base10 a base62 utilizaré estas librerías:

  1. function dec2base($dec, $base, $digits = FALSE) {
  2.       if($base < 2 or $base > 256) {
  3.             die("Invalid Base: .$base\n");
  4.       }
  5.       bcscale(0);
  6.       $value = ;
  7.       if(!$digits) {
  8.             $digits = digits($base);
  9.       }
  10.       while($dec > $base1) {
  11.             $rest = bcmod($dec,$base);
  12.             $dec = bcdiv($dec,$base);
  13.             $value = $digits[$rest].$value;
  14.       }
  15.       $value=$digits[intval($dec)].$value;
  16.       return (string) $value;
  17. }
  18.  
  19. function base2dec($value, $base, $digits = FALSE) {
  20.       if($base < 2 or $base > 256) {
  21.             die("Invalid Base: .$base\n");
  22.       }
  23.       bcscale(0);
  24.       if($base < 37) {
  25.             $value = strtolower($value);
  26.       }
  27.       if(!$digits) {
  28.             $digits = digits($base);
  29.       }
  30.       $size = strlen($value);
  31.       $dec = ‘0′;
  32.       for($loop=0; $loop < $size; $loop++) {
  33.             $element = strpos($digits, $value[$loop]);
  34.             $power = bcpow($base, $size-$loop-1);
  35.             $dec = bcadd($dec, bcmul($element, $power));
  36.       }
  37.       return (string)$dec;
  38. }
  39.  
  40. function digits($base) {
  41.       if($base < 64) {
  42.             return substr(‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_’, 0, $base);
  43.       } else {
  44.             return substr("\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#\x24%&amp;’()*+,-./0123456789:;<=>\x3f@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", 0, $base);
  45.       }
  46. }
  47.  
  48. function base_encode($value) {
  49.       return dec2base(base2dec($value, 256), 62);
  50. }
  51.  
  52. function base_decode($value) {
  53.       return dec2base(base2dec($value, 62), 256);
  54. }

Las dos últimas funciones son las que utilizaremos para las conversiones.

Paquetes y librerías utilizadas:

Adicionalmente:

Estructura de la web

Cualquier proyecto web que se precie debe comenzarse describiendo qué queremos mostrar a nuestros visitantes, hay que recopilar todas las ideas, decidir las que interesan de verdad, estudiar cómo se van a disponer en el frontend y terminar con un mapa web que nos indique el flujo a seguir en el trabajo. Este será el nuestro:

mapaWebIraUrlCuando un usuario vaya a una de nuestras url’s cortas en realidad estaremos reenviando la petición http internamente a un script encargado de hacer todo el proceso, link.php en mi caso.

Para ver las estadísticas de una URL me ha gustado el sistema de bit.ly, así que se lo copiamos :P . Añadiendo un “+” al final de la URL corta, en vez de saltar a la dirección larga mostraremos las estadísticas. Esto lo haremos, como en el caso anterior, dirigiendo internamente a otro script, stats.php.

Si el identificador que pretendemos usar para saltar a la url larga o ver estadísticas no existe, reenviaremos a index.php para que muestre un mensaje de error tipo “La url no existe“.

El dominio

Obviamente tendremos que buscar un dominio lo más corto posible, la mayoría estarán ya ocupados, pero buscando y buscando en TLD’s extraños puedes encontrar algo. Yo he escogido un .me porque tiene un carácter menos que un .com :) y no cuesta lo que un .es :P .

La base de datos

Muy sencilla, dos tablas solamente, en una mantendremos las urls generadas y en otra las estadísticas de acceso a las mismas.

  1. CREATE TABLE IF NOT EXISTS `urls` (
  2.   `id` bigint(20) NOT NULL auto_increment,
  3.   `url` varchar(500) NOT NULL,
  4.   `titulo` varchar(255) NOT NULL,
  5.   PRIMARY KEY  (`id`)
  6. ) ENGINE=MyISAM  DEFAULT CHARSET=latin1;
  7.  
  8. CREATE TABLE IF NOT EXISTS `stats` (
  9. `id` bigint(20) NOT NULL auto_increment,
  10. `idurl` bigint(20) NOT NULL,
  11. `codpais` varchar(255) NOT NULL,
  12. `referer` varchar(255) NOT NULL,
  13. `hostreferer` varchar(255) NOT NULL,
  14. `ua` varchar(255) NOT NULL,
  15. `hora` datetime NOT NULL,
  16. `pais` varchar(255) NOT NULL,
  17. `marca` varchar(255) NOT NULL,
  18. `modelo` varchar(255) NOT NULL,
  19. PRIMARY KEY  (`id`),
  20. KEY `idurl` (`idurl`,`hora`)
  21. ) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

La configuración de Apache

Como hemos comentado queremos que cuando alguien vaya a cualquier url de nuestro site del tipo

http://tudominio.com/prueba3

Se redirija internamente al script link.php que será el encargado de procesar la petición. De igual modo si alguien visita

http://tudominio.com/prueba3+

le mostraremos las estadísticas de esa URL (si existen). Configuramos Apache para que tenga en cuenta todas estas particularidades, mod_rewrite será nuestro amigo para conseguirlo. En mi caso he hecho que si la llamada no es un script php, ni una imagen ni un archivo javascript ni un css ni tiene el signo “+“, se vaya a link.php. Si tiene el signo “+” se irá a stats.php.

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/(.*).php$
RewriteCond %{REQUEST_URI} !^/css\/(.*)$
RewriteCond %{REQUEST_URI} !^/js\/(.*)$
RewriteCond %{REQUEST_URI} !^/(.*)\+$
RewriteCond %{REQUEST_URI} !^/images\/(.*)$
RewriteRule ^(.+) /link.php?id=$1
RewriteCond %{REQUEST_URI} ^/(.*)\+$
RewriteRule ^(.+) /stats.php?id=$1

Imagen y diseño

Para el diseño, o te lo haces tú mismo o si eres un negado creativo como yo te descargas una plantilla superchula de freecsstamplates.org y la adaptas a tus necesidades, no voy a entrar en más detalles.

Crear urls cortas

El primer script de nuestra aplicación. Un sencillo formulario donde el usuario introduce la URL que quiere acortar y al hacer submit… la acortamos integrando el sistema con la comprobación de malware que explicaba hace unos días ;) .

  1. $urlbase="";
  2. if(isset($_POST[‘url’])){
  3.         $url=$_POST[‘url’];
  4.  
  5.         try{
  6.             if(substr($url, 0, 4)!="http")
  7.                 $url="http://".$url;
  8.             $class = new GoogleSafeBrowsing(‘ABQIAAAAQYvf-54bCBMuGY20SeONVxQ236Mc_IjryQBl-W_Repaw3fCykA’, true);
  9.             $nomalware=$class->lookupsFor($url);
  10.  
  11.             if($nomalware){
  12.                     $htmltitle="";
  13.                     $html=file_get_contents($url);
  14.                     if($html!=""){
  15.                         preg_match(‘/(.*)<\/title>/is’, $html, $matches);
  16.                     if(is_array($matches) &amp;&amp; count($matches>0))
  17.                         $htmltitle=trim($matches[1]);
  18.                     }
  19.                         $query="select * from urls where url=’$url’";
  20.                 $rs=$conn->Execute($query);
  21.                 if($rs->recordcount()>0){
  22.                     $id=$rs->fields[‘id’];
  23.                 }else{
  24.                     $query="insert into urls (url, titulo) VALUES (‘$url’, ‘$htmltitle’)";
  25.                     $rs=$conn->Execute($query);
  26.                     $id=$conn->insert_id();
  27.                 }
  28.                 $base=base_encode($id);
  29.                 $urlbase="http://iraurl.me/".$base;
  30.             }else{
  31.                 $err=4;
  32.             }
  33.         }catch(exception $e){
  34.             $err=3;
  35.         }
  36. }

Hemos añadido una pequeña comprobación. Si la URL que se quiere añadir ya existe, devolvemos la misma URL corta, yo he tomado esa decisión, tú puedes hacer lo que quieras. Además obtenemos el título de la URL final para tener una referencia hacia la misma, cuestión de sencillez visual :P .

Reenvío a urls largas

Ya tenemos nuestra URL corta, vamos ahora a reenviar las solicitudes a ella a la larga. Recordemos que nuestro Apache nos va a redirigir esa petición a link.php?id=XXXX. Nuestro script actualiza, además, las estadísticas de visitas de la url.

  1.  
  2. "php">if(isset($_GET[‘id’])){
  3.     $idb=$_GET[‘id’];
  4.     $id=base_decode($idb)+0;
  5.     try{
  6.         $query="select * from urls where id=$id";
  7.         $rs=$conn->Execute($query);
  8.         if($rs->recordcount()>0){
  9.             $url=$rs->fields[‘url’];
  10.             $referer=@$_SERVER[‘HTTP_REFERER’];
  11.             $ua=@$_SERVER[‘HTTP_USER_AGENT’];
  12.             $ip=@$_SERVER[‘REMOTE_ADDR’];
  13.  
  14.             $hostreferer="";
  15.             if(preg_match(‘@^(?:http://)?([^/]+)@i’, $referer, $matches)>0)
  16.                 $hostreferer = $matches[1];
  17.  
  18.             $terminal=getMarcaModelo($_SERVER[‘HTTP_USER_AGENT’]);
  19.             $marca=$terminal[‘marca’];
  20.             $modelo=$terminal[‘modelo’];
  21.  
  22.             $temp=getGeoCodeAndPais($ip);
  23.             $codpais=$temp[‘code’];
  24.             $pais=$temp[‘pais’];
  25.  
  26.             $query="insert into stats (idurl, codpais, referer, ua, hora, pais, marca, modelo, hostreferer) VALUES
  27.                     ($id, ‘$codpais’, ‘$referer’, ‘$ua’, now(), ‘$pais’, ‘$marca’, ‘$modelo’, ‘$hostreferer’)";
  28.             $rs2=$conn->Execute($query);
  29.  
  30.             header("HTTP/1.x 301 Moved");
  31.             header("Location: $url");
  32.             exit;
  33.         }else{
  34.             header("Location: http://iraurl.me/index.php?err=1");
  35.             exit;
  36.         }
  37.     }catch(exception $e){
  38.         header("Location: http://iraurl.me/index.php?err=2");
  39.         exit;
  40.     }
  41. }
  42. header("Location: http://iraurl.me/index.php?err=1");

Como veis, si la URL no existe redirigimos al usuario a index.php con un mensaje de error. Necesitaremos dos funciones adicionales, las que nos devuelven información del país de origen de una IP y los datos del terminal del usuario (móvi o web). No entraré en detalles sobre la instalación de Maxmind GeoLite Country o de Wurfl/Tera-Wurfl.

  1. function getGeoCodeAndPais($ip){
  2.     require_once(dirname(__FILE__)."/geoip/geoip.inc");
  3.     $gi = geoip_open("/usr/share/GeoIP/GeoIP.dat",GEOIP_STANDARD);
  4.     $codpais=geoip_country_code_by_addr($gi, $ip);
  5.     $pais=geoip_country_name_by_addr($gi, $ip);
  6.     geoip_close($gi);
  7.     return array("pais"=>$pais, "code"=>$codpais);
  8. }
  9.  
  10. function getCapabilities($ua){
  11.     require_once(dirname(__FILE__)."/Tera-WURFL/TeraWurfl.php");
  12.     $wurflObj = new TeraWurfl();
  13.     $matched = $wurflObj->GetDeviceCapabilitiesFromAgent($ua);
  14.     $movil = $wurflObj->capabilities;
  15.  
  16.     return $movil;
  17. }

Estadísticas

La teoría es la misma. Si existe la URL cargamos los datos, si no redirigimos a la home. En nuestra caso utilizamos el componente de pestañas de JqueryUI para organizar los distintos tipos de datos que permitiremos ver y añadiremos los botones para exportar a Excel y PDF.

  1. $idb=substr($_GET['id'], 0, strlen($_GET['id'])-1);
  2.  
  3. $id=base_decode($idb)+0;
  4. $query="select * from urls where id=$id";
  5. $rs=$conn->Execute($query);
  6. if($rs->recordcount()>0){
  7.     $urlbase="http://iraurl.me/".$idb;
  8.     $url=$rs->fields['url'];
  9.     $id=$rs->fields['id'];
  10.     $htmltitulo=$rs->fields['titulo'];
  11.     if($htmltitulo=="")
  12.         $htmltitulo=$url;
  13.     $query="select count(*) as nregs from stats where idurl=$id";
  14.     $rs=$conn->Execute($query);
  15.     $clicks=$rs->fields['nregs'];
  16. }else{
  17.     header("Location: http://iraurl.me/index.php?err=1");
  18.     exit;
  19. }

Muy sencillo.

stats

Lo complicado en este caso es mostrar las gráficas con FusionCharts. Para cada una debemos añadir algo de código html:

El script xml.php será el que devuelva los datos en el formato adecuado para FusionCharts. Por ejemplo:

  1. $query="select DAY(hora) as dia, MONTH(hora) as mes, YEAR(hora) as ano, count(*) as nclicks
  2. from stats
  3. where idurl=$id
  4. group by ano, mes, dia
  5. order by hora";
  6. $rs=$conn->Execute($query);
  7.  
  8. $xml='';
  9. while($r=$rs->fetchrow()){
  10.  $xml.='.$r['dia']."/".$r['mes']."/".$r['ano'].'" value="'.$r['nclicks'].'" color="#A1A1A1" />';
  11. }
  12. $xml.='';

Os doy sólo un ejemplo, el resto lo montáis por vuestra cuenta :) .

Descifrar urls cortas

Todos los sistemas de acortar URL's funcionan tal y como cuento en este artículo, haciendo un HTTP/301 redirect hacia la url original.

A partir de la URL corta podemos saber cual es la URL original simplemente siguiendo las redirecciones que hace. Muy sencillo con PHP y que, además nos sirve para, integrándola en nuestra API de malware, prevenir posibles problemas con la URL final.

  1. function get_web_page( $url )
  2. {
  3.     $options = array( 'http' => array(
  4.         'user_agent'    => 'spider',
  5.         'max_redirects' => 10,
  6.         'timeout'       => 120,
  7.     ) );
  8.     $context = stream_context_create( $options );
  9.     $page    = @file_get_contents( $url, false, $context );
  10.  
  11.     $result  = array( );
  12.     if ( $page != false )
  13.         $result['content'] = $page;
  14.     else if ( !isset( $http_response_header ) )
  15.         return null;    // Bad url, timeout
  16.  
  17.     // Save the header
  18.     $result['header'] = $http_response_header;
  19.  
  20.     // Get the *last* HTTP status code
  21.     $nLines = count( $http_response_header );
  22.     for ( $i = $nLines-1; $i >= 0; $i-- )
  23.     {
  24.         $line = $http_response_header[$i];
  25.         if ( strncasecmp( "HTTP", $line, 4 ) == 0 )
  26.         {
  27.             $response = explode( ' ', $line );
  28.             $result['http_code'] = $response[1];
  29.             break;
  30.         }
  31.     }
  32.  
  33.     return $result;
  34. }
  35.  
  36. $url="";
  37. if(isset($_POST['url'])){
  38.     $url=$_POST['url'];
  39.     $datos=get_web_page( $url );
  40.     if($datos){
  41.         $headers=$datos['header'];
  42.         $urls=array($url);
  43.         foreach($headers as $head){
  44.             $temp=explode(" ", $head);
  45.             if(strtolower($temp[0])=="location:"){
  46.                 $urls[]=$temp[1];
  47.             }
  48.         }
  49.         $htmltitle="";
  50.             preg_match('/(.*)<\/title>/is', $datos['content'], $matches);
  51.         if(is_array($matches) &amp;&amp; count($matches>0))
  52.             $htmltitle=trim($matches[1]);
  53.     }
  54. }

Ya está, en $urls tendremos la lista de urls que van saltando hasta llegar a la final.

descifrar

Api

Hoy en día todo tiene que tener Api. Para las estadísticas es muy sencillo, el propio XML que generamos para consumir con FusionCharts nos permite que clientes externos se alimenten del mismo. Para crear URL's cortas remotamente, simplemente creamos un archivo api.php:

  1. if(isset($_GET['url'])){
  2.     $url=urldecode($_GET['url']);
  3.     try{
  4.         $htmltitle="";
  5.         if(substr($url, 0, 4)!="http")
  6.             $url="http://".$url;
  7.         $html=file_get_contents($url);
  8.         if($html!=""){
  9.             preg_match('/(.*)<\/title>/is', $html, $matches);
  10.             if(is_array($matches) &amp;&amp; count($matches>0))
  11.                 $htmltitle=trim($matches[1]);
  12.         }
  13.         $query="select * from urls where url='$url'";
  14.         $rs=$conn->Execute($query);
  15.         if($rs->recordcount()>0){
  16.                 $id=$rs->fields['id'];
  17.         }else{
  18.                 $query="insert into urls (url, titulo) VALUES ('$url', '$htmltitle')";
  19.                 $rs=$conn->Execute($query);
  20.                 $id=$conn->insert_id();
  21.         }
  22.         $base=base_encode($id);
  23.         $urlbase="http://iraurl.me/".$base;
  24.         echo $urlbase;
  25.     }catch(exception $e){
  26.         echo "ERROR";
  27.     }
  28. }

Eso es todo. No olvides integrarlo también con el sistema de malware.

Conclusiones

Bueno, y todo este rollo ¿para qué?. Pues muy sencillo, para que veais que hoy en día la tecnología está al alcance de todos, es sencillo y rápido crear un proyecto en Internet, hay de todo por todas las esquinas, la tecnología no es lo importante, lo que verdaderamente cuenta es cómo mueves ese producto tecnológico para rentabilizarlo y obtener un beneficio de él.

Ya tengo mi proyecto superchulo funcionando, sólo me ha costado unas 15 horas de trabajo. Le he puesto un poco de Adsense por aquí y por allí. ¿Y ahora qué? ¿A esperar a que la gente entre y me haga millonario? :P Es mucho más complicado que eso como todos sabéis, primero tienes que tener una masa de usuarios elevada que le dé movimiento al proyecto y después tienes que conseguir que la mayoría de ellos sean gente normal, no gente técnica, usuarios avanzados que no pagamos por nada ni pinchamos en publicidad :P .

Hoy en día, en Internet, como en cualquier negocio, las técnicas de marketing y venta son mucho más importantes que la tecnología en sí misma, es duro reconocerlo, pero es así. De nada sirve que tengas el mejor producto del mundo mundial si no consigues que la gente lo utilice y se deje dinero, así de claro. Si tienes los conocimientos adecuados para mover el negocio, no te preocupes, la tecnología te la aporta cualquier partner por un módico precio, pero poner en manos de otro toda la estrategia de ventas de tu negocio no está tan claro ¿no?.

Espero que os sirva de algo el artículo. He querido mostrar, fundamentalmente, cómo utilizando algunas librerías que puedes obtener sin coste puedes hacer algo realmente útil y funcional con muy poco esfuerzo. Seguro que sacáis alguna idea.

Perdón por el rollo :P , al final me ha costado mucho más escribir el artículo que implementarlo.

  1.  
  2. "php">Location: http://iraurl.me/

» Leer más, comentarios, etc...

Cerebro en la Sombra » Técnico

HTPC (Home Theater PC)

Noviembre 24th, 2009 - [Enlace local]

Si hace unas semanas os hablaba del DVD grabador de mis padres, hoy voy a enseñaros uno de mis juguetes, la evolución natural de esos cacharros, el HTPC.

Los HTPC son ordenadores cuya principal función es servir de plataforma de ocio y entretenimiento: televisión, dvd, reproductor de vídeos, música, fotos… todo lo hace él solito y con una gran ventaja: en cualquier momento puedes actualizar las partes de hardware que se hayan quedado obsoletas y tener tu aparato actualizado permanentemente.

El mío tiene ahora dos años y medio y en líneas generales es esto:

El aparato se complementa con un receptor A/V Denon AVR-2802,  7 cajas acústicas (JBL e Infinity),  un subwoofer Yamaha,  un televisor LCD Samsung de 32” y un mando a distancia Logitech Harmony 555.

IMG_6257

La caja

Es el punto más crítico e importante de un aparato de este tipo ya que normalmente querremos colocarlo en el salón de casa y no nos gustaría que desentonase, que se viese la típica caja de ordenador. Desde el principio tenía claro que buscaba una caja que no fuese excesivamente grande, a ser posible del tamaño de un DVD o similar. La mayoría de modelos son, en cuanto a tamaño, como una minitorre de ordenador tradicional en horizontal, muy altos, cosa que no me convencía. Buscando encontré la que terminé comprando, Thermaltake Mozart SX, buen precio, mando a distancia, buena disipación, dos ventiladores incorporados… me pareció perfecta y que era lo que yo buscaba, sin embargo no es oro todo lo que reluce.

Seguramente el sitio más completo donde puedes buscar componentes de este tipo es Sistemas Ibertrónica.

IMG_6258Los componentes

Cuando comencé a montar el aparato me di cuenta de que la escasa altura de la caja penalizaba las opciones de ampliación del aparato con tarjetas PCI ya que no se pueden poner tal cual, son más grandes que la caja. Para solucionarlo viene de serie con un adaptador que expande la ranura PCIe y dos PCI de la placa en horizontal tal y como podéis ver en la imagen de abajo. En general debiera ser más que suficiente, pero no tiene por qué ser así. Otro detalle importante a tener en cuenta es que la placa hay que escogerla cuidadosamente ya que, para poder utilizar el adaptador que os he comentado, la ranura PCIe tiene que estar situada en la posición exacta donde se sitúa el adaptador, de otro modo no podremos pincharla.

En mi caso tengo pinchadas la tarjeta gráfica y la sintonizadora de TV como podéis ver. En la ranura de abajo de todo tengo el adaptador de la placa para sacar la salida de audio digital y no puedo poner nada más que eso, la ranura PCI en sí queda totalmente inutilizada ya que la tarjeta gráfica es más “gorda” de lo habitual y se come el espacio disponible. En mi caso no he tenido problemas, todo lo que necesitaba está ahí, pero si necesitas algo más no tendrás donde ponerlo. De hecho una de las razones de poner la sintonizadora dual fue precisamente la imposibilidad de poner dos sencillas.

La gráfica se conecta al televisor a través de un cable DVI-HDMI con lo que la visualización es increíblemente perfecta. Hasta hace un par de meses tenía otra gráfica, pero se estropeó :P , en realidad la estropeé yo. Traía uno de esos pequeños ventiladores que traen las gráficas y al poco tiempo comenzó a hacer un ruido muy molesto, con lo que decidí desconectarlo y que aguantase con el disipador lo que pudiese. Al final fueron más de dos años, no está mal. Para la nueva tenía claro que la quería silenciosa, sin ventilador, pensáoslo bien antes de comprar una con ventilador, los de estas tarjetas suelen ser de muy baja calidad.

blog_IMG_6253Aquí os muestro una panorámica del equipo montado, queda todo muy limpio y accesible, la verdad. Una de las ventajas que tiene es que la fuente de alimentación es de tamaño normal, no tienes que preocuparte si se estropea. En aparatos tipo barebone tienes un grave problema si se estropea la fuente, y tarde o temprano se estropeará. El disco IDE que tengo es porque ya lo tenía :P , tengo ganas de quitar el cable IDE amarillo que se ve y que abulta muchísimo y  sustituirlo por los sencillos Serial ATA, pero mientras funcione tampoco lo vamos a jubilar :)blog_IMG_6250He pensado más de una vez en ponerle un lector Blu Ray y aprovechar la calidad de imagen que ofrece, pero después pienso, ¿para qué si no alquilo una película desde 2001?.

El software

Hasta hace un par de semanas estaba con Windows XP Media Center Edition 2005, pero lo actualicé a Windows7 viendo las buenas referencias y creedme, es un lujazo verlo funcionar ahora, el buen rendimiento que tiene se une a las prestaciones del nuevo Media Center dando como resultado una plataforma de ocio multimedia que roza la perfección, aunque siempre hay cosas que te gustaría cambiar :) . En su día probé otros programas media center, el mejor y más completo es, sin duda, Media Portal, también probé soluciones bajo Linux, MythTV y Freevo, pero no acabaron de convencerme, quizás les de otra oportunidad un día de estos.

Hace mucho tiempo que quiero integrar Imagenio en el Media Center y tenerlo todo centralizado, pero no había manera de hacerlo hasta que apareció DVBLink for IPTV, un pequeño pero gran software que crea sintonizadoras virtuales para importar en Media Center los canales de televisión IP de forma que quedan integrados como cualquier otro canal de TV, pudiendo programar las grabaciones o verlos con un sólo click en el mando. DVBLink for IPTV cuesta unos 20 euros, un precio más que asequible para las ventajas que aporta, al menos yo lo veo así.

La prueba de fuego que le hice es grabar tres programas simultáneamente (uno en cada sintonizador TDT y otro desde Imagenio) y, mientras tanto, reproducir una película. Todo perfecto, no se puede pedir más.

El mando a distancia

Posiblemente el elemento más importante y el que más desapercibido pasa de todo el sistema si no quieres tener varios mandos a la vez entre tus manos.

Hasta hace un par de meses tenía un One-for-All Kamaleon que compré allá por 2002, pero también se estropeó :P , así que me decidí por un Logitech Harmony 555, muy buena relación calidad-precio y, sobre todo, con soporte directamente para el mando de las cajas Thermaltake.

IMG_6260

La verdad es que la curva de aprendizaje del mando y el tiempo de puesta en marcha completa son muy elevados, una vez tienes claro como funciona llega el momento de ir ajustando cada botón para que haga lo que tu quieres. Aunque tiene el clásico modo de funcionamiento de todos los mandos basado en dispositivos, es decir, funcionar como cada mando por separado, el funcionamiento principal se basa en actividades, lo que despista mucho al principio. Por ejemplo:

De esta manera con un sólo botón preparas el ambiente que vas a utilizar. El modo de actividades permite, además, combinar en el teclado del mando funciones de distintos aparatos, por ejemplo, que el volumen maneje el receptor A/V, otro botón el cambio de fuente en la TV, los de play/stop el Media Center, etc. En el modo dispositivo, cada mando simula ser el mando original completo sin posibilidad de intercalar funciones de otros mandos.

Conclusiones

Como casi todo en la vida, tiene sus pros y sus contras.

A favor, sin duda, la versatilidad, la sencillez de manejo y la centralización de todo tu ocio multimedia en un sólo aparato

En contra… quieras o no,  es un ordenador con todos los inconvenientes que conlleva también eso para la gente no técnica. Si estás cansado de que familiares y amigos te llamen cada vez que tienen un problema con su ordenador, ni se te ocurra recomendarles que se pongan un cacharro de estos. Son muy apetecibles para todo el mundo pero dado que son tan abiertos a poder hacer casi de todo, la cantidad de problemas que pueden surgir son muy elevados. Sólo aptos para usuarios muy avanzados.

En cuando al hardware, lo más importante en este tipo de aparatos es la caja, yo he contado mi experiencia. Quizás hoy me  hubiese comprado una más normal en cuanto a tamaño de manera que pudiese utilizar las tarjetas PCI en su forma habitual, no lo sé, pero aún así estoy muy contento con la que tengo, es amplia y está muy bien refrigerada. Con este tipo de cajas hay que tener mucho ojo con la placa que compras, no sirve cualquiera ya que hay que hacer coincidir la ranura PCIe con el hueco adecuado para el adaptador horizontal. De igual modo cuando veas cajas mira una que venga con el display y el mando incorporados, comprarlos después te saldrá más caro y posiblemente no se integren a la perfección.

Para los demás componentes no creo que haya que irse a componentes de última generación, ya veis mi equipo, normalito y se mueve a la perfección.

Finalmente gástate algo de dinero en un buen mando universal que te permita tener todo unificado en un solo aparato, a la larga lo agradecerás, no tengo más que decir, creo que no haya que pensar mucho para darse cuenta de las ventajas :P .

Logitech Harmony 555

» Leer más, comentarios, etc...

Fetishcode...Thinking in objects

JSF y su valor añadido

Noviembre 24th, 2009 - [Enlace local]

A

» Leer más, comentarios, etc...

Buayacorp

Archivos de WordPress útiles y efectivos

Noviembre 24th, 2009 - [Enlace local]

Cómo crear una página de archivos de WordPress útil y efectiva.

» Leer más, comentarios, etc...

Variable not found

Hojas con estilo usando .less

Noviembre 23rd, 2009 - [Enlace local]

.Less: CSS Dinámicos para .NET Seguro que más de una vez has sufrido de lo lindo al tocar un archivo de hoja de estilos. Suelen estar mal organizados, ser muy extensos, difíciles de mantener, hay declaraciones duplicadas por todas partes… Y seguro que siempre te has preguntado si hay otra forma menos complicada de conseguir lo mismo.

K. Scott Allen nos habla en su post Keeping CSS Files DRY de .Less (dot less), un interesante componente portado del mundo Ruby capaz de “compilar” hojas de estilo que utiliza una notación extendida y mucho más potente del estándar CSS, permitiendo las siguientes características:

¿Y cómo funciona esto? Muy sencillo. En primer lugar, escribimos la hoja de estilos en un archivo .less, en el que podemos utilizar las características descritas anteriormente, y lo incluimos en las páginas donde nos interese, como lo hacemos siempre:

<link href="/Content/estilos.less" rel="stylesheet" type="text/css" />

Es interesante tener en cuenta que Less es compatible con CSS, lo que quiere decir que si simplemente renombramos el .css a .less, ya tendremos una hoja de partida.

A continuación, debemos introducir en el web.config una declaración que asocie la extensión .css al compilador Less:

<httpHandlers>
  <add validate="false" path="*.less" verb="*"
    type="dotless.Core.LessCssHttpHandler, dotless.Core" />
</httpHandlers>

Cuando llega una petición, el handler se encargará de compilar el archivo .less, generar el .css correspondiente y enviarlo minimizado al cliente, cacheando el resultado para posteriores usos, si se lo indicamos en la configuración, una sección opcional del web.config.

.Less está diseñado para ASP.NET 3.5 o superior, y por supuesto, es válido tanto para Webforms como para ASP.NET MVC. Se distribuye bajo licencia Apache 2.0, y está aún en fase beta, por lo que habrá que estar atento a la evolución de este interesantísimo proyecto.

Enlaces:

Publicado en: Variable not found.



» Leer más, comentarios, etc...

4 bits blog

Cómo proteger las contraseñas de los usuarios

Noviembre 22nd, 2009 - [Enlace local]

Voy a recuperar otro de mis documentos perdidos en Google Docs (el anterior fue el de las autotools), esta vez se trata de un pequeño manual sobre cómo proteger las contraseñas de lo usuarios de cualquier aplicación que se programe, el manual es una traducción al español del artículo How to encrypt user passwords de Daniel Fernandez.

1. Visión general

Casi todas las aplicaciones web modernas necesitan, de un modo u otro, cifrar las contraseñas de sus usuarios. Se podría decir que, desde el momento en que la aplicación tiene usuarios, y los usuarios se identifican usando una contraseña, esas contraseñas se deben guardar usando algún tipo de cifrado.

Hay muchas razones básicas para esto: las bases de datos pueden estar comprometidas, y por tanto también las comunicaciones. Pero la razón más importante es que se tiene que pensar que las contraseñas de los usuarios son datos personales. Sus contraseñas son sus claves para su privacidad, por tanto son personales, y nadie tiene el derecho de conocerlas. Y se debe cumplir esto si se quiere ganar la confianza de los usuarios.

2. El algoritmo

Ahora que se quieren cifrar las contraseñas, se debe saber cómo. Aquí entra la primera regla:

I. Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o hash).

Esto se debe a que, excepto para algunos escenarios específicos, no hay ninguna razón para que una contraseña sea descifrada. Si se cifran las contraseñas usando cifrados basados en claves (una técnica de dos sentidos) y un atacante consigue saber la clave de cifrado, se revelarán todas las contraseñas. Si no se tiene una clave para el cifrado, el riesgo desaparece, y el atacante tendrá que confiar en el uso de técnicas de fuerza bruta o similares.

Pero, ¿qué ocurre si un usuario pierde su contraseña? ¿No podrá recuperarla? La respuesta es NO. No sólo no se puede recuperar, si no que ni siquiera existe una manera de leer/conocer/ver las contraseñas de los usuarios, no importa si se es el administrador del sistema. Si un usuario pierde su contraseña, sólo se podrá reiniciarla a un nuevo valor y enviárselo por algún canal seguro al usuario, y pidiéndole que la cambie lo más pronto que pueda.

Ahora que está claro que las funciones resumen son necesarias en las contraseñas, ¿cuál algoritmo se debería usar? Hay bastantes, y dependen de las necesidades de cada uno. Los más usados son:

En la mayoría de los casos, MD5 o SHA-1 serán elecciones adecuadas para los resúmenes de contraseñas, aunque aplicar estos algoritmos no será suficiente, como se verá a continuación.

Cuando se dice que se deberían usar funciones resumen para los cifrados de las contraseñas, y dado que estas funciones resumen son técnicas de un sentido, la siguiente pregunta es: «Si no se pueden descifrar las contraseñas, ¿cómo se comprueba que un usuario tiene una contraseña correcta?»

Hay una respuesta muy simple a esta cuestión, que será la segunda regla:

II. La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.

Lo que significa que, una vez los usuarios han introducido sus contraseñas en la identificación, se realizará la función resumen sobre la contraseña introducida con el mismo algoritmo que se ha usado con las contraseñas guardadas, y se compararán ambos resultados. Como estas funciones garantizan que dos entradas iguales obtienen el mismo resultado (lo que no es cierto en el sentido contrario), si los resultados coinciden se considerará que la contraseña introducida por el usuario es correcta.

3. Mejorando la seguridad de los resúmenes

Todos los algoritmos de funciones resumen mencionados anteriormente comparten una característica: son públicos. Son algoritmos muy conocidos y ampliamente implementados, por lo que cualquiera puede usarlos.

Si cualquiera puede usar el mismo algoritmo que la aplicación, y por alguna razón los atacantes pueden ver la base de datos de los resúmenes de las contraseñas, ¿cómo se puede estar seguro que los atacantes no serán capaces de conseguir alguna contraseña de los usuarios probando todas las posibilidades hasta que encuentren algún resumen que coincida con uno de los guardados?

La respuesta es que no se puede. Pero se puede hacer que todo eso sea una tarea enorme y con un gran tiempo de realización, de modo que no resulte útil, a menos que puedan esperar toda la eternidad. Para lograr esto, se van a explicar dos conceptos: «salt» y el contador de iteración.

3.1. «Salt»

«Salt» es una secuencia de bytes que se añaden a la contraseña antes de conseguir su resumen. Esto hace que estos resúmenes sean diferentes a los que deberían ser usando sólo la contraseña, y como resultado se consigue protección contra los ataques de diccionario. Se pueden seguir dos estrategias para usar «salt»:

En la práctica, un «salt» aleatorio (o variable) es la mejor idea porque, aunque al generarse aleatoriamente tendrá que guardarse junto al resumen de la contraseña (por lo que cualquiera podrá recuperarlo) y esto hará que sea trivial para un atacante conocerlo, permitirá que cada contraseña de usuario sea independiente del resto, por lo que tendrán que atacarse una por una.

Se tiene que pensar que, si se usa un «salt» fijo y el atacante consigue saberlo, la seguridad de toda la base de datos de contraseñas será nula. Y hay muchos métodos para que el atacante pueda conocer este «salt», como aplicar fuerza bruta en sus contraseñas o en contraseñas que tenga de algún usuario válido. En un escenario de un «salt» fijo, una contraseña débil podría hacer que el sistema de contraseñas fuera débil.

Sin embargo, si se quiere mantener una parte del «salt» en secreto, se puede usar una técnica que mezcle las otras dos, usando un «salt» compuesto de una parte fija y otra aleatoria, guardando los bytes aleatorios junto al resumen de la contraseña.

El tamaño mínimo de un «salt» es de 8 bytes. Si se usa una opción mixta, al menos 8 bytes deberían ser aleatorios.

Así, se puede definir la tercera regla:

III. Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.

3.2. El contador de iteración

El contador de iteración es el número de veces que la función hash que realiza el resumen se aplica a sus propios resultados.

Esto significa que, una vez se ha seleccionado un «salt» y concatenado con la contraseña, se deberá aplicar la función hash (por ejemplo, MD5), conseguir el resultado, y pasarlo de nuevo como entrada a la misma función, y hacer lo mismo las veces que indique el contador de iteración.

El número mínimo recomendado de iteraciones es 1.000, y esto proporcionará una buena seguridad extra. Hay que pensar que, cuando se crea un único resumen de una contraseña para un nuevo usuario, la diferencia entre aplicar la función hash una vez o 1.000 veces no será un problema, tal vez doscientos milisegundos, pero los atacantes deberán tener que generar una enorme cantidad de intentos de resumen mediante fuerza bruta y, para un atacante, la diferencia entre aplicar la función hash una vez y aplicarla mil veces para cada intento será un gran problema computacional.

Así, se obtiene la cuarta regla:

IV. Iterar la función hash al menos 1.000 veces.

4. Secuencia de traducción de caracteres a secuencia de bytes

Antes de poder guardar correctamente los resúmenes de las contraseñas, hay algo que se debería tener en cuenta, que es la diferencia entre cadenas de caracteres y secuencias de bytes: dos cadenas de caracteres se pueden representar con diferentes secuencias de bytes dependiendo de la codificación empleada (ISO-8859-1, UTF-8, …)

Esto afecta, ya que las contraseñas suelen ser cadenas de caracteres, pero las funciones hash trabajan a nivel de byte.

4.1. Problemas codificando la contraseña introducida

En el supuesto de que un nuevo usuario se identifique con una contraseña que no contiene caracteres ASCII, y que la aplicación de identificación, que se está ejecutándo en Windows 2000 en Europa Occidental, convierta la cadena de caracteres a bytes sin elegir una codificación específica para la operación, e incluso usando la que haya de forma predeterminada, en Windows suele ser ISO-8859-1. De modo que el resumen se realice sobre esa secuencia de bytes y quede almacenado.

Entonces, este usuario vuelve de nuevo y esta vez no visita la aplicación de identificación anterior, en vez de eso usa cualquier otra aplicación con la misma base de datos de contraseñas pero en Linux. El usuario introduce correctamente su contraseña y … ¡no coincide!

¿Por qué? Porque la mayoría de los sistemas Linux, usan UTF-8 como codificación predeterminada, y las contraseñas que ha introducido el usuario en ambos casos se han codificado en diferentes secuencias de bytes, afectando a la correcta concordancia de las contraseñas.

Cómo se puede resolver esto, pues utilizando una codificación fija para las codificaciones de las cadena de caracteres a secuencia de bytes. Así se enuncia la quinta regla:

V. Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.

Si se usa Java, donde las cadenas de caracteres (clases String) se codifican independientemente (aunque usan de fondo UTF-16), no habrá que preocuparse de si la aplicación usa ISO-8859-1 o alguna otra codificación en lugar de UTF-8 para su interfaz de usuario, al igual que no será necesario que la codificación para los resúmenes de las contraseñas coincidan con las de la interfaz de usuario. Sólo será necesario que la codificación sea una fija, y UTF-8 proporciona un buen equilibrio entre tamaño y conjunto de caracteres.

4.2. Problemas codificando en el almacenamiento del resumen

Normalmente se quiere gestionar y almacenar el resumen de contraseñas como una cadena de caracteres, pero la función hash dará como resultado una secuencia de bytes que no necesariamente representará un carácter válido en alguna codificación. Por esto, la secuencia de bytes del resumen no se puede codificar en una cadena de caracteres, habiendo peligro de pérdidas de datos si se realizase.

Aquí es donde entra al rescate la codificación BASE64. Codificando la secuencia de bytes del resumen en BASE64, se asegura que la secuencia de bytes de salida representa una cadena de caracteres US-ASCII válida. Por lo que se podrá codificar una secuencia de bytes en BASE64 en una cadena de caracteres US-ASCII.

VI. Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.

5. Resumen de las reglas para el cifrado de contraseñas

La lista completa de reglas a aplicar:

  1. Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o hash).
  2. La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.
  3. Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.
  4. Iterar la función hash al menos 1.000 veces.
  5. Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.
  6. Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.

6. Realizándolo en Java

La forma más sencilla de cifrar contraseñas en Java, mediante las técnicas explicadas es usar Jasypt.

Si no se puede usar jasypt, o si por alguna razón se quiere desarrollar alguna característica de cifrado propia, se necesitarán:

También, se puede utilizar el código fuente para las clases org.jasypt.digest.StandardByteDigester y org.jasypt.digest.StandardStringDigester, disponible en el repositorio de jasypt.

7. Defensa frente a los típicos ataques

7.1. Ataques de fuerza bruta

Realizado sobre: Una única contraseña.

Descripción: El atacante intenta conseguir la contraseña del usuario generando todas las contraseñas posibles, resumiéndolas y probando si coinciden con el resumen de la contraseña del usuario.

Defensa: La iteración de la función hash un número de veces, como 1.000 (mínimo recomendado), el coste de la creación del resumen de la contraseña en el tiempo de identificación del usuario no es significante, pero el coste de un ataque de fuerza bruta por parte de un atacante que genera millones de resúmenes será muy grande. Hay que recordar que una de las mejores maneras de proteger los datos cifrados es haciendo que el coste de romper la seguridad sea más alto de lo que cuesta el esfuerzo.

7.2. Ataques de diccionario

Realizado sobre: Cada contraseña o una base de datos de contraseñas

Descripción: El atacante intenta conseguir la contraseña de un usuario comparando sus resúmenes contra un conjunto de resúmenes de contraseñas más probables, normalmente se generan desde una lista de palabras de un diccionario. Este ataque explota una debilidad actual de las aplicaciones, ya que muchos usuarios usan palabras del diccionario como contraseña.

Defensa: Añadiendo un «salt» aleatorio, la debilidad de las contraseñas basadas en diccionario se reduce, y la posibilidad de de que el resumen aparezca en un conjunto de resúmenes previamente creados por el atacante es mínima.

7.3. Ataques de cumpleaños

Realizado sobre: La base de datos de las contraseñas.

Descripción: Este ataque explota la paradoja del cumpleaños, la cual en resumen consiste en, teniendo un gran conjunto de resúmenes de contraseñas, las posibilidades de generar una contraseña cuyo resumen coincida con al menos uno de los resúmenes en el conjunto es mucho más alto que lo esperado. Y estas posibilidades aumentan bastante cuando el tamaño del conjunto (el número de usuarios) aumenta.

Defensa: Añadiendo un «salt» aleatorio las posibilidades de que un ataque de cumpleaños tenga éxito son mínimas, porque el atacante tendría que atacar cada contraseña por separado, y no el conjunto de contraseñas, para encontrar una coincidencia. Esto es porque se tendría que encontrar una contraseña que crease el mismo resumen que el del atacante usando el mismo «salt» que se usó para el resumen, y que es diferente para cada contraseña haciendo que el ataque pase a ser de fuerza bruta.

Espero que este pequeño manual os sea de utilidad, a mí me sirvió para mi PFC.

» Leer más, comentarios, etc...

Ingenieria de Software / Software Engineering

Microsoft Project Admin Blog

Noviembre 21st, 2009 - [Enlace local]

Con gusto recibo el post donde se anuncia el nuevo blog para administradores de Project Server y para muestra un excelente post el cual menciona los requerimientos de instalación.

» Leer más, comentarios, etc...

MonoCaffe

Monocaffe Connections Manager 0.6

Noviembre 20th, 2009 - [Enlace local]

Monocaffe Connections Manager 0.6 está lista para ser descargada. Además de arreglar algunos fallos he añadido una caracteristica genial, un línea de comandos en cluster. Marcando diferentes pestañas y luego escribiendo en la parte inferior, se verá reflejado en todas las pestañas marcadas. De ésta forma podemos trabajar en varias maquinas a la vez.







Tambien he arreglado algunos bugs y añadido otros :-) los podeis ver en el launchpad de mcm en http://launchpad.net/mcm



Finalmente, me gustaria añadir una version hecha en Qt para KDE, pero prefiero continuar afinando las versiones de consola y de GTK, por lo que si alguien está interesado, que me deje un comentario aquí o en launchpad.



Pronto haré una entrada sobre todo el proceso para poder montar un paquete en launchpad para Ubuntu, siempre que me acuerde de todos los pasos, porque es lioso de cojones.

» Leer más, comentarios, etc...

Joan Garnet :: Arquitectura y desarrollo RIA

Upgrade de wordpress

Noviembre 20th, 2009 - [Enlace local]

Acabo de actualizar a WordPress 2.8.6, si alguien encuentra alguna anomalía en el funcionamiento del blog que por favor me avise. Por el momento no he detectado nada extraño…
Gracias!

» Leer más, comentarios, etc...

4 bits blog

Primeros pasos con django

Noviembre 20th, 2009 - [Enlace local]

Hace tiempo estuve aprendiendo a programar en Python porque me apeteció y porque me tocó lidiar en el trabajo con una aplicación web, la cual realicé en django.

¿Qué es django?

Django es un entorno de desarrollo (lo que en inglés se denomina framework) para la creación rápida de aplicaciones web, basándose en el MVC (Modelo-Vista-Controlador).

Entre todas las posibilidades que ofrece están que permite trabajar con las bases de datos como si no existieran, la creación de URL bonitas sin tener que tocar nada del servidor web, el uso de plantillas en (X)HTML para la creación de las diferentes páginas, y muchas cosas más, todas ellas estupendas y supongo que todas ellas también las tendrán otros entornos similares que utilicen otros lenguajes de programación.

Iniciando un nuevo proyecto

Lo primero que se debe hacer en django es crear lo que se denomina un proyecto, éste a su vez contendrá aplicaciones que serán las que contengan el código utilizado en la aplicación web. Por poner un ejemplo: el proyecto sería un CMS y las aplicaciones las diferentes funcionalidades de dicho CMS.

Para ello se deberá ejecutar:

$ django-admin.py startproject nombre_del_proyecto

Esto creará un directorio, llamado «nombre_del_proyecto», con los archivos básicos de todo proyecto de django:

Lo malo es que sólo con esto no se va a poder hacer nada más que cambiar unos cuantos valores de la configuración, para que un proyecto haga algo deberá tener al menos una aplicación que contendrá el código necesario.

Iniciando una nueva aplicación

Para crear una aplicación se debe usar el archivo «manage.py» del proyecto del siguiente modo:

$ manage.py startapp nombre_de_la_aplicación

Esto creará un directorio, llamado «nombre_de_la_aplicación», con los archivos básicos de toda aplicación de django:

Para que esta aplicación esté disponible en el proyecto, hay que añadirla a la lista de aplicaciones del proyecto que está en el archivo «settings.py».

INSTALLED_APPS = (
    'hello_world.hello_app',
)

Nota: Cuando se crea un nuevo proyecto django crea una lista de aplicaciones predeterminada, que en este caso daría lo mismo borrarlas.

Creando un «hola mundo»

Lo primero será crear un proyecto («hello_world») y una aplicación («hello_app») para dicho proyecto, tal y como se ha mencionado antes. Una vez hecho, se deberá crear una vista para la página web.

Creando una vista

Para la creación de una vista, se debe editar el archivo «views.py» de la aplicación creada del siguiente modo:

# views.py
# -*- coding: utf-8 -*-
# Views used in hello world application.

from django.shortcuts import get_object_or_404, render_to_response

def hello:
    """Hello view function."""

    return render_to_response ('hello.htm', { 'hello_msg' : '¡Hola mundo!' })

La función render_to_response usa una plantilla (que ahora se explicará) para crear la página web de la vista.

Creando una plantilla

Django dispone de un lenguaje propio para la creación de plantillas, básicamente son archivos HTML con unas etiquetas especiales, que se marcan con llaves dobles, que django se encargará de rellenar.

De este modo, crear una plantilla es tan sencillo como crear un archivo html en un directorio llamado «templates» dentro del directorio del proyecto o de la aplicación. En este caso valdría crear un archivo llamado «hello.htm» (es el que se usa en la función hello) en el directorio «templates» de la aplicación «hello_app».






    
    {{ hello_msg }}



    

{{ hello_msg }}

Esta es una plantilla muy sencilla, el lenguaje de las plantillas de django permite realizar muchas más cosas, como herencia entre plantillas, definir bloques, bucles, …

Definiendo una dirección para la vista

Una vez creada la vista y su plantilla, habrá que definir una dirección para poder acceder a la vista. Como se mencionó al principio, para eso está el archivo «urls.py» del proyecto. Este archivo contiene una lista de direcciones a las que se les asignará unas vistas, de modo que cuando se abra esa dirección la vista asignada se encargue de crear su correspondiente página web.

En este caso, el archivo «urls.py» quedaría del siguiente modo:

# urls.py
# -*- coding: utf-8 -*-
# Django URL patterns for hello world project.

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'', 'hello_world.hello_app.views.hello'),
)

Como se puede ver, lo único que se hace es añadir un patrón (una expresión regular) con una vista asociada. En este caso se trata de una dirección vacía (r''), la raíz del dominio, a la que se asocia la función hello que se definió en las vistas de la aplicación.

Probando la aplicación web

Una vez llegado a este punto, ya se puede probar la aplicación web, para ello no hace falta tener ningún servidor web instalado, ya que django dispone de uno para realizar pruebas. Para ejecutarlo, desde el directorio del proyecto:

$ python manage.py runserver

Y aquí concluye esta introducción básica, pero larga, próximamente más. Si os ha gustado y queréis ir aprendiendo por vuestra cuenta, siempre podéis leer la documentación oficial de django.

» Leer más, comentarios, etc...

Arragonán

Testeando respuestas JSON con Grails

Noviembre 20th, 2009 - [Enlace local]

Aunque en la documentación de Grails aparece un ejemplo de como escribir un test para una acción que recibe una petición en formato XML o JSON, no lo hace de como escribir un test de una acción que genera el JSON, que tampoco es que sea precisamente complicado.

Si lo único que quisieramos probar es que, en una cadena JSON, se está devolviendo algún valor que estuvieramos esperando, podríamos utilizar el contenido que devuelve la response como una cadena(con contentAsString) y hacer alguna comprobación sobre el texto:

def response = controller.response.contentAsString
assertTrue response.contains('"name":"Gustavo Poyet"')
assertTrue response.contains('"goals":63')

Pero claro, este test es un muy ligero y no prueba demasiado. Si quisieramos probar más a fondo la respuesta(estructura, un elemento concreto de un array, etc), podríamos usar contains sobre la cadena devuelta, convertir tipos… vamos, que sería más que posible que el código del test terminara siendo infumable. Pero está disponible out of the box el converter JSON de Grails, que permite testear más a fondo fácilmente y con un código mucho más claro.

Es muy posible que si alguien se ve en la necesidad de escribir un test para una acción que devuelve un JSON, ya conozca el converter, por que es una de las alternativas que hay para hacerlo:

render object as JSON

Pues esa misma clase, tiene el paso contrario, pasar una cadena a un objeto groovy/java(JSONElement). De esta forma ya nos ahorraremos trabajo sucio y el código quedará bastante claro:

def response = controller.response.contentAsString
def responseJSON = grails.converters.JSON.parse(response)
assertNotNull responseJSON.players
assertEquals 5, responseJSON.players.size()
assertEquals 63, responseJSON.players[0].goals
assertEquals "Gustavo Poyet", responseJSON.players[0].name

» Leer más, comentarios, etc...

Fetishcode...Thinking in objects

Nueva demo de ADF Faces 11g

Noviembre 19th, 2009 - [Enlace local]

A

» Leer más, comentarios, etc...

Variable not found

Publicado ASP.NET MVC 2 Beta

Noviembre 18th, 2009 - [Enlace local]

mvc-logo-landing-pageHace sólo unas horas Haack ha anunciado en su blog la publicación de la versión beta del  framework ASP.NET MVC 2, que tenemos a nuestra disposición tanto a nivel de código fuente como de instalador binario.

Echando un vistazo rápido a las release notes, las novedades son:

Y ojo, que esta beta sólo funciona con Visual Studio 2008 SP1, no podemos probarla con la última beta de Visual Studio 2010. Eso sí, puede convivir en nuestros equipos con MVC 1.0 sin interferencias.

El siguiente paso será la publicación de la RC, que está prevista “antes de finalizar el año”, aunque no se planea incluir funcionalidades nuevas, simplemente corregir bugs y pequeñas mejoras que puedan derivar de esta fase de pruebas y del feedback de los usuarios. Por tanto, en líneas generales, lo que nos trae esta beta será prácticamente lo que vendrá en la versión 2 :-)

Enlaces:

Publicado en: Variable not found.



» Leer más, comentarios, etc...

Buayacorp

Chuleta para hacer temas en Drupal

Noviembre 17th, 2009 - [Enlace local]

Un interesante cheatsheet o chuleta para hacer temas en Drupal.

» Leer más, comentarios, etc...

Información legal y técnica