Spoiler: installa questo addon

Questo articolo comincia così, con io che mi ricordo di un articolo su Mozilla hacks e scopro un servizio incredibile per fare lo scraping delle pagine web senza impazzire troppo.

Ogni settimana una capatina al cinema si fa volentieri e se si può andarci per 3 europei tanto meglio, tanto e vero che ormai la pagina delle promozioni è diventata una delle più visitate sul mio telefono; purtroppo di un sito in versione mobile non se ne parla quindi non è proprio il top.

Quindi ecco finalmente che l’idea prende forma: creare un addon che, direttamente dalla home di Firefox, mi dica quali sono le offerte della settimana!

La cosa sembra semplice, c’è già del codice pronto che posso usare e grazie a kimonify posso creare una API che mi restituisca i dati al bisogno.

Siccome la cosa si è rivelata davvero così semplice mi limiterò a commentare il codice boilerplate dell’esempio ed a spiegare quali funzioni svolge.

Cominciamo!

Il file bootstrap.js è la base del nostro nuovo addon, quì specifichiamo cosa vogliamo che succeda dopo l’installazione e come deve comportarsi il nostro addon.

Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/Home.jsm");
Cu.import("resource://gre/modules/HomeProvider.jsm");
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

Queste sono delle librerie che abbiamo a disposizione quando non usiamo l’addon SDK, sono descritte quì, quelle che useremo sono Home, HomeProvider e ci permettono di modificare l’aspetto della pagina home di Firefox per Android.

function optionsCallback() {
    return {
        // titolo del pannello, obbligatiorio
        title: Strings.GetStringFromName("title"),
        // quali dati usare, obbligatorio
        views: [{
            // come visualizzare i dati
            type: Home.panels.View.GRID,
            // quali dati usiamo
            dataset: DATASET_ID,
            // come gestire gli URL degli elementi, ItemHandler.BROWSER o ItemHandler.INTENT
            itemHandler: Home.panels.ItemHandler.BROWSER,
            // New in Firefox 32 (no-op in Firefox 31+)
            onrefresh: refreshDataset
        }],
        // cosa fare quando si disinstalla l'addon
        onuninstall: function() {
            // If your add-on only adds a panel and does nothing else, it is nice to
            // uninstall the add-on for users if they remove the panel in settings.
            AddonManager.getAddonByID(ADDON_ID, function(addon) {
                addon.uninstall();
            });
        }
    };
}

La funzione optionsCallback ritorna un oggetto con 3 proprietà, 2 statiche e un metodo.

  • title: si spiega da se, il titolo dell’addon, in particolare non ho voluto mettere una stringa perché in futuro potrei voler aggiungere nuove lingue, quindi l’oggetto String, che abbiamo definito come un interfaccia con il file youraddon.properties, dove potremo includere le traduzioni, è la scelta giusta.
  • views: questo array contiene un oggetto che descrive come visualizzare i dati, quale DATASET stiamo usando e come gestire le interazioni con gli elementi visualizzati, se siete fortunati abbastanza da avere utenti che usano Firefox 31+ otterrete anche la proprietà onrefresh a cui potete attaccare una funzione che verrà chiamata quando l’utente farà un pull to refresh.
  • onuninstall: una funzione da chiamare quando l’addon viene disinstallato, è bene che rimuoviate voi le sezioni aggiunte visto che per l’utente è più complicato.

La cosa interessante è naturalmente l’attributo views, quì decidete come apparirà il vostro addon e quali dati deve visualizzare quindi concentriamoci su questo.

type

l’attributo type può essere di due tipi, Home.panels.View.GRID o Home.panels.View.LIST. Naturalmente il primo visualizza i dati in una griglia ed il secondo in una lista; la grande distinzione che si fa tra i due è lo spazio che danno alle immagini quando visualizzano i dati.

Ecco i due screenshots.

Il layout a griglia

Il layout a lista

Come potete vedere lo spazio dato alle immagini è ciò che cambia e per il nostro caso, ovvero mostrare la programmazione dei film, i poster hanno un peso notevole quindi ho usato il layout a griglia.

dataset

Questa è semplicemente una stringa del tipo “your.dataset@somedomain.org” e serve a dare un nome all’oggetto che tiene in memoria i dati che andremo a visualizzare.

itemHandler

Cosa fare quando viene cliccato un elemento della lista? Quì potete definire il comportamento del vostro addon; alcune idee:

  • mandare il proprio voto ad un sondaggio
  • visualizzare una pagina creata al momento con le informazioni aggiuntive
  • (il mio approccio) rimandare alla pagina del sito UCI (che inventiva)

Inoltre siccome lavoriamo con Android

onrefresh

cosa deve accadere quando l’utente fa un pull to refresh, nel mio caso ho una funzione che aggiorna i dati all’ultima versione disponibile quindi chiamo quella.

Passiamo oltre, questo oggetto che mi viene dato da optionsCallback a cosa mi serve? Devo passarlo a Home.panels.register per fare in modo che appaia nella home.

