martes, 29 de enero de 2013

Blogger: icono para dispositivos Apple

Si desde un dispositivo Apple (Iphone o Ipad) accedes a tu blog de Blogger (o realizado con cualquier otra plataforma) puedes añadir una página en la pantalla de inicio del dispositivo pero ¿con qué icono saldrá? En este artículo te explicaré cómo personalizarlo.

Si no has hecho nada por personalizar el icono para dispositivos Apple lo más normal es que si añades una página en la pantalla de inicio del dispositivo te salga un icono con una especie de miniatura de la página que has añadido y con las esquinas redondeadas. Lo normal es que quieras personalizar este icono:

  1. Crea un icono personalizado de 57x57 píxeles, guárdalo en formato png y súbelo a algún sitio donde esté accesible desde Blogger (dropbox, Google Drive,...). En el caso de areaTIC lo que tengo es una página de Blogger que no publico y donde añado este icono, los de redes sociales,... una vez añadidos el enlace que me genera lo puedo utilizar en las distintas páginas artículos del blog. El icono creado para areaTIC es el siguiente (no es necesario que redondees las esquinas, ni lo sombrees,... se encarga tu dispositivo Apple por tí).


    Si queremos más información sobre los iconos para dispositivos Apple, consejos para su creación,... podemos acceder a la siguiente página de la iOS Developer Library.

  2. Entramos en Blogger, vamos al apartado Plantilla y pulsamos sobre el botón "Edición de HTML".


  3. Buscamos en el HTML de la plantilla el código:
    </head>
    
  4. Justo encima del código anterior añadimos el código siguiente:
    <link rel='apple-touch-icon' href='http://.../iphoneicon.png' />
    
    Donde:
    • "iphoneicon.png" es el nombre del icono que has creado en el paso 1.
    • "http://.../iphoneicon.png" es la URL correspondiente al icono.
    Tras los cambios guardamos la plantilla.

  5. Ahora llega el momento de probarlo, si disponéis de un dispositivo Apple (iPhone o iPad) es muy sencillo, ¿pero y si no lo tenemos? Podemos acceder al blog de Duopixel y utilizar su simulador de iPhone, seleccionas la imagen creada en el paso 1 la subes y podrás ver el resultado. En un iPad se visualiza de igual forma.


Y hasta aquí el artículo de hoy, espero que os haya sido interesante, dentro de areaTIC puedes encontrar otros artículos, no dudes en consultar nuestro archivo; también puedes seguirnos por RSS o las principales redes sociales (twitter, facebook, linkedin...)


sábado, 26 de enero de 2013

WCF ChannelFactory<T>. Cómo simplificar la creación de clientes WCF.

En el artículo anterior vimos como usar ChannelFactory para crear un cliente WCF, hoy veremos un ejemplo sobre como crear nuestra clase "proxy" que podría servirnos para simplificar la creación de clientes WCF y no complicar la vida a los miembros del equipo que trabajen en presentación.

En el mismo proyecto AreaTIC.Client crearemos un clase AreaTicWCFProxy que implemente la interfaz ICommunicationObject y que en el constructor simplemente obligue a pasar el nombre del servicio al creador, dentro ya implementaríamos todo el tema de bindings, seguridad y demás aspectos configurables del cliente. Veamos un ejemplo que se podría ir complicando en función de las características de cada arquitectura.
using System.ServiceModel;

namespace AreaTIC.Shared
{
 public class AreaTicWCFProxy:ICommunicationObject
 {
   private ChannelFactory mfactory = null;

   public AreaTicWCFProxy(string Address)
   {
     //definimos binding.
     WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);

     //definimos endpoint 
     EndpointAddress endPoint = new EndpointAddress(string.Format("http://localhost:8080/Services/{0}",Address));

     //definimos factory y creamos canal.
     mfactory = new ChannelFactory(binding, endPoint);

     mfactory.Open();
    }

    public T client 
    {
      get { return mfactory.CreateChannel(); }
    }

    void ICommunicationObject.Abort()
    {
      mfactory.Abort();
    }

    IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
    {
      throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state)
    {
       throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
    {
       throw new NotImplementedException();
    }

    IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state)
    {
       throw new NotImplementedException();
    }

    void ICommunicationObject.Close(TimeSpan timeout)
    {
       mfactory.Close(timeout);
    }

    void ICommunicationObject.Close()
    {
       mfactory.Close();
    }

    event EventHandler ICommunicationObject.Closed
    {
       add { throw new NotImplementedException(); }
       remove { throw new NotImplementedException(); }
    }

    event EventHandler ICommunicationObject.Closing
    {
       add { throw new NotImplementedException(); }
       remove { throw new NotImplementedException(); }
    }

    void ICommunicationObject.EndClose(IAsyncResult result)
    {
       throw new NotImplementedException();
    }

    void ICommunicationObject.EndOpen(IAsyncResult result)
    {
       throw new NotImplementedException();
    }

    event EventHandler ICommunicationObject.Faulted
    {
       add { throw new NotImplementedException(); }
       remove { throw new NotImplementedException(); }
    }

