Subgurim SiteMap, generación dinámica de SiteMaps para ASP.NET

Upload/logo.jpg

Acaba de salir la primera versión oficial del Subgurim SiteMap para ASP.NET.
Echadle un vistazo al Microsite (podéis elegir entre castellano y en inglés). A continuación, os remito la pequeña introducción a lo que hace la librería (sacado de la página principal del Subgurim SiteMap):

¿Qué es el protocolo SiteMaps?
Siguiendo el protocolo Sitemaps el webmaster informará a los buscadores de cuáles son las páginas de su website, así como darle información variada sobre cada una de éstas, como la última fecha de modificación, la frecuencia con que se suele modificar o su importancia relativa.
Dando esa información, facilitamos la vida a los buscadores, y como consecuencia última mejoraremos nuestro lugar en las búsquedas.
Pero tener un SiteMap no es ni mucho menos trivial: hay que crear un documento XML con un elemento para todas y cada una de las páginas de nuestra Web...

¿Qué es el SubgurimSiteMap?
Con el SubgurimSiteMap nuestro website dispondrá de un SiteMap automáticamente, dinámicamente y sin que tengamos que hacer absolutamente NADA. simplemente seguimos unos sencillos pasos de instalación, configuramos, si queremos, un par de cosillas y... ¡¡Ya está!!
Además, nos diferenciamos de otros generadores de SiteMap en que:
  • Nuestro fichero sitemap crecerá poco a poco.
  • El coste de rendimiento es despreciable.
  • Funciona igual en servidores compartidos que en servidores privados.
  • Acepta URLs dinámicas sin ningún problema, tan sencillamente como acepta URLs estáticas.

MicroWeb: http://es.sitemap.subgurim.net



Regex + WebClient = mi primer Spider

Sólo como curiosidad y nunca para que lo useis en la práctica, en este artículo veremos cómo averiguar los enlaces salientes que tiene una página.

Dicho de otro modo, lo que haremos será descargarnos el contenido de una página determinada y ubicarlo en un string. Luego, utilizaremos expresiones regulares (ver artículos expresiones regulares y uso de expresiones regulares en ASP.NET) para determinar el contenido del atributo "href" dentro del tag de enlazado "a".

Veamos el código:

    /// <summary>
    /// Función q
ue hace las veces de robot o spider
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    private static string spider(Uri url)
    {
        // Expresión regular que lee el contenido del href
        string pattern_Href = @"(?<=<a[^<>]+href=""?)[^""<>]+(?=""?[^<>]*>)";

        // Recogemos el contenido de la url como un string
        string html = getHTML(url);

        // Recogemos la colección de resultados que nos da Regex
        MatchCollection col = Regex.Matches(html, pattern_Href, RegexOptions.IgnoreCase);

        StringBuilder sb = new StringBuilder();
        // Añadimos al stringbuilder cada uno de los resultados
        foreach (Match match in col)
        {
            sb.Append(match.Value);
            sb.Append("<br />");
        }

        return sb.ToString();
    }

    /// <summary>
    /// Recoger el contenido de una Uri como string
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    private static string getHTML(Uri url)
    {
        // Absolutamente sencillo!!
        WebClient wc = new WebClient();
        return wc.DownloadString(url);
    }

    /// <summary>
    /// Recoger el contenido de una Uri como string de forma más trabajada
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    private static string getHTML2(Uri url)
    {
        string retorno = string.Empty;

        // Creamos un HttpWebRequest
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        try
        {
            // Recogemos la respuesta
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                // Leemos el resultado de "Status Code"
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    // Tampoco es demasiado difícil leer el contenido como un string ;)
                    StreamReader sr = new StreamReader(response.GetResponseStream());
                    retorno = sr.ReadToEnd();
                }
            }
        }
        catch
        { retorno = string.Empty; }

        return retorno;
    }


Como vemos, el código se explica a sí mismo.