function startup(data, reason) {
  // Ecco che registriamo il pannello nella nostra home.
  Home.panels.register(PANEL_ID, optionsCallback);

  switch(reason) {
    case ADDON_INSTALL:
    case ADDON_ENABLE:
      Home.panels.install(PANEL_ID);
      HomeProvider.requestSync(DATASET_ID, refreshDataset);
      break;

    case ADDON_UPGRADE:
    case ADDON_DOWNGRADE:
      Home.panels.update(PANEL_ID);
      break;
  }

  // Open the panel when the add-on is first installed.
  if (reason == ADDON_INSTALL) {
    openPanel();
  }

  // Update data once every hour.
  // It is okay to call addPeriodicSync every time in order to update the callback.
  HomeProvider.addPeriodicSync(DATASET_ID, 24 * 3600, refreshDataset);
}

questa parte di codice aggiunge il mio pannello al HUB di Firefox e gestisce i vari casi, startup viene sempre chiamata, praticamente è il punto iniziale del nostro addon, tutto comincia da lì e quindi gestiamo i casi che possono verificarsi.

Altra parte interessante è HomeProvider, questo è un adattatore che permette di avere un oggetto dataset in memoria senza troppi sforzi, in questo modo non dovete preoccuparvi di gestire i dati che vi sono arrivati, buttateli semplicemente nel dataset che avete richiesto.

Per ottenere un dataset-storage vi basta usare HomeProvider.getStorage, passandogli l’ID del vostro dataset, per aggiungere dei dati al vostro dataset usate il metodo save dello storage associato, ecco un esempio.

var mioStorage = HomeProvider.getStorage('mioAddon.dataset@mioDominio.com');
mioStorage.save({
    'foo': 'foo',
    'bar': 'bar',
    'baz': 'baz'
});

rimpiazzare i dati

Per rimpiazzare i dati vecchi con quelli nuovi aggingete {replace: true} come opzione a mioStorage.save in questa maniera

mioStorage.save({
    'foo': 'nuovo-foo',
    'bar': 'nuovo-bar',
    'baz': 'nuovo-baz'
},{
    replace: true
});

Installare l’addon

Ok adesso abbiamo più o meno chiaro come fa a funzionare un addon per Firefox su Android ma l’interrogativo più grande è come diavolo lo installo?

Per fortuna c’è uno script build nella repository quindi diamogli un’occhiata

#!/bin/bash
# Replace this value with your add-on name
XPI=youraddon.xpi
# Replace this value to push to different release channels.
# Nightly = org.mozilla.fennec
# Aurora = org.mozilla.fennec_aurora
# Beta = org.mozilla.firefox_beta
# Release = org.mozilla.firefox
ANDROID_APP_ID=org.mozilla.firefox
# List add-on files here
zip -r $XPI bootstrap.js 
install.rdf 
chrome.manifest 
content 
locale 
README.md 
-x *.DS_Store*
# Push the add-on to your device to test
adb push "$XPI" /sdcard/"$XPI" && 
adb shell am start -a android.intent.action.VIEW 
-c android.intent.category.DEFAULT 
-d file:///mnt/sdcard/"$XPI" 
-n $ANDROID_APP_ID/.App && 
echo Pushed $XPI to $ANDROID_APP_ID

Cosa succede:

  • diamo un nome al file xpi che andremo a creare, lo assegni alla variabile XPI
  • decidiamo con quale applicazione vogliamo lavorare sul telefono, le varie opzioni sono i canali di aggiornamento di Firefox; per adesso lasciamo intatto ANDROID_APP_ID=org.mozilla.firefox
  • se abbiamo aggiunto dei file al progetto e questi ci servono aggiungili dopo il comando zip
  • adesso che abbiamo compresso tutto in un unico pacchetto è il momento di usare adb per spedire il nostro addon al telefono
  • una volta mandato il file apriamo Firefox e chiediamo di aprire il file appena mandato

Solo che mandarlo nella sdcard non mi gustava perché non tutti i telefoni hanno la SD e quindi molti gestiscono una finta SD che non è proprio immediato gestire da telefono.

Ecco quindi la mia versione, non cambia molto ma ci sono due particolari interessanti:

  • L’addon viene spedito nella cartella Download, che è raggiungibile dall’APP download presente in ogni telefono android
  • Viene aperto Firefox chiedendogli di aprire il file appena mandato, Firefox riconoscerà che è un addon e quindi ci chiederà se vogliamo installarlo
#!/bin/bash

# Replace this value with your add-on name
XPI=PromozioniUCI.xpi

# Replace this value to push to different release channels.
# Nightly = org.mozilla.fennec
# Aurora = org.mozilla.fennec_aurora
# Beta = org.mozilla.firefox_beta
# Release = org.mozilla.firefox
ANDROID_APP_ID=org.mozilla.firefox

# List add-on files here
zip -r $XPI bootstrap.js 
            install.rdf 
            chrome.manifest 
            content 
            locale 
            README.md 
    -x *.DS_Store*