    void ICommunicationObject.Open(TimeSpan timeout)
    {
       mfactory.Open(timeout);
    }

    void ICommunicationObject.Open()
    {
       mfactory.Open();
    }

    event EventHandler ICommunicationObject.Opened
    {
       add { throw new NotImplementedException(); }
       remove { throw new NotImplementedException(); }
    }

    event EventHandler ICommunicationObject.Opening
    {
       add { throw new NotImplementedException(); }
       remove { throw new NotImplementedException(); }
    }

    CommunicationState ICommunicationObject.State
    {
       get { return mfactory.State; }
    }
  }
}
Si os fijáis en fragmento de código hemos definido un atributo privado ChannelFactory y a partir de los parámetro que recibimos en el constructor de nuestra clase configuraríamos el canal.

El resto son métodos que obliga a implementar la interfaz ICommunicationObject, en este caso no he implementado el tema de asíncronos ni los eventos... Implementamos los básicos Open, Close (...) para gestionar el canal.

Para conectar al servicio que levantamos en el artículo anterior el código sería tan simple como esto:
using AreaTIC.Shared.ServiceContracts;
using AreaTIC.Shared.DataContracts;
using AreaTIC.Shared;

namespace AreaTIC.Client
{
 class Program
 {
   static void Main(string[] args)
   {
    string result = new AreaTicWCFProxy("EjemploAreaTIC").client.FuncionEjemplo(new Tipo1());
    Console.Write(result);
    Console.ReadLine();
   }
 }
}
Si queremos conectar con otro servicio y disponemos de la interfaz simplemente tendríamos que sustituir la interfaz en la llamada de modo que la misma clase proxy nos serviría para conectar a todos los servicios que tengamos en nuestro servidor de negocio.

Ya de cara al siguiente artículo veríamos como implementar el tema de asíncronos. Recordaros que podéis seguir areaTIC en las redes sociales, esperamos vuestra participación!


martes, 22 de enero de 2013

WCF ChannelFactory<T>. Evitar el uso de svcUtil para generar clientes WCF.

A la hora de crear un cliente para un servicio WCF podemos hacer algo tan sencillo y rápido como botón derecho "Agregar referencia de Servicio" y aquí introducir la URL que contiene los meta-datos del servicio.

Visual Studio en este punto usa svcUtil para generar automáticamente las clases proxy y los tipos que se usarán en el servicio.

Hasta aquí todos estaremos de acuerdo que no hay modo más sencillo y rápido de generar un cliente que usando svcUtil. En caso que estemos trabajando en una arquitectura en la que disponemos de los tipos y la interfaz que se ha usado para crear el servicio, crear un "service reference" sería práctico pero poco óptimo dado que estaríamos generando clases y archivos que realmente no sería necesario crear en presentación (cliente).

Hoy veremos como usar la clase ChannelFactory<T> que nos permitirá generar un cliente para nuestro servicio. Para el ejemplo será necesario crear 3 proyectos; AreaTIC.Client (Consola), AreaTIC.Server (Consola) y AreaTIC.Shared (librería de clases).

Crearemos una solución que contenga los proyectos AreaTIC.Server y AreaTIC.Shared. En primer lugar crearemos los tipos que usaremos en las operaciones del servicio (DataContract). Para implementar el patrón estos tipos necesitaremos que sean accesibles tanto en servidor como cliente así que ubicaremos las clases en el proyecto AreaTIC.Shared en un namespace AreaTIC.Shared.DataContract.
namespace AreaTIC.Shared.DataContracts
{
    [DataContract]
    public class Tipo1
    {
        [DataMember]
        public string Atributo1 { get; set; }
        [DataMember]
        public string Atributo2 { get; set; }
    }
}
En segundo lugar, para crear un servicio WCF nos hará falta una interfaz con el atributo [ServiceContract] que contenga la firma de los métodos que se incluirán en el servicio. Dicha interfaz será necesaria tanto en servidor como cliente así que la ubicaremos dentro del proyecto AreaTIC.Shared en un namespace que llamaremos AreaTIC.Shared.ServiceContracts.
namespace AreaTIC.Shared.ServiceContracts
{
    [ServiceContract]
    public interface IServiceEjemplo
    {
        [OperationContract]
        string FuncionEjemplo(Tipo1 pValue);
    }
}
Crearemos la clase que implementará la interfaz anterior dentro de AreaTIC.Server.Services.
using AreaTIC.Shared.ServiceContracts;
using AreaTIC.Shared.DataContracts;

namespace AreaTIC.Server.Services
{
    public class ServiceEjemplo:IServiceEjemplo
    {
        string IServiceEjemplo.FuncionEjemplo(Tipo1 pValue)
        {
            Thread.Sleep(5000);

            //Implementación (...)

            return "bla bla";
        }
    }
}
Para acabar con la parte servidor faltaría definir un Binding y hospedar el servicio (esta parte podría hacerse vía web.config <system.servicemodel>).
using System.ServiceModel;
using AreaTIC.Server.Services;
using AreaTIC.Shared.ServiceContracts;

