Categorie : Logiciel

Google+ Hangouts une image plus grande et le partage d’écran

Google vient d’annoncer via Christopher Johnson quelques évolutions intéressantes pour Google+ Hangouts. La première est plus de l’ordre de la cosmétique puisque le design va s’appuyer sur la nouvelle charte de Google, ensuite la taille de l’image de la vidéo principale va considérablement augmenter, et enfin on va pouvoir faire du partage d’écran.

Avec le partage d’écran google+ hangouts devient une sacrée solution pour animer des conf call et devient de plus en plus un concurrent sérieux à skype et ichat, je ne parle même pas de Microsoft Office Live Meeting. Notez que cette fonctionnalité de partage d’écran était accessible avant mais via Hangouts Extra, Google vient de donc de déployer la fonctionnalité à tout le monde!


Boomerang for Gmail pour envoyer des emails plus tard

La gestion des emails est quelque chose de complexe, soit vous répondez tout de suite, soit vous le mettez dans votre todo, tout cela dépend de votre façon de travailler. Boomerang est une extension pour Chrome et Firefox à destination des utilisateurs de Gmail.

Cette extension est simple mais efficace car elle va vous permettre d’optimiser la gestion de votre boite email.

-Ainsi vous écrivez l’email et décidez ensuite de l’heure ou la date d’envoi de ce dernier. Ce type d’option peut être pratique pour envoyer un email en fonction d’un évènement particulier ou d’une date spécifique.
-Autre fonctionnalité intéressante, vous pouvez demander que s’il n’y a pas eu de nouvel évènement au bout de x jours l’email peut revenir dans votre inbox, malin pour relancer un client s’il n’a pas répondu à votre email
-Dernière fonctionnalité qui vaut le test est selon moi la possibilité de faire des emails récurrents envoyés à une date prédéfinie avec un niveau de répétition

Boomerang est gratuit pour 10 emails envoyés par mois et ensuite payant avec des offres commençant à 5$/mois jusqu’à 45$/mois. Pour tester la solutions c’est ici : http://www.boomeranggmail.com/


Share un nouvel outil pour partager des documents sans limites de taille ni de quantité

Décidément cela bouge dans le marché du partage de documents entre utilisateurs via le cloud. Après l’annonce d’OVH de venir concurrencer Dropbox avec Hubic (qui offre 25 Gigas gratuitement), c’est au tour de bittorrent de proposer un outil : Share.

Alors qu’est ce que Share, premièrement c’est un outil de partage de documents sans limite de taille de fichiers ni de quantité, on peut partager autant de fichiers que l’on souhaite. Techniquement, ce stockage est basé sur le P2P bien évidemment c’est le savoir faire de Bittorrent, mais pour que cela fonctionne il faut suffisamment de peer et on imagine que si l’on ne partage un document qu’avec une seule personne le P2P est plus que limité. Afin de réduire cette contrainte structurelle du P2P, Bittorrent va « cacher » le fichier sur amazon EC2 et S3 et une fois qu’il y aura suffisamment de peers, le « passera » dans un mode P2P. Le point faible de Share est qu’il faille installer une application contrairement à Dropbox qui se représente comme un dossier dans l’explorateur.

Share est disponible en version PC et mac mais pour ce dernier on est dans une version « alpha » qui est donc à utiliser en connaissance de cause. L’utilisateur va donc installer une version spécifique de Share sur PC et sur mac la version alpha de µtorrent. Ensuite il va utiliser le logiciel comme un outil de partage avec ses amis.

Share est donc une forme de P2P personnalisé. Un outil qui touchera certainement plus les initiés que le commun des mortels!


Enlever le mot de passe et les restrictions d’un pdf en 5 secondes

Quoi de plus énervant d’avoir un superbe pdf mais impossible à imprimer parce que l’auteur a ajouté des restrictions sur son fichier. Ce cas est très fréquent et finalement très énervant, un peu comme les DRM sur les ebooks.

