Widget Tumblr LastFM et Ajax en Cross Domain

J'ai voulu ajouter à mon Tumblr un nouveau widget affichant mes coups de coeur musical sur LastFM. Pour cela j'ai :

  1. Besoin de récupérer le flux rss sur LastFM
  2. Le parser
  3. Afficher une liste des n derniers coup de coeurs
  4. Intégration dans votre template Tumblr

1 - Récupération du flux J'avais réalisé en local un code javascript qui marchait très bien, avec une XmlHttpRequest qui récupérait en GET le contenu du flux rss.

Mais une fois inclut dans mon Template ça ne fonctionnait plus, la faute au Cross Domain qui n'est pas autoriser dans la version 1 de XmlHttpRequest.

Après avoir chercher divers solutions, je suis tombé sur le site du zéro avec une explication simple, utiliser le level 2 de XMLHttpRequest ou la version Microsoft disponible dans IE8.

J'ai donc modifier mon code en utilisant l'exemple du site du zero pour arriver à ça :

function getXDomainRequest() {
        var xdr = null;
        
        if (window.XDomainRequest) {
                xdr = new XDomainRequest(); 
        } else if (window.XMLHttpRequest) {
                xdr = new XMLHttpRequest(); 
        } else {
                alert("Votre navigateur ne gère pas l'AJAX cross-domain !");
        }
        
        return xdr;        
}

        window.onload = function(){
        
            var xdr = getXDomainRequest();
            xdr.onload = function() {        
                ...
            }
       }
};

Vous remarquerez l'utilisation de l'évènement onLoad sur l'objet xdr et non plus onComplete ou onSuccess (cet évènement n'est pas disponible dans le level 1 de XMLHttpRequest)
Cependant cela ne suffit pas car LastFM n'as pas mis en place d'Access Control, je n'ai donc pas l'autorisation de récupérer les informations.

Je passe donc par un fichier php hébérgé sur un de mes serveurs qui fait le relais en utilisant CUrl, il reçoit l'url du flux rss en paramètre et le retourne.
La requête Ajax aura le droit d'interroger ce fichier car je lui ai paramètré un Access-Control pour tout domaine distant. Voici le code de ce fichier :