namespace AreaTIC.Server
{
 class Program
 {
 static void Main(string[] args)
 {
  try
  {
    Uri baseURI = new Uri("http://localhost:8080/Services/");
    string Address = "EjemploAreaTIC";
    ServiceHost hostEjemplo = new ServiceHost(typeof(ServiceEjemplo), baseURI);
    string ContractTypeName="AreaTIC.Shared.ServiceContracts.IServiceEjemplo";

    WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);
    hostEjemplo.AddServiceEndpoint(ContractTypeName, binding, Address);

    Console.WriteLine("Servicio Ejemplo a la escucha");
  }
  catch (Exception)
  {
    Console.WriteLine("Error al hospedar el servicio");
  }
  finally 
  {
    Console.ReadLine();
  }
 }
 }
}
Llegado este punto ya tenemos el servicio a la escucha ahora veamos como generar el cliente usando ChannelProxy. En primer lugar crearemos otra solución que contenga un nuevo proyecto AreaTIC.Client al que añadiremos como referencia la librería de clases AreaTIC.Shared. En la misma clase Program.cs definiremos el binding, endPointAddress y conectaremos al servicio que hemos creado en el paso anterior mostrando la información que devuelve la llamada por pantalla.
using System.ServiceModel;
using AreaTIC.Shared.DataContracts;
using AreaTIC.Shared.ServiceContracts;

namespace AreaTIC.Client
{
 class Program
 {
   static void Main(string[] args)
   {
     //definimos binding.
     WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);

     //definimos endpoint 
     EndpointAddress endPoint = new EndpointAddress("http://localhost:8080/Services/EjemploAreaTIC");

     //definimos factory y creamos canal.
     ChannelFactory<IServiceEjemplo> factory = new ChannelFactory<IServiceEjemplo>(binding,endPoint);
            
     //en este punto podríamos personalizar el comportamiento de nuestro cliente (serializador, inspectors, credenciales, certificados, ...);
     
     factory.Open();
     IServiceEjemplo wcfClient = factory.CreateChannel();
     Console.WriteLine(wcfClient.FuncionEjemplo(new Tipo1()));
     Console.ReadLine();
     factory.Close();
    }
 }
}
Si todo ha ido bien, mostraremos el texto 'bla bla' por pantalla. En arquitecturas donde se accede a datos vía WCF es muy interesante usar ChannelFactory<T> en cliente para evitar tener una lista interminable de "Service Reference" que serán engorrosos de configurar vía app.config.

Hasta aquí el artículo de hoy. Espero les sea de ayuda y como siempre os animo a participar con vuestros comentarios y/o aportaciones. Recuerda que puedes seguir areaTIC en las redes sociales!


viernes, 18 de enero de 2013

Blogger: página de error 404 y SEO eficiente

La página de error 404 se produce cuando hay conectividad con el servidor pero este no es capaz de encontrar la página que se solicita. Por ejemplo, si introducimos en nuestro navegador la URL www.areatic.net/inicio (página que no existe) nos llevará a la página de error personalizada de areaTIC.net.

Desde la perspectiva de SEO es importante que nuestro sitio , sea realizado en Blogger como comentaré en este artículo o en cualquier otra plataforma (Wordpress, Drupal, Joomla,...) tenga una página de error 404 personalizada ya que los robots de búsqueda valoran negativamente la no existencia. A tener en cuenta sobre la página de error 404:

  1. Un usuario llegará a una página de error 404 de nuestro sitio por diversos motivos, los más frecuentes, porque en el sitio hay un error (por ejemplo en un enlace) que le lleva a una página que no existe o porque introduce una URL que no existe para el sitio. El objetivo debe ser que esta visita que por uno de los dos motivos anteriores (u otros) llega a la página de error 404 no abandone nuestro sitio.

  2. Nuestra página de error 404 debería tener un texto personalizado, que no culpe al usuario del error y enlaces a páginas concretas como podría ser la home, el sitemap del sitio, un buscador... con el objetivo que el usuario los pulse y no abandone nuestro sitio.

  3. Podemos ser más imaginativos, si nuestro sitio tiene una tienda online, tras el mensaje pertinente y algún enlace (por ejemplo al sitemap) podemos presentarle una lista de productos en oferta, es decir, utilizarlo como una landing page a nuestra tienda.

  4. Otra opción imaginativa podría ser intentar utilizar la página de error 404 para conseguir seguidores en las redes sociales mediante los diferentes pluggins que existen de twitter, facebook,... eso sí, deberíamos minimizar en lo posible el uso de ficheros externos (javascript, CSS,...) para no ralentizar la carga de la página.

  5. ¿Más ideas? Seguro que sí, podéis agregarlas en los comentarios del artículo!!! Algunas páginas de error 404 personalizadas:


Llegados a este punto sólo me queda indicaros cómo hacer vuestra página de error 404 personalizada en Blogger, es muy sencillo, es realizar los pasos:
  1. Entramos en Blogger.

  2. Vamos al menú 'Configuración' y dentro a 'Preferencias de búsqueda'.

  3. En la sección 'Errores y redireccionamientos', junto a 'Mensaje de página no encontrada personalizado', pulsamos el enlace 'Editar'.

  4. En el cuadro que se nos abre podemos introducir el texto/HTML que creamos conveniente.


  5. Guardamos los cambios y ya tenemos página de error 404 personalizada en nuestro sitio de Blogger.

¿Tenéis más ideas para la página de error 404? ¿habéis personalizado ya la vuestra? Ya me diréis... espero que os haya sido interesante, dentro de areaTIC puedes encontrar otros artículos, no dudes en consultar nuestro archivo; también puedes seguirnos por RSS o las principales redes sociales (twitter, facebook, linkedin...)


LECTURAS RELACIONADAS RECOMENDADAS POR AREATIC.NET

martes, 15 de enero de 2013

Blogger: script para crear tabla de contenidos a partir de los posts

El otro día buscando una manera de crear una tabla de contenido a partir de los posts de areaTIC y agrupados según las etiquetas de cada uno llegué a este interesante artículo en Ciudadblogger. Se acercaba bastante a lo que quería hacer pero lo tuve que adaptar ligeramente (podéis ver el resultado en la pestaña ARCHIVO de este blog):

  • Muestro la fecha del post delante de su título, así dentro de cada grupo (que corresponde a una etiqueta) los tengo ordenados cronológicamente ya que en el caso de areaTIC tiene más sentido que alfabéticamente.

  • Corrijo un problema que había con la ordenación de los grupos (etiquetas) ya que si había etiquetas en mayúsculas y minúsculas siempre salían primero las mayúsculas y se perdía la ordenación alfabética.

  • Como nuevos marco los 3 últimos posts en lugar de los 10 que tenía el código original... 10 posts se puede ir 1/2/3/... meses atrás, eso según como ya no es nuevo... ¿no? En mi caso no lo era, podéis modificarlo según lo que necesitéis

  • Ajusto el estilo del "Nuevo" y lo coloco al inicio de línea del post, delante de la fecha, se localizan más fácilmente las novedades.

  • Aumento la separación entre los grupos (etiquetas) para que se visualicen mejor las entradas y el grupo al que pertenecen.

A continuación os pego el código resultante y que se está utilizando en areaTIC:
<script>
//<![CDATA[
var postTitle=new Array();
var postUrl=new Array();
var postMp3=new Array();
var postDate=new Array();
var postLabels=new Array();
var postBaru=new Array();
var sortBy="titleasc";
var tocLoaded=false;
var numChars=250;
var postFilter=""
var numberfeed=0;

function loadtoc(a){
  function b(){
    if("entry" in a.feed){
      var d=a.feed.entry.length;
      numberfeed=d;
      ii=0;
      for(var h=0;h<d;h++){
        var n=a.feed.entry[h];
        var e=n.title.$t;
        var m=n.published.$t.substring(0,10);
        var j;
        for(var g=0;g<n.link.length;g++){
          if(n.link[g].rel=="alternate"){
            j=n.link[g].href;
            break
          }
        }
        var o="";
        for(var g=0;g<n.link.length;g++){
          if(n.link[g].rel=="enclosure"){
            o=n.link[g].href;break
          }
        }
        var c="";
        if("category" in n){
          for(var g=0;g<n.category.length;g++){
            c=n.category[g].term;
            var f=c.lastIndexOf(";");
            if(f!=-1){
              c=c.substring(0,f)
            }
            postLabels[ii]=c;
            postTitle[ii]='['+m+'] ' + e;
            postDate[ii]=m;
            postUrl[ii]=j;
            postMp3[ii]=o;
            // Marcamos como nuevos los 3 últimos posts
            if(h<3){
              postBaru[ii]=true  
            }else{
              postBaru[ii]=false
            }
            ii=ii+1
          }
        }
      }
    }
  }
  b();
  sortBy="titleasc";
  sortPosts(sortBy);
  sortlabel();
  tocLoaded=true;
  displayToc2();
  document.write('')
}

function filterPosts(a){
  scroll(0,0);
  postFilter=a; 
  displayToc(postFilter)
}

function allPosts(){
  sortlabel();
  postFilter="";
  displayToc(postFilter)
}

function sortPosts(d){
  function c(e,g){ 
    var f=postTitle[e];
    postTitle[e]=postTitle[g];
    postTitle[g]=f;
    var f=postDate[e];
    postDate[e]=postDate[g];
    postDate[g]=f;
    var f=postUrl[e];
    postUrl[e]=postUrl[g];
    postUrl[g]=f;
    var f=postLabels[e];
    postLabels[e]=postLabels[g];
    postLabels[g]=f;
    var f=postMp3[e];
    postMp3[e]=postMp3[g];
    postMp3[g]=f;
    var f=postBaru[e];
    postBaru[e]=postBaru[g];
    postBaru[g]=f
  }
  for(var b=0;b<postTitle.length-1;b++){
    for(var a=b+1;a<postTitle.length;a++){
      if(d=="titleasc"){
        if(postTitle[b].toUpperCase()>postTitle[a].toUpperCase()){
          c(b,a)
        }
      }
      if(d=="titledesc"){
        if(postTitle[b].toUpperCase()<postTitle[a].toUpperCase()){
          c(b,a)
        }
      }
      if(d=="dateoldest"){
        if(postDate[b]>postDate[a]){
          c(b,a)
        }
      }
      if(d=="datenewest"){
        if(postDate[b]<postDate[a]){
          c(b,a)
        }
      }
      if(d=="orderlabel"){
        if(postLabels[b].toUpperCase()>postLabels[a].toUpperCase()){
          c(b,a)
        }
      }
    }
  }
}