Alors à chaque problème sa solution, avec pdfunlock vous allez faire sauter cette protection et pouvoir imprimer ou copier du texte comme vous le souhaitez! Un upload du fichier à déplomber, si le contenu est critique vous pouvez le faire avec une connexion sécurisée et hop vous téléchargez en retour un nouveau PDF sans aucune restrictions.

Notez que cette méthode ne fonctionne pas avec les PDF qui sont protégés à l’ouverture (demande d’un mot de passe pour ouvrir le document) mais dans 99% les auteurs ajoutent des restrictions sur l’impression et la sélection du texte alors pas d’inquiétude!


10 milliards d’applications android téléchargées, mais toujours pas de revenus corrects

Voici un chiffre qui pose. Le market d’android, l’équivalent de l’app store d’Apple vient de franchir un cap très intéressant puisqu’il y a eu plus de 10 milliards de téléchargement d’applications via cette plateforme.

Comme on peut le constater sur cette image, le rythme de téléchargement des applications augmente de mois en mois. Pour information en Octobre 2011, Apple a annoncé 18 milliards de téléchargements, Android va donc plus vite sur ce point.

En revanche il ne faut pas oublier qu’Android pour les développeurs n’est pas très intéressant versus Apple, certains estiment d’ailleurs que les revenus de l’android market représenteraient uniquement 7% de ceux de l’app store :


Activer simplement la nouvelle base de Google

Comme vous le savez déjà Google va déployer une nouvelle interface pour son moteur de recherche avec notamment un nouveau design de la barre supérieure (la fameuse barre noire). Pour l’instant cette nouvelle navigation n’est pas encore déployée mais comme pour le nouveau design de Youtube il est possible de l’activer très simplement grâce à l’ajout d’un cookie.

Mettez vous sur la page Google.fr puis :

Sous Google Chrome ouvrer : « Afficher/Options pour les développeurs/Console Javascript » et coller le code ci-dessous :

document.cookie="PREF=ID=03fd476a699d6487:U=88e8716486ff1e5d:FF=0:LD=en:CR=2:TM=1322688084:LM=1322688085:S=McEsyvcXKMiVfGds; path=/; domain=.google.fr";window.location.reload();

Pour Firefox c’est presque la même méthode mais tapez Ctrl-Shift-k pour ouvrir la console et coller le code :

document.cookie="PREF=ID=03fd476a699d6487:U=88e8716486ff1e5d:FF=0:LD=en:CR=2:TM=1322688084:LM=1322688085:S=McEsyvcXKMiVfGds; path=/; domain=.google.fr";window.location.reload();

Notez que si vous avez un compte google apps de connecté cela ne marchera pas, il faut soit se déconnecter soit utiliser un compte gmail classique. Et voilà vous avez la nouvelle barre!


Utiliser les données de Google Analytics avec Google Docs pour faire un tableau de bord

Je ne sais pas vous mais google analytics peut avoir des limites dans la génération de certains tableaux de bord. Prenons par exemple un site donc le CA n’est pas identifié dans Google Analytics, ce dernier ne pourra pas avoir une vue simple et unique de son trafic et de son chiffre d’affaires.

Si vous utilisez Google Docs voici une solution testée et approuvée pour récupérer les données de votre profil analytics pour les manipuler dans Google Docs.

Premièrement il faut utiliser le template Google Docs de SEOMOZ qui va vous permettre de vous connecter à votre compte GA (Google Analytics). Pour utiliser ce dernier il faut faire « make a copy ». Ensuite vous avez deux possibilités, soit vous utilisez le template et vous construisez votre tableau de bord dedans soit vous avez déjà un tableau de bord et dans ce cas il va falloir ajouter le script de connection (Google Apps Script) : Tools/Script editor/ puis copié/coller de qui suit :