<?php
        header("Access-Control-Allow-Origin: *");
        header('Content-Type: text/html; charset=utf-8');
        header("content-type: application/xml");

        $curl_handle = curl_init();                
        curl_setopt($curl_handle,CURLOPT_URL, $_REQUEST['url']);
        curl_setopt($curl_handle,CURLOPT_CONNECTTIMEOUT,2);
        curl_setopt($curl_handle,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($curl_handle,CURLOPT_GET,1);
                                                        
        $res = curl_exec($curl_handle);                        
        curl_close($curl_handle);

        print $res;
?>

Une fois le fichier mis en place j'ajoute ces 2 lignes à mon code Javascript afin d'envoyer la requête asynchrone :

xdr.open("GET", "http://static.ptirouz.net/tumblr/ajax_rss_call.php?url=http://ws.audioscrobbler.com/2.0/user/" + lastfmUser + "/lovedtracks.rss");
        xdr.send();

2 - Parsage du flux RSS Maintenant que ma fonction javascript reçoit bien le contenu du flux rss, je dois le parser afin de garder uniquement les infos nécessaires.

On utilise bien entendu la réponse XMLHttpRequest au format XML à savoir this.responseXML, un bout de code vaut tous les mots :

var innerHTML = "";
var track, lien;
                
var bloc = this.responseXML.getElementsByTagName("item");
        if (nbtracks > bloc.length) {
                nbtracks = bloc.length;
        }

        for (i=0; i<nbtracks ; i++) {
                var subxml = bloc[i].childNodes;

                for (j=0; j<subxml.length; j++) {
                        switch(subxml[j].tagName) {
                                case "title" :
                                        track = subxml[j].firstChild.nodeValue;
                                        break;
                                case "link" :
                                        lien = subxml[j].firstChild.nodeValue;
                                        break;                
                        }
                }        
              innerHTML += "\t<li class=\"lovedtracks\"> a href="\""">" + track + "</a \r\n";

3 - Affichage de la liste Il y a plusieurs façon de faire, j'a choisi celle qui consiste à remplir un objet DOM déjà existant dans le template HTML de ma page, comme ça mon code javascript peut rester tranquillement dans la balise Header et être non obstructif :

Dans mon javascript je met la touche finale :

document.getElementById('lastfm').innerHTML = innerHTML;

4 - Intégration dans votre Template Tumblr Maintenant que toute les pièces sont réuni reste plus qu'à l'intégrer à votre thème.

a - Ajouter les champs de configuration du widget :

<meta name="text:LastFm User" content=""/>
     <meta name="text:Loved Tracks Number" content="5"/>

b - Initialiser les variable Javascript :

<script type="text/javascript"></script>

c - Ajouter la référence au script créer précédemment qui à été ajouté statiquement à Tumblr :

<script src="http://static.tumblr.com/9bfczhx/0iZkv3ppu/lastfmlovedtracks.js"></script>

d - Ajouter un peu de style :

ul#lastfm {
                list-style-type:none;
                margin:0px;
                padding:0px;
        }

        li.lovedtracks
        {
                display:block;
                margin-bottom:3px;
        }

        li.lovedtracks a {                
                color:#666;
        }
        li.lovedtracks a:hover {
                color: #0099cc;
        }

e - Afficher la liste :

{block:IfLastFMUser}
    <h3 class="title"> a href="http://www.lastfm.fr/user/{text:LastFm User}/library/loved">Mes coups de coeurs</a </h3>!
    {/block:IfLastFMUser}

f - Paramétrage :
Il ne reste plus qu'as paramétrer votre utilisateur LastFM et le nombre de coup de coeur à afficher dans l'onglet Appearance 

Parametragelastfmtumblr

 


Pour fini un petit aperçu sur mon site

Apercuwifgtlastfmtumblr

Le Widget "En ce moment je lis" pour Tumblr

Dans ce monde en temps réel on sait ce que chacun fait (statut facebook ou twitter), écoute (lastfm, msn, skype).
Pour compléter ça j'ai créer sur mon Tumblr le widget "En ce moment je lis", qui permet d'afficher une couverture, le titre et le nombre de pages lus de votre livre actuel.

Pour le mettre en place il faut modifier le thème html, si ce n'est pas déjà fait vous devez dans un premier temps activer la personnalisation du HTML comme cela :
dans la page http://montumblr/customize, cliquer l'onglet Theme puis Use custom HTML Dans les premières lignes ou il y à déjà des balises META, insérer ce code :

<meta name="image:Book Cover" content=""/>
<meta name="text:Book Title" content=""/>                
<meta name="text:Book SubTitle" content=""/>
<meta name="text:Book Volume" content=""/>
<meta name="text:Book Author" content=""/>
<meta name="text:Book PagesRead" content=""/>
<meta name="text:Book PagesNumber" content=""/>

Puis à l'endroit de l'affichage voulu ajouter le code ci dessous :

{block:IfBookTitle}
        En ce moment je lis
                        
                {block:IfBookCoverImage}
                        
                {/block:IfBookCoverImage}
                                
                {block:IfNotBookCoverImage}
                        {text:Book Title}{block:IfBookSubTitle}, {text:Book SubTitle}{/block:IfBookSubTitle}{block:IfBookVolume}
                        ({text:Book Volume}){/block:IfBookVolume}{block:IfBookAuthor} de {text:Book Author}{/block:IfBookAuthor}
                {/block:IfNotBookCoverImage}
                                
                {block:IfBookPagesRead}
                        pages {text:Book PagesRead}{block:IfBookPagesNumber}/{text:Book PagesNumber}{/block:IfBookPagesNumber}
                {/block:IfBookPagesRead}
        
{/block:IfBookTitle}

Une fois ce code mis en place et sauvegarder, cliquer sur Appearance, vous devriez avoir quelques infos supplémentaire à remplir :

(download)
 Si vous désirez modifier ou bien créer vous même un widget, aller sur la page d'aide qui est très bien faite et le code est simple à comprendre.

(Dé)Sérialiser une réponse JSON en objet .Net de façon Générique

Pour le premier billet de ce blog dédié au développement, je ressort une classe que j'ai faite il y a quelques temps mais qui m'est très utiles.

Elle permet via la classe DataContractJsonSerializer apparu avec WCF dans le Framework 3.5 et contenu dans l'assembly System.ServiceModel.Web de sérialiser une réponse JSON (string) en un objet .Net de façon générique.
Pour cela on utilise le concept de paramètres de type, qui sera situé entre les "" :

public static T Deserialize(string jsonString)

Le T (on peut mettre ce que l'on veut) précisera donc le type du paramètre et pourra être utilisé dans le corps de la fonction et même en tant que type de renvoi de la fonction. Voici le code de cette classe qui finalement est très simple :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Net;

namespace PTiRouZ.Net.Api
{
    /// 
    /// Class permettant de convertir le code Json en objet .Net
    /// 
    public static class serializeTools
    {
        /// <summary>Déserialise une chaine Json en Object .Net</summary>
        /// <typeparam name="T">Type de l'Objet .Net de sortie</typeparam>
        /// <param name="jsonString">Chaine Json</param>
        /// <returns>un objet .Net de type <typeparamref name="T"/></returns>       
        public static T Deserialize<t>(string jsonString)
        {
            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
                return (T)serializer.ReadObject(ms);
            }
        }

        /// <summary>Sérialise un Object .Net en une chaine Json</summary>
        /// <typeparam name="T">Type de l'Objet .Net en entrée</typeparam>
        /// <param name="ClassObject">Object .Net de Type <typeparamref name="T"/></param>
        /// <returns>Une chaine Json</returns>        
        public static string Serialize<t>(T ClassObject)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
                serializer.WriteObject(ms, ClassObject);
                return Encoding.Default.GetString(ms.ToArray());
            }
        }
    }
}

Les objets .Net cible doivent implémenter des DataContract et DataMember situé dans l'assembly System.Runtime.Serialization qui permettront la(dé)sérialisation.

[DataContract]
    public class site_albums
    {
        /// Identifiant de l'album
        [DataMember]
        public int id { get; set; }

        /// nom de l'album 
        [DataMember]
        public string name { get; set; }
    }

Hello World !

Salut, J'ai déjà essayé par le passé de tenir un blog au niveau personnelle mais comme je ne suis pas un grand écrivain je n'ai jamais réussi à en sortir des choses intéressantes, à part mes photos de vacances et quelques vidéos récupérer sur youtube ou dailymotion :-) J'ai donc décidé d'essayer de faire un blog sur les choses que je connais, et sur lesquels je travaille tout les jours, le développement, la programmation, la prise de tête quotidienne pour que ce p?§$£% de caractère UTF-8 s'affiche correctement ! On est tous confronté à ces petits problèmes qui nous pourrissent la journée alors que ça aurait du prendre 5 minutes ... Je vais donc mettre sur ce blog toutes les solutions, tips que j'ai trouvé afin peut être t'aider d'autres personnes dans le besoin... Les sujet seront nombreux car j'ai un champ d'action large dans mon boulot : Php, Xsl, Xml, WPF, WCF, Silverlight, C# ... et je pense également divers choses touchant au web. Sur ces quelques mots je vous laisse avec le premier billet qui lui même donne une solution à un problème que j'ai eu pour écrire le 3ème billet ... :-)