Los puntos más importantes son:
- La expresion regular pattern_Href... esta me la he inventado yo, pero seguramente habrá mejores.
- La recogida de la url como un string. Vemos que hay dos modos de hacerlo. El más sencillo simplemente sirve para lo que sirve y poco más... el más complejo se asegura de que la Web a la que intentamos acceder existe y además devuelve un código "200 OK", es decir que es correctamente legible.



Nuevas versiones del GoogleMaps.Subgurim.NET y el FileUploadAJAX

           

Durante los últimas días se han lanzado nuevas versiones de dos controles que seguro que ya conocéis bien:
- FileUploadAJAX (v1.1): corrección de bugs y un montón de nuevas funcionalidades para el control que permite subir ficheros al servidor de forma asíncrona (con apariencia AJAX).
- GoogleMaps.Subgurim.NET (v2.1): adaptación a las últimas novedades del la API oficial de GoogleMaps, correción de bugs y nuevas funcionalidades como geoCoding Inverso. Recordamos que este control te permite utilizar los mapas de Google en tu Web de forma muy sencilla.

Si no conocéis los controles, os recomendamos que les echéis un vistazo, pues seguro que en un momento u otro os serán necesarios para vuestros proyectos .



Manejar en código el login y registro de un usuario

Gracias a los controles CreateUserWizard y Login de ASP.NET 2.0, crear un usuario y/o ingresarlo en el sistema es tremendamente fácil. Sin embargo, estos controles, al tener una naturaleza tan sencilla, consiguen -por defecto- unos resultados igualmente sencillos.

Aumentar sus capacidades no supone otra cosa que manejar algunos de sus eventos, crear nuevos pasos o personalizar sus templates.

Sin embargo este no es el objetivo de este artículo, lo que nosotros vamos a ver es cómo podemos manejar con nuestro propio código el registro de un usuario así como su ingreso en el sistema.


Registro de un usuario
Y como más vale un ejemplo que mil palabras, ahí va nuestro código para registrar un usuario (recordad importar el namespace "System.Web.Security"):

    protected void registrar(string usuario, string password, string email, string pregunta, string respuesta)
    {
        MembershipCreateStatus status;

      // Tratamos de crear el usuario
        Membership.CreateUser(usuario, password, email, pregunta, respuesta, true, out status);

      // Comprobamos el estado de la creación del usuario
        if (status == MembershipCreateStatus.Success)
        {
            Roles.AddUserToRole(usuario, "Usuario");
        // Podemos realizar otras acciones como sumar una visita, etc.

// Ingresamos el usuario: nuestra próxima función
ingresar(usuario, password, true);
        }
        else
        {
        // Si no se ha podido crear el usuario,
        // Manejemos cuál ha sido el motivo
            switch (status)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    lb_Error.Text = "Hay otro usuarios con el mismo nombre";
                    break;
                default:
                    lb_Error.Text = "Ha habido un error de registro: " + status.ToString();
                    break;
            }
            lb_Error.Visible = true;
        }
    }



El punto clave aquí es el "Membership.CreateUser(...)", donde le pasamos nombre, contraseña y demás información, recogiendo el estado del registro de usuario en nuestra variable "status", un enumerador del tipo "MembershipCreateStatus".

Teniendo la variable "status" podemos manejar cualquier cosa que haya salido mal. Nosotros nos limitamos mostrar un mensaje personalizado si hay otro usuario con el mismo nombre y un mensaje generalizado para cualquier otro tipo de error.

Como vemos, si el registro del usuario ha sido satisfactorio, hemos decidido añadir el usuario al Rol "Usuario" (que se supone un Rol ya creado) y posteriormente ingresarlo automáticamente en el sistema (función que explicaremos a continuación). Pero nosotros podemos hacer lo que queramos, dependiendo de cómo tengamos preparada nuestra aplicación: podemos sumar una visita en nuestro sistema o cualquier otra cosa que se nos ocurra.