function getGAauthenticationToken(email, password) {

  //Fetches GA authentication token, which can then be used to fetch data with the getGAdata function
//Created by Mikael Thuneberg
    try {
      
        if (typeof email == "undefined") {
            return "Email address missing";
        }
        if (typeof password == "undefined") {
            return "Password missing";
        }
        if (email.length == 0) {
            return "Email address missing";
        }
        if (password.length == 0) {
            return "Password missing";
        }
        password = encodeURIComponent(password);
        var responseStr
        var randnumber = Math.random()*5000;

      Utilities.sleep(randnumber);
      Utilities.sleep(randnumber);

        var response = UrlFetchApp.fetch("https://www.google.com/accounts/ClientLogin", {
            method: "post",
            payload: "accountType=GOOGLE&Email=" + email + "&Passwd=" + password + "&service=analytics&Source=Mikael Thuneberg-GA Google Docs functions-1.0"
        });
        responseStr = response.getContentText();
        responseStr = responseStr.slice(responseStr.search("Auth=") + 5, responseStr.length);
        return responseStr;
    } catch (e) {
        if (e.message.indexOf("CaptchaRequired") != -1) {
            return "Complete CAPTCHA at http://www.google.com/accounts/" + e.message.slice(e.message.indexOf("CaptchaUrl=") + 11, e.message.length);
        } else {
            return "Authentication failed (" + e.message + ")";
        }
    }
}

function getGAaccountData(authToken, dataType, includeHeaders, maxRows, startFromRow) {
//Fetches account data for the authenticated user
//Input authentication token produced by the getGAauthenticationToken function
//If dataType parameter is omitted, the functions fetches a list profiles to which the user has access
//By specifying the dataType parameters as "goals", the functions will fetch a list of goals by profile
//By specifying the dataType parameters as "segments", the functions will fetch a list of advanced segments
//Created by Mikael Thuneberg
  if (typeof authToken == "undefined") {
        return "Authentication token missing";
    }
    dataType = (typeof dataType == "undefined") ? "profiles" : dataType;
    maxRows = (typeof maxRows == "undefined") ? 200 : maxRows;
    maxRows = (typeof maxRows == "string") ? 200 : maxRows;
    startFromRow = (typeof startFromRow == "undefined") ? 1 : startFromRow;
    startFromRow = (typeof startFromRow == "string") ? 1 : startFromRow;
    if (authToken.length == 0) {
        return "Authentication token missing";
    }
    if (dataType.length == 0) {
        dataType = "profiles";
    }
    if (authToken.indexOf("Authentication failed") != -1) {
        return "Authentication failed";
    }
    try {
        authToken = authToken.replace(/\n/g, "");
        dataType = dataType.toLowerCase();
        var URL = "https://www.google.com/analytics/feeds/accounts/default?max-results=" + maxRows + "&start-index=" + startFromRow
        var responseStr;
      
        var response = UrlFetchApp.fetch(URL, {
            method: "get",
            headers: {
                "Authorization": "GoogleLogin auth=" + authToken,
                "GData-Version": "2"
            }
        });
        responseStr = response.getContentText();
        var XMLdoc = Xml.parse(responseStr);
        var lapset2;
        var TempArray = [];
        var RowArray = [];
        var HeaderArray = [];
        if (includeHeaders == true) {
            var rivi = 1;
            if (dataType == "segments") {
                HeaderArray[0] = "Segment ID";
                HeaderArray[1] = "Segment Name";
                HeaderArray[2] = "Segment Definition";
            } else {
                HeaderArray[0] = "Account Name";
                HeaderArray[1] = "Profile Title";
                HeaderArray[2] = "Profile Number";
            }
            TempArray[0] = HeaderArray;
        } else {
            var rivi = 0;
        }
        var sar = 0;
        var lapset;
        var dataFound = false;
        if (dataType == "segments") {
            lapset = XMLdoc.getElement().getElements();
            for (i = 0; i < lapset.length; i++) {
                if (lapset[i].getName().getLocalName() == "segment") {
                    sar = 0;
                    RowArray[0] = lapset[i].getAttribute("id").getValue();
                    RowArray[1] = lapset[i].getAttribute("name").getValue();
                    lapset2 = lapset[i].getElements();
                    for (j = 0; j < lapset2.length; j++) {
                        if (lapset2[j].getName().getLocalName() == "definition") {
                            RowArray[2] = lapset2[j].getText();
                        }
                    }
                    TempArray[rivi] = RowArray;
                    RowArray = [];
                    dataFound = true;
                    rivi++;
                    if (rivi == maxRows) {
                        return TempArray;
                    }
                } else {
                    if (lapset[i].getName().getLocalName() == "entry") {
                        break;
                    }
                }
            }
        } else { // datatype = profiles 
            lapset = XMLdoc.getElement().getElements("entry");
            for (i = 0; i < lapset.length; i++) {
                sar = 0;
                lapset2 = lapset[i].getElements();
                for (j = 0; j < lapset2.length; j++) {
                    if (lapset2[j].getName().getLocalName() == "title") {
                        RowArray[1] = " " + lapset2[j].getText();
                        dataFound = true;
                    } else {
                        if (lapset2[j].getName().getLocalName() == "property") {
                            if (lapset2[j].getAttribute("name").getValue() == "ga:accountName") {
                                RowArray[0] = lapset2[j].getAttribute("value").getValue();
                            }
                            if (lapset2[j].getAttribute("name").getValue() == "ga:profileId") {
                                RowArray[2] = lapset2[j].getAttribute("value").getValue();
                                break;
                            }
                        }
                    }
                }
                TempArray[rivi] = RowArray;
                RowArray = [];
                dataFound = true;
                rivi++;
                if (rivi == maxRows) {
                    return TempArray;
                }
            }
        }
        if (dataFound == false) {
            //return "No data found";
        }
        return TempArray;
    } catch (e) {
        return "0";
    }
}