# Push the add-on to your device to test
adb push "$XPI" /storage/sdcard0/Download/"$XPI" && 
adb shell am start -a android.intent.action.VIEW 
                   -c android.intent.category.DEFAULT 
                   -d file:/storage/emulated/0/Download/"$XPI" 
                   -n $ANDROID_APP_ID/.App && 
echo Pushed $XPI to $ANDROID_APP_ID

Con questo vi lascio, buon weekend!

Comincia la settimana

Solitamente la settimana non inizia mai bene, c’è sempre qualcosa a fare in modo che il bilancio non sia del tutto positivo ma questa è cominciata niente male.
Ho trovato due progetti molto interessanti che voglio condividere con voi; il primo è jpm il nuovo strumento di sviluppo di addon rilasciato da Mozilla, era ora che pubblicassero uno strumento compatibile con NodeJS e con gli strumenti odierni di sviluppo come Grunt o Gulp. Finora esisteva un plugin per Grunt che doveva usare lo strumento cfx spawnando un processo figlio, vedremo se qualcuno riuscirà a tirarne fuori un plugin degno di questo nome.

Il secondo progetto è una figata pazzesca, praticamente hanno impacchettato un servizio come quello offerto da Codepen o JSfiddle in un addon per Firefox; penso che ci passerò molto tempo a lavorare. Siccome è un bel progetto ed è anche open vi segnalo la repository su github.

Firefox OS e le richieste Cross-Domain

Se si vuole usare le richieste Cross-Domain in un’app per Firefox OS possiamo usare 2 diversi approcci:
– fare tutto a mano e usare l’oggetto XMLHTTPRequest ogni volta che dobbiamo richiedere qualche cosa
– configurare il tutto a modino e usare la solita sintassi

Ora io non so nulla di CROSS e configurazione di certificati lato server e bla bla bla ma da questa pagina si capisce subito che potremmo semplicemente prendere jQuery e nel file che includiamo nel nostro sito aggiungere al costruttore la proprietà mozSystem: true

jQuery.ajaxSettings.xhr = function() {
try {
return new XMLHttpRequest({mozSystem: true});
} catch( e ) {}
};

Ecco questo non va fatto assolutamente; primo perché avete modificato il file di jQuery e quindi questa modifica verrà rilevata dai revisori del marketplace, secondo perché cosa pensate che accadrà quando aggiornerete jQuery? La nuova istruzione non ci sarà, quindi la vostra webApp crasherà mentre voi vi grattate la testa.

Ciò che dovete veramente fare è includere questa modifica nella vostra app semplicemente aggiungendo un file cross.js con questo contenuto alla vostra pagina; ricordatevi che nelle app privileged, ovvero quelle che possono ottenere i permessi di fare richieste Cross-Domain, sono vietati gli script in-line ovvero ogni tag script della vostra pagina non deve avere contenuto e deve puntare ad un file locale, quindi niente CDN!

TL;DR

ecco il contenuto del file da includere, salvatelo in cross.js o quello che volete e aggiungetelo alla vostra pagina prima di fare qualsiasi richiesta HTTP.

$.ajaxSetup({
   xhr: function () {
          return new XMLHttpRequest({ mozSystem: true });
   }
});

questo è al momento l’unico metodo che funziona visto che $.ajaxSetup non riesce a settare la proprietà se la indicate dalla proprietà xhrFields benchè questa sia la proprietà desisgnata prorpio per risolvere questo problema dal team di jQuery

Durante l’ultimo mese mi sono ritrovato a giocare con NodeJS, una piattaforma per eseguire codice JavaScript direttamente sulla macchina (server-side). La cosa più semplice sarebbe stata di installarlo dalle repository di Ubuntu, visto che non era importante per me avere l’ultima versione disponibile al momento, ma la lettura di questo articolo mi ha fatto desistere.

Riassunto dell’articolo

Packages can run arbitrary scripts, which makes sudoing a package manager command as safe as a chainsaw haircut.
Isaac Z. Schlueter

Principalmente non c’è niente di sbagliato nell’installazione di NodeJS in una cartella del sistema con permessi di root se non fosse che NodeJS si porta dietro un secondo pezzo di software chiamato NPM, un gestore di pacchetti per NodeJS.

Installare NPM in una cartella con i permessi di root significa dare ai pacchetti che installerò i permessi di root quando li eseguirò. Quando ci si fida di chi ha creato il pacchetto (ad esempio voi vi fidate del vostro lavoro) questo è un problema che non si pone ma non sempre potete fidarvi .

Come ovviare alla situazione? Cambiare la cartella di installazione di NodeJS (o anche solo di NPM) può essere una buona idea. Io ho scelto la cartella ~/.local/node della mia macchina per esempio.

Ecco quindi le istruzioni da seguire per l’installazione locale di NodeJS e NPM partendo dai sorgenti che potete scaricare dal sito ufficiale..

View Fullscreen

Una cosa importante da ricordarsi è che in questo modo l’eseguibile di NodeJS non risulta in una cartella della PATH, quindi prima di usare uno qualsiasi degli strumenti che sono legati a NodeJS bisogna dare il comando export PATH=PATH:mia/cartella/installazione/locale.

Happy Coding!