Ingreso de un usuario
Ingresar a un usuario es, cuanto menos, tan sencillo como registrarlo, veamos el código comentado y posteriormente lo estudiaremos:

    protected void ingresar(string usuario, string password, bool recuerdame)
    {
        // Tratamos de validar al usuario
        if (Membership.ValidateUser(usuario, password))
        {
            // La variable recuerdame indica si queremos que el usuario
            // Entre automáticamente al sistema la próxima vez
            FormsAuthentication.SetAuthCookie(usuario, recuerdame);

            // Ahora el usuario ya está validado y debemos decidir donde
            // redirigirlo. Habitualmente, si la url tiene el querystring "returnUrl"
            // le devolvemos allí, y si no podemos decidir si enviar al usuario
            // al nuestra página principal, a la misma página en que está o donde queramos.
            if (Request.QueryString["returnUrl"] != null)
            {
                Response.Redirect(HttpUtility.UrlEncode(Request.QueryString["returnUrl"]));
            }
            else
            {
                Response.Redirect("~/");
                //Response.Redirect(Request.Url.AbsoluteUri);
            }
        }
        else
        {
            // La validación no ha podido llevarse a cabo!!
            // Probemos si es debido a que el usuario existe pero está desactivado
            // O el usuario directamente no existe (podríamos mejorar estas comprobaciones)
            if ((Membership.GetUser(usuario) != null) && (Membership.GetUser(usuario).IsLockedOut))
            {
                lb_Error.Text = "Tu cuenta de usuario ha sido bloqueada";
            }
            else
            {
                lb_Error.Text = "Fallo en tu nombre de usuario o contraseña";
            }
            lb_Error.Visible = true;
        }
    }


Como podemos observar, la clave aquí está en "Membership.ValidateUser(usuario, password)", que trata de validar al usuario y nos devuelve un booleano mostrando el éxito o no de la validación/ingreso del usuario en el sistema.

Si lo ha validado correctamente, actuamos en consecuencia: decidimos si marcar una cookie para recordar al usuario en nuevas visitas y posteriormente mandamos lo mandamos a la Url que decidamos.

Si no ha sido validado, mostramos en nuestra Label (que se supone creada en nuestro ".aspx" o ".ascx") un mensaje indicando el problema que ha habido.


Conclusión
Sí, los controles CreateUserWizard y Login son muy sencillos de usar y muy potentes si usamos todas sus funcionalidades (eventos, templates, personalización)... pero ahora ya podemos crear una ligera secuela de estos nosotros mismo, comprobando que no tienen ningún secreto.




Mi primera base de datos SqlServer Express con VWD

¿Cómo se crear una base de datos SQLServer Express con el Visual Web Developer?
Los que ya lo hayan hecho se extrañarán de que esto sea una pregunta, porque es muy sencillo llevarlo a cabo... pero los que no lo hayan hecho nunca tendrán un mar de dudas al respecto.

Y es que la mayoría de tutoriales explica, y muy bien, las excelencias de ADO.NET, los controles de datos, etc. Pero pocos hay que simplemente creen una base de datos paso a paso sobre la que poder empezar a trabajar. Así que pongámonos manos a la obra, pues en muy poquitos pasos vamos a crear un base de datos, una tabla y algún que otro procedimiento almacenado.

Crear una base de datos SQLServer Express
SQLServer Express es el motor de base de datos que Microsoft proporciona gratuitamente. Es ideal para probarla en entornos de desarrollo, pero nunca para entornos de trabajo.

El Visual Web Developer trabaja a las mil maravillas con SqlServer Express.

Como sabéis en ASP.NET existen ciertas carpetas con nombre especiales que tienen funcionalidades específicas. En nuestro caso nos interesa la carpeta App_Data, preparada para almacenar todo tipo de datos. En especial, nosotros la debemos usar para crear nuestra base de datos.

1.- Creamos la carpeta App_Data (si no está creada ya) y obtenemos esto:

Upload/crearbbdd.jpg

2.- Botón de la derecha --> Añadir un nuevo Item
Upload/crearbbdd-2.jpg

3.- Creamos una base de datos y la nombramos "personal.mdf"

Upload/crearbbdd-3.jpg


4.- Con lo que la base de datos queda ubicada en nuestra carpeta App_Data.