function getGAdata(authToken, profileNumber, metrics, startDate, endDate, filters, dimensions, segment, sort, includeHeaders, maxRows, startFromRow) {
//Fetches data from the GA profile specified, using the authentication token generated by the getGAauthenticationToken function
//For instructions on the parameters, see http://bit.ly/bUYMDs
//Created by Mikael Thuneberg
  try {
        startDate.getYear();
    } catch (e) {
        return "Invalid start date";
    }
    try {
        endDate.getYear();
    } catch (e) {
        return "Invalid end date";
    }
    try {
        if (typeof authToken == "undefined") {
            return "Authentication token missing";
        }
        if (typeof profileNumber == "undefined") {
            return "Profile number missing";
        }
        if (typeof metrics == "undefined") {
            return "Specify at least one metric";
        }
        if (profileNumber != parseInt(profileNumber)) {
            return "Invalid profile number";
        }
        filters = (typeof filters == "undefined") ? "" : filters;
        dimensions = (typeof dimensions == "undefined") ? "" : dimensions;
        segment = (typeof segment == "undefined") ? "" : segment;
        maxRows = (typeof maxRows == "undefined") ? 100 : maxRows;
        maxRows = (typeof maxRows == "string") ? 100 : maxRows;
        startFromRow = (typeof startFromRow == "undefined") ? 1 : startFromRow;
        startFromRow = (typeof startFromRow == "string") ? 1 : startFromRow;
        if (authToken.length == 0) {
            return "Authentication token missing";
        }
        if (profileNumber.length == 0) {
            return "Profile number missing";
        }
        if (metrics.length == 0) {
            return "Specify at least one metric";
        }
        if (authToken.indexOf("Authentication failed") != -1) {
            return "Authentication failed";
        }
        authToken = authToken.replace(/\n/g, "");
        var startDateString
        var endDateString
        var dMonth
        var dDay
        if (startDate.getMonth() + 1 < 10) {
            dMonth = "0" + (startDate.getMonth() + 1);
        } else {
            dMonth = startDate.getMonth() + 1;
        }
        if (startDate.getDate() < 10) {
            dDay = "0" + startDate.getDate();
        } else {
            dDay = startDate.getDate();
        }
        startDateString = startDate.getYear() + "-" + dMonth + "-" + dDay
        if (endDate.getMonth() + 1 < 10) {
            dMonth = "0" + (endDate.getMonth() + 1);
        } else {
            dMonth = endDate.getMonth() + 1;
        }
        if (endDate.getDate() < 10) {
            dDay = "0" + endDate.getDate();
        } else {
            dDay = endDate.getDate();
        }
        endDateString = endDate.getYear() + "-" + dMonth + "-" + dDay
        if (startDateString > endDateString) {
            return "Start date should be before end date";
        }
        var URL = "https://www.google.com/analytics/feeds/data?ids=ga:" + profileNumber + "&start-date=" + startDateString + "&end-date=" + endDateString + "&max-results=" + maxRows + "&start-index=" + startFromRow;
        if (metrics.slice(0, 3) != "ga:") {
            metrics = "ga:" + metrics;
        }
        metrics = metrics.replace(/&/g, "&ga:");
        metrics = metrics.replace(/ga:ga:/g, "ga:");
        metrics = metrics.replace(/&/g, "%2C");
        URL = URL + "&metrics=" + metrics
        if (dimensions.length > 0) {
            if (dimensions.slice(0, 3) != "ga:") {
                dimensions = "ga:" + dimensions;
            }
            dimensions = dimensions.replace(/&/g, "&ga:");
            dimensions = dimensions.replace(/ga:ga:/g, "ga:");
            dimensions = dimensions.replace(/&/g, "%2C");
            URL = URL + "&dimensions=" + dimensions;
        }
        if (filters.length > 0) {
            if (filters.slice(0, 3) != "ga:") {
                filters = "ga:" + filters;
            }
            filters = filters.replace(/,/g, ",ga:");
            filters = filters.replace(/;/g, ";ga:");
            filters = filters.replace(/ga:ga:/g, "ga:");
            filters = encodeURIComponent(filters);
            URL = URL + "&filters=" + filters;
        }
        if (typeof(segment) == "number") {
            segment = "gaid::" + segment;
        }
        if (segment.length > 0) {
            if (segment.indexOf("gaid::") == -1 && segment.indexOf("dynamic::") == -1) {
                if (segment.slice(0, 3) != "ga:") {
                    segment = "ga:" + segment;
                }
                segment = "dynamic::" + segment;
            }
            segment = encodeURIComponent(segment);
            URL = URL + "&segment=" + segment;
        }
        if (sort == true) {
            URL = URL + "&sort=-" + metrics;
        }
    }
    catch (e) {
        return "0";
    }
    try {
        var response = UrlFetchApp.fetch(URL, {
            method: "get",
            headers: {
                "Authorization": "GoogleLogin auth=" + authToken,
                "GData-Version": "2"
            }
        });
    } catch (e) {
        if (e.message.indexOf("Timeout") != -1) {
            response = UrlFetchApp.fetch(URL, {
                method: "get",
                headers: {
                    "Authorization": "GoogleLogin auth=" + authToken,
                    "GData-Version": "2"
                }
            });
        } else {
            return "0";
        }
    }
    try {
        var responseStr = response.getContentText();
        var XMLdoc = Xml.parse(responseStr);
        var lapset = XMLdoc.getElement().getElements("entry");
        var lapset2;
        var TempArray = [];
        var RowArray = [];
        var HeaderArray = [];
        if (includeHeaders == true) {
            var rivi = 1;
        } else {
            var rivi = 0;
        }
        var sar = 0;
        var dataFound = false;
        for (i = 0; i < lapset.length; i++) {
            sar = 0;
            lapset2 = lapset[i].getElements();
            for (j = 0; j < lapset2.length; j++) {
                if (lapset2[j].getName().getLocalName() == "dimension") {
                    RowArray[sar] = lapset2[j].getAttribute("value").getValue();
                    if (rivi == 1) {
                        HeaderArray[sar] = lapset2[j].getAttribute("name").getValue();
                    }
                    sar++;
                }
                if (lapset2[j].getName().getLocalName() == "metric") {
                    RowArray[sar] = Number(lapset2[j].getAttribute("value").getValue());
                    if (rivi == 1) {
                        HeaderArray[sar] = lapset2[j].getAttribute("name").getValue();
                    }
                    sar++;
                }
            }
            TempArray[rivi] = RowArray;
            RowArray = [];
            dataFound = true;
            rivi++;
        }
        if (dataFound == false) {
            //return "No data found";
        }
        if (includeHeaders == true) {
            TempArray[0] = HeaderArray;
        }
        return TempArray;
    } catch (e) {
        return "0";
    }

}