function sortlabel(){
  sortBy="orderlabel";
  sortPosts(sortBy);
  var a=0;
  var b=0;
  while(b<postTitle.length){
    temp1=postLabels[b];
    firsti=a;
    do{
      a=a+1;
    }
    while(postLabels[a]==temp1);
    b=a;
    sortPosts2(firsti,a);
    if(b>postTitle.length){
      break
    }
  }
}

function sortPosts2(d,c){
  function e(f,h){
    var g=postTitle[f];
    postTitle[f]=postTitle[h];
    postTitle[h]=g;
    var g=postDate[f];
    postDate[f]=postDate[h];
    postDate[h]=g;
    var g=postUrl[f];
    postUrl[f]=postUrl[h];
    postUrl[h]=g;
    var g=postLabels[f];
    postLabels[f]=postLabels[h];
    postLabels[h]=g;
    var g=postMp3[f];
    postMp3[f]=postMp3[h];
    postMp3[h]=g;
    var g=postBaru[f];
    postBaru[f]=postBaru[h];
    postBaru[h]=g
  }
  for(var b=d;b<c-1;b++){
    for(var a=b+1;a<c;a++){
      if(postTitle[b].toUpperCase()>postTitle[a].toUpperCase()){
        e(b,a)
      }
    }
  }
}

function displayToc(a){
  var l=0;
  var h="";
  var e="Judul Artikel";
  var m="Klik untuk sortir berdasarkan judul";
  var d="Tanggal";
  var k="Klik untuk Sortir bedasarkan tanggal";
  var c="Kategori";
  var j="";
  if(sortBy=="titleasc"){
    m+=" (descending)";
    k+=" (newest first)"
  }
  if(sortBy=="titledesc"){
    m+=" (ascending)";
    k+=" (newest first)"
  }
  if(sortBy=="dateoldest"){
    m+=" (ascending)";
    k+=" (newest first)"
  }
  if(sortBy=="datenewest"){
    m+=" (ascending)";
    k+=" (oldest first)"
  }
  if(postFilter!=""){
    j="Klik untuk menampilkan semua"
  }
  h+="<table>";
  h+="<tr>";
  h+='<td class="toc-header-col1">';
  h+='<a href="javascript:toggleTitleSort();" title="'+m+'">'+e+"</a>";
  h+="</td>";
  h+='<td class="toc-header-col2">';
  h+='<a href="javascript:toggleDateSort();" title="'+k+'">'+d+"</a>";
  h+="</td>";
  h+='<td class="toc-header-col3">';
  h+='<a href="javascript:allPosts();" title="'+j+'">'+c+"</a>";
  h+="</td>";h+='<td class="toc-header-col4">';
  h+="Download MP3";
  h+="</td>";
  h+="</tr>";
  for(var g=0;g<postTitle.length;g++){
    if(a==""){
      h+='<tr><td class="toc-entry-col1"><a href="'+postUrl[g]+'">'+
         postTitle[g]+'</a></td><td class="toc-entry-col2">'+postDate[g]+
         '</td><td class="toc-entry-col3">'+postLabels[g]+
         '</td><td class="toc-entry-col4"><a href="'+postMp3[g]+
         '">Download</a></td></tr>';
      l++
    }else{
      z=postLabels[g].lastIndexOf(a);
      if(z!=-1){
        h+='<tr><td class="toc-entry-col1"><a href="'+postUrl[g]+'">'+postTitle[g]+
           '</a></td><td class="toc-entry-col2">'+postDate[g]+
           '</td><td class="toc-entry-col3">'+postLabels[g]+
           '</td><td class="toc-entry-col4"><a href="'+postMp3[g]+
           '">Download</a></td></tr>';
        l++
      }
    }
  }
  h+="</table>";
  if(l==postTitle.length){
    var f='<span class="toc-note">Menampilkan Semua '+
          postTitle.length+" Artikel<br/></span>"
  }else{
    var f='<span class="toc-note">Menampilkan '+l+
          " artikel dengan kategori '";
    f+=postFilter+"' dari "+postTitle.length+" Total Artikel<br/></span>"
  }
  var b=document.getElementById("toc");
  b.innerHTML=f+h
}