¡¡Ya tenemos nuestra base de datos creada!!


Creación de tablas
Para manejar cualquier cosa de la base de datos deberemos entrar en el "Database Explorer", lo que conseguimos sin más que hacer doble click sobre el fichero "personal.mdf":

Upload/creartabla.jpg


Vamos a crear un tabla presionando con el botón de la derecha sobre Table --> añadir nueva tabla.

Obtendremos una tabla vacía:

Upload/creartabla-2.jpg

Vamos a rellenarla con dos campos ejemplo:
- Un entero llamado Id, que debe ser autonumérico (Identity) y clave principal.
- Un nvarchar(50) llamado Texto

1.- Creamos los campos
Upload/creartabla-3.jpg

2.- Hacemos que la clave sea un identity y la asignamos como clave principal

Upload/creartabla-4.jpg    
Upload/creartabla-5.jpg

Finalmente la guardamos con el nombre que queramos (por ejemplo Table1) y ya tenemos nuestra primera tabla


Crear un procedimiento almacenado
El "modo tradicional" de interactuar con una base de datos era con simples sentencias SQL que le mandamos desde nuestro código a la base de datos. Con los procedimientos almacenados, esas sentencias SQL están ubicadas físicamente en el propio motor de base de datos, ganando muchísimo en velocidad y seguridad.

Nosotros vamos a crear dos procedimientos almacenados sencillos, para ver cómo funciona el mecanismo de creación.

Volviendo a nuestro "database explorer" presionamos botón de la derecha sobre "Procedimientos almacenados" o "Stored Procedures" y añadimos uno nuevo:

Upload/crearsp.jpg

La primera linea es la declaración de creación del procedimiento almacenado (CREATE PROCEDURE).
"dbo" es el propietario de la base de datos y "StoredProcedure1" es el nombre de la base de datos. Nosotros vamos a cambiar ese nombre por "Insertar".

Las siguiente líneas (que por defecto aparecen comentadas) son los parámetros de entrada/salida del procedimiento almacenado. Por ejemplo, nosotros vamos a insertar un registro a nuestra Table1, por lo que el parámetro de entrada será el Texto.

Quedaría por tanto así:

Upload/crearsp-2.jpg

Por defecto, los procedimientos almacenados devuelven un entero indicando la cantidad de registros que se han visto afectados por la acción. Por ejemplo, modificar un registro nos devolvería un "1", y los mismo para borrar o seleccionar. La cuestión es que pocas veces es útil esa información, por eso el programa nos ofrece (dejando comentado) la opción de forzar a que el procedimiento almacenado no nos devuelva ninguna información al respecto. Descomentamos pues la línea "SET NOCOUNT ON" y de ese modo ahorramos en ancho de banda y mejoramos en velocidad.

Y ahora viene la parte que más me gusta: insertar el SQL. Bajo "SET NOCOUNT ON" haremos click en el botón secundario y seleccionaremos "insert SQL"

Upload/crearsp-3.jpg

Elegimos la tabla sobre la que queremos trabajar y presionamos sobre "Add":

Upload/crearsp-4.jpg

Por defecto nos aparece una sentencia INSERT en el cuadro de texto donde se ubica el SQL, pero lo podemos cambiar sin más que presionar sobre el botón de la derecha y elegir...

Upload/crearsp-5.jpg

Para crear nuestra sentencia SQL de inserción, no tenemos más que jugar un poco con el QueryBuilder de modo que obtenemos esto:

Upload/crearsp-6.jpg


Cuando ya tengamos la sentencia SQL formada, presionamos sobre OK y volveremos a nuestro procedimiento almacenado:

Upload/crearsp-7.jpg

¡Ya tenemos nuestro procedimiento almacenado hecho!

Pero vamos a probarlo. Primero debemos guardarlo. Al hacerlo veremos que queda todo igual, excepto que "CREATE" cambia por "ALTER". Resulta obvio decir que como el procedimiento almacenado ya está creado, lo que hagamos ahora será una modificación o alteración. Pero a nosotros eso no nos va a afectar en nada. Simplemente guardaremos como si fuera un fichero cualquiera y ya está.