J’ai volontairement modifié les messages d’erreurs pour afficher des « 0 » afin de pouvoir tout de même faire des graphiques (même avec des erreurs de connection).

Pour vous connecter vous avez besoin de votre email de compte GA, de votre mot de passe (vous pouvez en faire un spécifique pour cet outil c’est un peu plus secure il me semble) et du profil GA pour lequel vous souhaitez avoir les données. Cet id de profil se trouve dans l’url de votre compte GA soit après « id » pour l’ancienne version de GA ou après le « p » pour la nouvelle interface.

Voilà donc pour la partie connection à GA et script à intégrer. Ensuite il faut copier/coller la page du template de SEOMOZ ou alors se faire sa propre page de récupération de données ce qui est finalement assez simple en utilisant les formules du template. De mon côté ce qui m’intéressait c’était d’avoir des données quotidiennes et non mensuelles, j’ai donc modifié la formule en conséquence.

Attention lors de la récupération des données si vous demandez beaucoup d’information GA va vous retourner des erreurs de connection (des « 0 » avec mon script modifié), il faut donc y aller doucement, je dirai que 30 données à chaque fois c’est un max. Il suffit de faire copié/collé sur 30 cellules.

Et voilà le travail (données issues d’un autre site que 2803.fr), on retrouve des données en provenance de GA et d’autres données comme le total sur ce graphique :