function displayToc2(){
  var a=0;
  var b=0;
  while(b<postTitle.length){
    temp1=postLabels[b];
    document.write("<p/>");
    document.write("<br/>");
    document.write('<p><a href="/search/label/'+temp1+'">'+temp1+"</a></p><ol>");
    firsti=a;
    do{
      document.write("<li>");
      if(postBaru[a]==true){
        document.write('<strong><span style="color:red">[Nuevo] </span></strong>')
      }
      document.write('<a href="'+postUrl[a]+'">'+postTitle[a]+"</a>");
      document.write("</li>");
      a=a+1
    }
    while(postLabels[a]==temp1);
    b=a;
    document.write("</ol>");
    sortPosts2(firsti,a);
    if(b>postTitle.length){
      break
    }
  }
}

function toggleTitleSort(){
  if(sortBy=="titleasc"){
    sortBy="titledesc"
  }else{
    sortBy="titleasc"
  }
  sortPosts(sortBy);
  displayToc(postFilter)
}

function toggleDateSort(){
  if(sortBy=="datenewest"){
    sortBy="dateoldest"
  }else{
    sortBy="datenewest"
  }
  sortPosts(sortBy);
  displayToc(postFilter)
}

function showToc(){
  if(tocLoaded){
    displayToc(postFilter);
    var a=document.getElementById("toclink")
  }else{
    alert("Just wait... TOC is loading")
  }
}

function hideToc(){
  var a=document.getElementById("toc");
  a.innerHTML="";
  var b=document.getElementById("toclink");
  b.innerHTML='<a href="#" onclick="scroll(0,0); showToc();Effect.toggle(\'toc-result\',\'blind\');">?? Menampilkan Daftar Isi</a><img src="http://radiorodja.googlepages.com/new_1.gif"/>'
}

function looptemp2(){
  for(var a=0;a<numberfeed;a++){
    document.write("<br>");
    document.write('Post Link : <a href="'+postUrl[a]+'">'+postTitle[a]+"</a><br>");document.write('Download mp3 : <a href="'+postMp3[a]+'">'+postTitle[a]+"</a><br>");
    document.write("<br>")
  }
};
//]]>
</script>
<script src="http://www.areatic.net/feeds/posts/default?max-results=9999&alt=json-in-script&callback=loadtoc"></script>

Entiendo que con la nueva sección ARCHIVO añadida a areaTIC se hace más fácil acceder a los contenidos que realmente interesan al lector y prescindir de los que no son de su interés. Bueno, hasta aquí el artículo de hoy, aprovecho una mejora en areaTIC para hacérosla llegar y que la podáis incorporar en vuestros blogs... espero que os haya sido interesante. Dentro de areaTIC puedes encontrar otros artículos, no dudes en consultar nuestro archivo; también puedes seguirnos por RSS o las principales redes sociales (twitter, facebook, linkedin...)


jueves, 10 de enero de 2013

WCF: Utilidad para convertir feeds en HTML estático

En el artículo de hoy veremos un ejemplo en el que usaremos WCF para leer el contenido de un RSS feed y generar código HTML estático que podremos incrustar posteriormente en nuestra web o blog.

Ya existen herramientas como feed burner que permiten generar código a partir de un feed para incrustar en una página HTML, el caso es que el código resultante es un script (javascript) que se incrusta en la página resultante y genera el contenido HTML del feed en runtime al cargar la página en el navegador.

Vamos con el ejemplo, usaremos Visual Studio 2010 Express en mi caso he usado un proyecto tipo web pero nos valdría también un proyecto Windows Forms o incluso una aplicación de consola.

El proyecto constará de una página con un TextBox que permita introducir la URL del feed, un botón "Generar" y un TextArea donde mostraremos el HTML resultante para poder "copiar y pegar" en nuestra web o blog.