Una vez hayamos guardado el procedimiento almacenado, hacemos botón de la derecha --> execute

Upload/crearsp-8.jpg

Y nos aparecerá una ventana donde nos pide que le demos valor a los parámetros de entrada del procedimiento almacenado. En este caso, sólo tenemos un parámetro. Le damos un valor cualquiera y presionamos OK:

Upload/crearsp-9.jpg



Vemos el resultado que nos da la ventana de salida u "output":

Upload/crearsp-10.jpg

¿Verdad que es sencillo?

Ahora vamos a hacer rápidamente un procedimiento almacenado que haga un SELECT de todos los registros dentro de Table1:

Upload/crearsp-11.jpg

Le hacemos un execute y obtenemos:

Upload/crearsp-12.jpg


Pues bien, eso es todo, espero que ahora sea más sencillo crear un BBDD con SqlServer Express



RSS con XmlSerializer para ASP.NET


RSS (Really Simple Syndication) se ha convertido en el estándar de facto en Internet para la sindicación de información... ¿Sindicación de información?... dicho de otro modo, el estándar RSS propone el formato en que la información (por ejemplo las noticias de un periódico o los artículos en un blog) debe mostrarse de cara al público. No se trata más que de otro "dialecto" XML.

Por ejemplo, imaginemos que tenemos una Web con una sección sobre noticias relacionadas con las discotecas y el mundo de la noche. Por la filosofía de nuestra página, queremos compartir las noticias de modo que cualquiera pueda tener acceso a éstas sin necesidad de acceder a la Web. Para ello no tenemos más que proporcionar estas noticias en formato RSS, de modo que pueda ser leído tanto por programas lectores (llamados comúnmente agregadores) como por otros webmasters que decidan incorporar esas noticias en sus propias Webs.

Veamos, por ejemplo un trozo representativo del RSS de este mismo Blog (en el momento en que este artículo ha sido escrito):

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>Subgurim.NET</title>
    <link>http://www.subgurim.net</link>
    <description>Blog ASP.NET 2.0 con C# en castellano</description>
    <ttl>1200</ttl>
    <language>es</language>
    <generator>RSS.Subgurim.NET 1.1</generator>
    <item>
      <title>Galerías de código e iconos en GoogleMaps.Subgurim.NET</title>
      <description>
        Cuerpo del artículo 1º
    </description>
      <link>http://www.subgurim.net/Articulos/asp-net-general-articulo125.aspx</link>
      <pubDate>Thu, 11 Jan 2007 12:44:46 GMT</pubDate>
      <guid>http://www.subgurim.net/Articulos/asp-net-general-articulo125.aspx</guid>
    </item>
    <item>
      <title>GoogleMaps.Subgurim.NET versión 2.0</title>
      <description>
        Cuerpo del artículo 2º
    </description>
      <link>http://www.subgurim.net/Articulos/controles-de-usuario-articulo124.aspx</link>
      <pubDate>Fri, 05 Jan 2007 20:50:08 GMT</pubDate>
      <guid>http://www.subgurim.net/Articulos/controles-de-usuario-articulo124.aspx</guid>
    </item>
  </channel>
</rss>


Con lo que vemos, podemos intuir que el formato RSS 2.0 define unos primeros campos donde se define en general al RSS para, posteriormente posteriormente definir los items bajo su campo "Item" en el que vendrá la información específica de (en nuestro caso específico) los artículos escritos en el Blog.

Para una completa especificación del formato RSS 2.0, podéis visitar http://www.rssboard.org/rss-specification. A continuación mostraremos los campos que nosotros vamos a manejar.