Plus d’information ici et .


Avec iTunes Match, légalisez tous vos mp3 pour 25$ par an

Apple a annoncé hier la sortie d’iTunes Match, la solution d’Apple pour proposer de la musique dans le cloud. Cette nouvelle fonctionnalité vient donc en concurrence directe avec les Google Music, Amazon MP3, etc…

En effet, l’objectif avec iTunes Match est de proposer à l’utilisateur une solution pour qu’il puisse écouter ses mp3 sur n’importe lequel de ces appareils (principalement des devices apple dans ce cas). Avec iTunes Match Apple va scanner votre bibliothèque de mp3 (il l’a déjà fait je vous rassure cf la fonctionnalité Genius), et vous proposer pour 25$ par an d’avoir accès à ces titres dans le nuage. Notez que par rapport aux services de Google ou Amazon si iTunes reconnait le titre vous n’avez pas besoin d’uploader le mp3. Ainsi énorme avantage vous allez écouter un titre en 256kbps libre de DRM! Si jamais iTunes ne connait pas le mp3 ou n’arrive pas à le reconnaitre ce dernier sera téléchargé sur les serveurs d’Apple pour une écoute ultérieure en streaming.

Apple propose donc une solution pour légaliser tous les mp3 que vous avez téléchargés sur edonkey, emule, bittorrent et Kazaa! Pour 25$/an les 25,000 titres on peut dire que cela vaut le coup, Apple ayant à sa charge la rétribution des ayants droits. Apple transforme donc un pirate en bon consommateur, pourquoi pas!

Source Apple