En el botón on_click() del botón "Generar" picaremos el siguiente código que es el encargado de conectar al feed y completar el TextArea con el contenido del feed.
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Net;
  using System.IO;
  using System.ServiceModel.Syndication;
  using System.Xml;
  using System.Web;

  namespace FeedToHTML
  {
    public partial class FeedToHTML : System.Web.UI.Page
    {
      protected void Page_Load(object sender, EventArgs e)
      {

      }

      protected void btnGenerar_Click(object sender, EventArgs e)
      {
        txtResult.Text = new GetHTMLFeed(txtFeedURL.Text,200);
      }
   
    private string GetHTMLFeed(string pURLFeed, int pMaxContenidoResumen) 
    {
      try
      {
        string res = "";

        #region Obtener XMLFeed
         XmlReaderSettings settings = new XmlReaderSettings();
         settings.DtdProcessing = DtdProcessing.Parse;
         XmlReader xmlReader = XmlReader.Create(pURLFeed, settings);

         SyndicationFeed feed = SyndicationFeed.Load(xmlReader);
         foreach (SyndicationItem item in feed.Items)
         {
           //char[] caracteres = item.Summary.Text.Substring(0, pMaxContenidoResumen).ToCharArray();
           res += string.Format("\r
{2} ...

\r", item.Title.Text, item.Links.LastOrDefault().Uri.ToString(), item.Summary.Text.Substring(0, pMaxContenidoResumen).Replace("\n", "")); } #endregion return res; } catch (Exception ex) { throw ex; } } } }

Hasta aquí el artículo de esta semana, recordaros que podéis consulta el archivo de areaTIC en busca de artículos que podrían interesarte y no olvides seguirnos en las redes sociales!


martes, 8 de enero de 2013

SQL Server: Modos de evaluación de directivas

En el artículo "SQL Server: Uso de directivas y condiciones para saber si hay tablas sin índice cluster" del 25 de diciembre os indicaba que existen varios modos de evaluación de directivas, hoy toca explicar en qué consiste cada uno de los cuatro existentes:

  1. On demand, la directiva se evalúa cuando lo especifica el usuario (de forma manual).

  2. On schedule, mediante un trabajo de SQL Server se especifica la periodicidad con la que se evalúa la directiva (automático).

  3. On change: log only, utiliza la notificación de eventos para evaluar la directiva cuando algún cambio relevante sucede (automático).

  4. On change: prevent, utiliza DDL triggers para detectar y deshacer las operaciones que provocan la violación de directivas definidas (de forma automática). Para que este modo de evaluación funcione es necesario que estén habilitados en el servidor los triggers anidados (nested triggers), podéis encontrar información al respecto en el siguiente enlace del MSDN.

En la siguiente imagen podemos ver cómo especificamos el modo de evaluación de la directiva cuando creamos/modificamos una.


También, tal y como comentaba en el artículo citado anteriormente, los modos de evaluación de directivas dependen de la faceta sobre la que se aplica la condición. A destacar:

Para saber los modos de evaluación de directivas de cada faceta es importante tener en cuenta:
  1. La siguiente consulta nos permite saber qué modo de evaluación de directivas aplica a cada faceta.

    -- Modos de evalución por faceta
    SELECT name AS Faceta, 
     CASE execution_mode
     WHEN 7 THEN 'On change: prevent'
     WHEN 6 THEN 'On change: log only'
     WHEN 4 THEN 'On schedule'
     WHEN 0 THEN 'On demand'
     ELSE 'Undefined'
     END AS ModoEvaluacion
    FROM msdb.dbo.syspolicy_management_facets 
    order by execution_mode
    
    Nos devuelve el siguiente conjunto de resultados:


    De este modo, por ejemplo, podemos definir directivas para prevenir que se creen stored procedures cuyo nombre no comience por 'usp' pero no podemos definir directivas para prevenir que se creen tablas cuyo nombre no comience por 'tbl'.

  2. Unas pocas directivas soportan el modo de evaluación 'On change: prevent', algunas más el On change: log only, unas cuantas más el On schedule y todas el On demand. El siguiente gráfico muestra mejor el concepto. Sólo soportan los modos de evaluación 'On Change' aquellas directivas relacionadas con facetas que tienen cobertura de eventos DDL.


Y hasta aquí el artículo de hoy, espero que os haya sido interesante, dentro de areaTIC puedes encontrar otros artículos, no dudes en consultar nuestro archivo; también puedes seguirnos por RSS o las principales redes sociales (twitter, facebook, linkedin...)


LECTURAS RELACIONADAS RECOMENDADAS POR AREATIC.NET

viernes, 4 de enero de 2013

Importancia DateTime.SpecifyKind trabajando con nullable<DateTime> en escenarios de intercambio XML

Al enviar/recibir información entre varias plataformas en formato Xml ya sea vía soap o similares tenemos que considerar la importancia de especificar bien la zona horaria al trabajar con campos DateTime en el Xml para evitar situaciones comprometidas.

Por defecto al definir un campo de tipo xs:Datetime en un modelo xsd aceptará un valor de Fecha y Hora con o sin especificar la zona horaria. Para especificar la zona horaria tendríamos que concatenar una "Z" (defecto, UTC) o concatenar al final +/- HH:mm para indicarle que desvío queremos que se aplique en la fecha respecto a GMT.

Resumiendo no hay modo de obligar a través de una definición tipo xsd a especificar la zona horaria en los campos de tipo fecha. Tal vez usando el atributo pattern de la especificación para DateTime se podría llegar a hacer algo pero no lo he probado y seguramente herramientas de generación de código como svutil o axis no reconocerían esta restricción con lo tampoco ganaríamos mucho.

A continuación veremos un caso curioso relacionado con la zona horaria que podría darse si estamos trabajando en una plataforma .NET c#:
DateTime fechaActual = DateTime.Now;
DateTime? fechaDesde = null;

fechaDesde = fechaActual.AddDays(-1);
Si nos fijamos el campo fechaDesde está definido como nullable.
<s:Envelope>
<s:Body>
<request>
<a:fechaDesde>2012-12-28T09:20:12</a:fechaDesde>
<a:fechaHasta>2013-01-03T11:59:55.0938142+01:00</a:fechaHasta>
</request>
</s:Body>
</s:Envelope>
Al enviar esta información vía soap, en el campo fechaDesde no se ha concatenado el +01:00 de la zona horaria mientras en el campo hasta si lo está. Esto podría originar que el receptor del mensaje tome el formato UTC tomando como valor 1 hora menos en este caso. La clase DateTime de .NET tiene una propiedad Kind que permite consultar la zona horaria del tipo, y por otro lado tenemos el método DateTime.SpecifyKind que permite especificar el tipo de zona horaria que queremos por defecto. En los tipos DateTime por defecto toma el valor "Local" que se corresponde al sistema operativo que esté ejecutando la aplicación pero en los tipos nullable está propiedad no se comporta del mismo modo por defecto al definir la instancia y hemos de asignarle mediante SpecifyKind el tipo de zona horaria que necesitamos. Veamos como hacerlo:
DateTime fechaActual = DateTime.Now;
DateTime? fechaDesde = null;

fechaDesde = DateTime.SpecifyKind(fechaActual.AddDays(-1), DateTimeKind.Local);
Con esto ya estaríamos especificando a nivel soap la zona horaria en el campo fecha desde como vemos abajo.
<s:Envelope>
<s:Body>
<request>
<a:fechaDesde>2012-12-28T09:20:12+01:00</a:fechaDesde>
<a:fechaHasta>2013-01-03T11:59:55.0938142+01:00</a:fechaHasta>
</request>
</s:Body>
</s:Envelope>
Hasta aquí el artículo de hoy, os recuerdo que podéis seguir areaTIC en las redes sociales o vía RSS!


martes, 1 de enero de 2013

Alexa Traffic Rank: qué es, para qué sirve y cómo mejorarlo

Alexa es una compañía filial de Amazon (desde 1999) conocida principalmente por su web y su barra de herramientas. Precisamente esta barra de herramientas recoge los datos de navegación de los usuarios que la tienen instalada y los utiliza para calcular el Alexa Traffic Rank que sirve para medir la popularidad de un sitio web en función del número de visitantes y las páginas que han visto durante los últimos tres meses.


Vendría a ser un ranking en el que la primera posición la ocupa el sitio con mayor número de visitas y páginas visualizadas; dicho ranking está disponible a nivel mundial y para cada país. Es importante destacar que la precisión del Alexa Traffic Rank es relativa ya que, tal como comentaba anteriormente, sólo tiene en cuenta los datos de navegación de aquellos usuarios que tienen la barra de herramientas de Alexa instalada…

  • ¿Cuántos usuarios la tienen instalada? Alexa no ofrece información sobre este dato.

  • ¿Qué usuarios la tienen instalada? Normalmente este tipo de barras se la instalan webmasters, managers SEO, managers de marketing online… ¿representa esto a toda la comunidad de internautas? yo diría que no…

  • Sólo tiene en cuenta los últimos tres meses, ¿y todas las visitas y páginas vistas anteriormente?

Si queréis una opinión más sobre lo anterior podéis consultar la de Peter Norvig (Director de Investigación de Google) en su siguiente artículo Alexa Toolbar and the Problem of Experiment Design.

A pesar de todo lo anterior el Alexa Traffic Rank tiene su reputación en internet y como en todo ranking cuanto mejor esté posicionado nuestro sitio mejor, eso sí, debemos considerarlo un ranking más y no le demos más importancia de la que realmente tiene. Nos puede servir, aunque no sea exacto, como prueba imparcial de las visitas de nuestro sitio frente a otro de la competencia, de cara a posibles anunciantes,… así que para mejorar tu posición en el Alexa Traffic Rank es recomendable:

  1. Lo primero es reclamar tu sitio, puedes hacerlo a través del siguiente enlace siguiendo los pasos correspondientes.

  2. Instala la barra de herramientas de Alexa en tu navegador así cada vez que navegues por tu sitio contarán esas visitas. Puedes instalar la barra mediante este enlace.

  3. Añade alguno de los widgets que ofrece Alexa a tu sitio, puedes encontrarlos en el siguiente enlace.

  4. Y como siempre lo más importante, el contenido. Crea contenidos de calidad en tu sitio, con frecuencia, utiliza las redes sociales para que lleguen al máximo número de internautas… al final esto se traduce en visitas y seguro que un % de ellas tienen la barra de herramientas Alexa instalada y hacen mejorar tu posición en el ranking. Os adjunto un par de gráficos sobre la evolución en el Alexa Traffic Rank de areaTIC.net durante 3 meses, en el ranking mundial y en España.



Para finalizar el artículo tres temas más que os pueden resultar interesantes:

  • No confundir el Alexa Traffic Rank con el Pagerank de Google, no tiene nada que ver ni hay relación entre ellos. Si quieres saber más sobre el Pagerank consulta el artículo de areaTIC.net Manual SEO, 5 conceptos básicos.

  • Alexa no es la única web que ofrece este servicio, competencia suya son compete.com y quantcast.com.

  • El ranking en Alexa no influye en el posicionamiento de una web en los distintos buscadores.

Y hasta aquí el artículo de hoy, el primero del 2013!!! Espero os haya parecido interesante, recordad dentro de areaTIC puedes encontrar otros artículos, no dudes en consultar nuestro archivo; también puedes seguirnos por RSS o las principales redes sociales (twitter, facebook, linkedin...)