Elementos de Canal
Mostraremos los campos que definen al RSS en general. Los tres primero son obligatorios, el resto opcionales:
- title: Es el título que le daremos a nuestro documento RSS y con el que se dará a conocer en cualquier lector de RSS. En nuestro ejemplo es "Subgurim.NET"
- link: enlace a la página a la que se refiere el documento RSS. En nuestro ejemplo mostramos un enlace a éste Blog: http://www.subgurim.net.
- description: algo sencillito y descriptivo de lo que se puede encontrar en este RSS, como por ejemplo: "Blog ASP.NET 2.0 con C# en castellano".
- ttl: sugiere la cantidad de minutos que debería ser cacheada la información. Es como decirle al lector de RSS "en tantos minutos no suelo añadir más información, por lo que puedes dejarlo en tu cache".
- language: idioma en que está escrita la información.
- generator: el programa o algoritmo que ha creado el RSS. En nuestro ejemplo lo llamamos "rss.subgurim.net 1.1" (por poner algo ;) )

Elementos de Item
Vayamos ahora con los elementos Item. En nuestro ejemplo se trataría de los artículos
- title: título del artículo, y que será lo primero que se muestre en un lector RSS.
- link: url del artículo, de modo que desde el lector se pueda viajar hacia la fuente original del artículo.
- description: en nuestro ejemplo es el texto completo del artículo, pero habitualmente no suelen ser más que las primeras líneas. La idea es que el  que lea sobre un lector RSS se interese sobre la noticia y posteriormente enlace al artículo original mediante link.
- pubDate: representa la fecha y hora en que el artículo fue escrito. Debe estar en el formato de tipo "Fri, 05 Jan 2007 20:50:08 GMT". Esto se consigue con aplicando ToString("R") al DateTime correspondiente.
- guid: representa a un identificador global único... y aquí se acaba el estándar. Los lectores RSS lo van a ver como un string, y nosotros somos quienes nos debemos de preocupar de que este identificador sea único. Por ejemplo, yo he decidido que, dado que cada artículo representa una única URL, el guid sea esa misma url.


Como habréis visto en las especificaciones oficiales, hay más elementos de RSS, pero son opcionales y, por simplicidad, no los vamos a poner... además, tampoco hacen demasiada función ;)


La clase RSS
Ahora sólo nos queda la tarea de hacer unas clases C# que respondan convenientemente a la estructura de RSS. El fichero lo podéis descargar (el enlace está debajo del título).

En líneas generales, tenemos tres clases: RSS, RSSChannel y RSSItem.

RSS tiene un único elemento Channel, y éstetiene como propiedades los elementos de canal y un listado genérico de RSSItem. En RSSItem encontraremos las propiedades que representan los elementos del Item.


Leer y escribir RSS
En el mismo fichero de descarga habréis visto dos ficheros ".cs": RSS.cs y RSSBLL.cs

RSS.cs contiene las tres clases de las que hemos hablado. Fijaos que se han añadido algunos atributos XML a las propiedades de las clases (XmlElement, XmlRoot y XmlIgnore)... ¿qué es eso?

Para leer y mostrar el fichero RSS vamos utilizar la clase XmlSerializer, ubicada dentro del namespace System.Xml.Serialization.

XmlSerializer hace un poquito de magia, que nos facilita el trabajo tremendamente. Dándole el tipo de una clase, es capaz de mostrarnos su equivalente XML, y viceversa: rellenar una instancia de la clase a partir de un XML.

Aplicado a nuestro ejemplo, veremos que a partir de la clase RSS, el XmlSerializer lo transformará en un fichero RSS mediante su método "Serialize"; y también, a partir de cualquier fichero RSS, rellenará una instancia a un objeto RSS mediante su método "Deserialize".

¿Y cómo hace esto? XmlSerialize utiliza propiedades de refactoring, es decir, que lee los metadatos para saber el nombre de las propiedades de una clase, su tipo, etc. Y ahí viene lo que comentábamos con lo de los atributos XML (XmlElement, XmlRoot y XmlIgnore), pues de alguna forma debemos decirle al XmlSerializer que la propiedad pubdate_DateTime no la muestre por XML o que la raíz del XML es "rss" y no "RSS".

Pero lo mejor es que le echéis vosotros mismos un vistazo al código, porque, como digo siempre, un ejemplo vale más que mil palabras.

        public static void writeRSS(RSS rss)
        {
            XmlSerializer ser = new XmlSerializer(typeof(RSS));
            StringBuilder sb = new StringBuilder();
            using (StringWriter sw = new StringWriter(sb))
            {
                using (XmlTextWriter writer = new XmlTextWriter(sw))
                {
                    writer.Formatting = Formatting.Indented;
                    ser.Serialize(sw, rss);
                }
            }

            HttpResponse response = HttpContext.Current.Response;

            response.Clear();
            response.ContentType = "text/plain";
            response.Write(sb.ToString());
            response.Flush();
            response.End();

        }

        public static RSS readRSS(string url)
        {
            RSS rss = new RSS();

            XmlSerializer ser = new XmlSerializer(typeof(RSS));

            using (XmlTextReader reader = new XmlTextReader(url))
            {
                rss = ser.Deserialize(reader) as RSS;
            }

            return rss;
        }


¡¡Absolutamente sencillo!!

Podéis hacer una prueba inmediata muy sencilla (tras importar el namespace Subgurim):

    RSSBLL.writeRSS(RSSBLL.readRSS("http://www.subgurim.net/rss/categorias.aspx"))




Galerías de código e iconos en GoogleMaps.Subgurim.NET

GoogleMaps.Subgurim.NET se ha convertido en solo medio año en el mejor control de Google Maps para ASP.NET.

Tras la liberación de su versión 2.0, GoogleMaps.Subgurim.NET ha inaugurado en su MicroWeb dos nuevas secciones: una galería de código y una galería de Iconos.

En la galería de código, la comunidad podrá compartir sus trabajos o pequeñas utilidades con el control, mientras que la galería de iconos cuenta con más de 250 iconos para su uso directo con el control.

¡Os animo a utilizarlo!





Detección de idioma

Como ya sabréis, ASP.NET es capaz de autodetectar el idioma del navegador de tu visitante. Sin embargo hay ocasiones en que querremos hacer esa detección con nuestro código.

En el ejemplo vamos a detectar todos los idiomas configurados en nuestro navegador y crearemos su CultureInfo correspondiente. Para ello, utilizaremos la propiedad Request.UserLanguages e importaremos el namespace System.Globalization:

detectaIdiomas.cs
        StringBuilder sb = new StringBuilder();

        CultureInfo culture;
        string _lng;
        foreach (string lng in Request.UserLanguages)
        {
            _lng = lng.Split(';')[0];

            try
            {
                culture = new CultureInfo(_lng);
            }
            catch (ArgumentException ae)
            {
                _lng = _lng.Split('-')[0];
                culture = new CultureInfo(_lng);
            }

            sb.Append(culture.NativeName);
            sb.Append("<br />");
        }

        Label1.Text = sb.ToString();


Como vemos el código no es tan inmediato como cabía esperar. Sería todo ideal si valiera con hacer:

    culture = new CultureInfo(lng);

Pero como cada navegador es de su padre y de su madre es necesario poner un par de parches.

El primero de ellos, marcado en rojo, se asegura de coger la información válida de lengua-CULTURA, porque cuando hay varios idiomas configurados en un mismo navegador, a partir del segundo idioma aparece un parámetro extra. Por ejemplo el listado de lenguas de un navegador con tres lenguas podría ser:

    es
    en-US;q:0,7
    fr-FR;q:0,3


El segundo de ellos, marcado en verde, se debe a que algunos navegadores se pasan los estándares por el xxxx, y ofrecen pares "lengua-CULTURA" que no están estandarizados (por ejemplo es-US). Lo único que se hace es que si da error de argumento, sólo coja la lengua y se olvide de la cultura.

Recuerdo que en el ejemplo no solo se muestran las lenguas del navegador sino que se inicia su cultura correspondiente. Esto último no es necesario, y si no lo usamos, quedaría:

muestraIdiomas.cs
        StringBuilder sb = new StringBuilder();

        string _lng;
        foreach (string lng in Request.UserLanguages)
        {
            sb.Append(
lng);
            sb.Append("<br />");
        }

        Label1.Text = sb.ToString();