WordPress: visualizzare il numero di iscritti di Feedburner

Piccola premessa: su google si trovano decine di articoli (soprattutto in inglese) che spiegano come ottenere quello che sto per scrivere, ma nessuno di essi funzionava sul mio blog. Dopo svariati tentativi sono riuscito a creare, fondendo le tante soluzioni proposte, una funzione funzionante.

Feedburner è un servizio Web 2.0 per la gestione e la pubblicizzazione dei propri feed RSS, podcast e tutto ciò che viene pubblicato sotto forma di aggiornamenti. Per chi utilizza WordPress, o qualunque altra piattaforma di blog, è buona norma appoggiarsi a Feedburner per divulgare i feed, dato che il sistema offre tutta una serie di strumenti per renderli più visibili e raggiungibili, oltre al fatto che integra un sistema di statistiche simile a Google Analytics (e questo è ancora più vero nella versione beta in linea da poco).

Da pochi giorni ho cambiato il tema al mio blog, e sto facendo in modo di integrarlo il più possibile con tutti i servizi a cui sono iscritto. L’ultimo che ho attivato è stato proprio Feedburner: prima di tutto ho installato il plugin FD Feedburner, che si occupa di redirigere automaticamente i visitatori dall’url default dei feed di WordPress all’url del feed di Feedburner.

Per visualizzare il numero di iscritti al mio feed, invece, ho creato una piccola funzione, basata sul codice di altri trovato in rete, ma migliorata sfruttando il meccanismo di cache di WordPress: le Transient API. La funzione fa uso delle api di Feedburner, con la funzione GetFeedData, per ottenere una stringa XML contenente qualche statistica sul proprio feed da cui estrapolare il dato che ci interessa:

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<rsp stat="ok">
  <feed id="qfjovtfi00bq8nig0nb82h5h1s" uri="l4mns">
    <entry date="2010-12-31" circulation="7" hits="56" reach="2" />
  </feed>
</rsp>

Per ricevere questi dati è sufficiente digitare nel proprio browser l’url https://feedburner.google.com/api/awareness/1.0/GetFeedData?uri=feed_uri, dove feed_uri è l’URI scelta quando si è creato l’account. Il valore da considerare è quello dell’attributo circulation.

La cache in WordPress: Transient API

L’articolo è dedicato a Feedburner, ma una breve parentesi sul sistema di cache di WordPress ci può stare, dato che ne ho fatto uso. WordPress dispone di una serie di funzioni per l’inserimento, la modifica, l’estrazione e l’eliminazione di dati di varia natura e di varia longevità in una memoria temporanea. Tale memoria è solitamente il database, ma la documentazione ufficiale suggerisce di non affidarsi a questa supposizione in quanto i transient (così sono chiamati i dati memorizzati), possono essere salvati in altre locazioni, soprattutto quando si installano plugin come WP Super Cache.

Ho utilizzato un transient per memorizzare il numero di iscritti con la funzione set_transient($transient, $value, $expiration), a cui ho dato come scadenza un giorno; per leggere il valore del transient ho applicato invece la funzione get_transient($transient). I dati XML di cui fare il parsing vengono scaricati con cURL.

Codice della funzione

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function get_feedburner_subscribers_count($feedname) {
  $key = 'feedburner_subscribers_count_'.$feedname;
 
  $count = get_transient($key);
  if($count !== false) {
    return $count;
  }
 
  $url = "https://feedburner.google.com/api/awareness/1.0/GetFeedData?uri=".$feedname;
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_URL, $url);
  $data = curl_exec($ch);
  curl_close($ch);
  $count = 0;
  if ($data) {
    preg_match('/circulation=\"([0-9]+)\"/', $data, $matches);
    if(count($matches) > 1) {
      $count = intval($matches[1]);
      set_transient($key, $count, 60*60*24);
    }
  }
  return $count;
}

Il codice controlla prima di tutto se è presente (e se lo è, se è ancora valido) il transient del numero di iscritti. La funzione get_transient() restituisce false se il transient è scaduto o non esiste. Vengono impostati 2 parametri per la chiamata con cURL: RETURNTRANSFER per fare in modo che il risultato della chiamata venga ritornato e non stampato, e l’URL da richiamare. Il resto del codice legge l’attributo circulation e lo memorizza all’interno del nostro transient, impostando una durata di validità di un giorno.

Questa funzione può essere posizionata dentro il file functions.php del tema applicato, e richiamata in qualunque posizione della pagina in cui si vuole visualizzare il numero di utenti iscritti al nostro feed.

jQuery UI, PHP e MySql: ordinamento di immagini con drag & drop

Con jQuery UI, il framework per interfacce utente di cui ho già parlato nell’articolo passato, si possono fare cose davvero ganze1. Basta dare un’occhiata alle demo per rendersi conto di quanto semplice sia inserire controlli usabili, accattivanti ed estremamente personalizzabili graficamente, nei propri siti web.

In particolare, oggi sfrutterò il plugin sortable della suite jQuery UI, per realizzare una galleria fotografica che consente di riordinare le foto trascinandole con il mouse, proprio come se fossero icone.

Con sortable, come suggerisce il nome, si possono riordinare elementi di una lista ordinata (ol) e non (ul), con un semplice drag and drop. Il markup per le gallerie di miniature che utilizzo sempre è, per l’appunto, una lista non ordinata:

1
2
3
4
5
6
7
<ul id="gallery">
  <li id="photo_1"><img src="img/1.jpg" alt="" /></li>
  <li id="photo_2"><img src="img/2.jpg" alt="" /></li>
  <li id="photo_3"><img src="img/3.jpg" alt="" /></li>
  <li id="photo_4"><img src="img/4.jpg" alt="" /></li>
  <li id="photo_5"><img src="img/5.jpg" alt="" /></li>
</ul>

con questo foglio di stile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#gallery {
  overflow: hidden;
  padding: 0 10px;
}
  #gallery li {
    border: 1px solid #c1c1c1;
    float: left;
    list-style-type: none;
    margin-right: 5px;
    padding: 2px;
  }
  #gallery img {
    vertical-align: middle;
  }

ho volutamente tralasciato i link alle immagini di dimensioni intere, visto che ai fini dell’articolo non ci interessa. Il codice di base per attivare sortable è il seguente:

1
2
3
4
5
$(function() {
  $("#gallery").sortable({
    placeholder: 'ui-state-highlight'
  });
});

Con questo codice si rendono trascinabili ed ordinabili le immagini della galleria. Con l’opzione placeholder: 'ui-state-highlight' indichiamo al plugin di occupare lo spazio della foto trascinata via dalla sua posizione originaria, con un elemento (li) la cui classe è ui-state-highlight. Avremmo potuto assegnare una qualunque classe al segnaposto, ma utilizzando quella di default abbiamo la certezza che esso sarà conforme al resto del tema UI scelto. L’unica regola CSS che aggiungo è la seguente:

1
2
3
4
#gallery .ui-state-highlight {
  height: 120px;
  width: 160px;
}

per fare in modo che il segnaposto abbia le dimensioni di tutte le altre miniature della galleria2. Per farvi un’idea di quanto fatto finora ecco due demo, una con il segnaposto ed una senza.

Tutto molto carino, ma l’utilità effettiva è pari allo zero: aggiornando la pagina infatti le immagini torneranno tutte al loro posto. Vediamo come persistere il nuovo ordinamento, presupponendo che le immagini siano a database. Ecco una tabella di immagini molto semplice:

1
CREATE TABLE photos (id INTEGER NOT NULL AUTO_INCREMENT, filename VARCHAR(255), ordering INTEGER, PRIMARY KEY(id));

Visualizzare le immagini è altrettanto facile:

1
2
3
4
5
6
7
8
9
10
11
// ... connessione al database ...

$result = mysql_query("SELECT id, filename FROM photos ORDER BY ordering");

echo '<ul id="gallery">';
while ($row = mysql_fetch_row($result)){
  printf('<li id="photo_%d"><img src="%s" alt="" /></li>', $row['id'], $row['filename']);
}
echo "</ul>";

// ... altro codice, chiusura connessione ...

E’ importante notare l’assegnamento di un id ad ogni elemento della lista, che è indispensabile per il salvataggio su database della nuova posizione di ogni foto. Ciascun id deve avere come prefisso una parola qualunque (nell’esempio, “photo”), seguita da un separatore, che può essere “-”, “=” o “_” (nell’esempio è “_”), e l’id (preso da database) dell’elemento da riordinare. Avendo impostato in questo modo la nostra galleria, possiamo aggiornare il codice javascript per inviare le informazioni al server:

1
2
3
4
5
6
7
8
9
$(function() {
  $("#gallery").sortable({
    placeholder: 'ui-state-highlight',
    update: function(e, ui) {
      var order = $('#gallery').sortable('serialize');
      $.post('reorder.php', order);
    }
  });
});

Tra le opzioni di sortable abbiamo specificato il gestore dell’evento update, che utilizza il metodo serialize() del plugin per avere il nuovo ordine serializzato – del tipo photo[]=1&photo[]=3&photo[]=4&photo[]=2&photo[]=5 – trasmissibile al server con una chiamata POST. L’elenco serializzato viene poi passato con una chiamata AJAX al file reorder.php, che si occupa di aggiornare il database con il nuovo ordinamento:

1
2
3
4
foreach ($_POST['photo'] as $order => $id) {
  $query = "UPDATE photos SET ordering = $order WHERE id = $id";
  mysql_query($query);
}

Potete vedere il codice in azione in questa pagina di esempio, che chiaramente non salverà niente a database, ma restituirà soltanto un dump delle query virtualmente eseguite.

  1. mi piace usare qualche toscanismo ogni tanto! []
  2. ho già parlato di miniature di dimensioni fisse []

Gestione di date con PHP, MySql, e jQuery UI Datepicker

Per questo mio articolo prendo spunto da un post di Richard Lord, datato 2006 ma ancora valido (basta vedere le date dei commenti). L’argomento affrontato è lo scambio di date tra PHP e MySql: le date vengono gestite dai due sistemi in modo completamente diverso, quindi si deve trovare un punto di incontro.

  • PHP utilizza il formato timestamp di Unix: un timestamp è un numero intero che rappresenta il numero di secondi trascorsi dal 1 gennaio 1970 (orario di Greenwich).
  • In MySql le date sono invece stringhe del formato YYYY-MM-DD HH:MM:SS (come vedremo in uno dei prossimi articoli, che dedicherò ai tipi di dati temporali in MySql).

Richard propone 3 diverse soluzioni, delle quali ritengo più opportuna la seconda: memorizzare le date in campi di tipo DATETIME nel database, convertendole da e verso timestamp PHP con le funzioni MySql FROM_UNIXTIME() (da timestamp a DATETIME) e UNIX_TIMESTAMP() (da DATETIME a timestamp). Esempio:

1
2
3
4
5
// query di aggiornamento
$query = "UPDATE table SET datetimefield = FROM_UNIXTIME($phpdate) WHERE...";

// query di lettura
$query = "SELECT UNIX_TIMESTAMP(datetimefield) FROM table WHERE...";

A volte è necessario ricevere date in input dagli utenti: a tal proposito si è visto il proliferare in questi anni di soluzioni javascript di datepicker, calendarietti in cui l’utente seleziona rapidamente la data, e viene quindi riempito il campo apposito con la data prescelta. Uno dei migliori, a livello di personalizzazione, potenza e affidabilità è il Datepicker del framework per interfacce utente jQuery UI. E’ facilissimo associare un calendario ad un campo di testo:

1
<input type="text" id="date" name="date" />
1
2
3
4
5
<script type="text/javascript">
  $(function() {
    $("#date").datepicker();
  });
</script>

Torniamo al problema originario, arricchendolo di questo metodo di input delle date. E’ possibile impostare il calendario in modo che la data memorizzata nel campo di testo #date, abbia il formato che si desidera, tra cui anche il timestamp: apparentemente il problema è risolto. Abbiamo il timestamp dall’utente, quindi leggiamo il contenuto del campo di testo con PHP, infine con FROM_UNIXTIME() salviamo la data sul db.

Non è tanto bello però far vedere, all’utente che sceglie una data nel suo bel calendarietto, un numero intero tipo 1276380000000 invece di 13/06/2010, per cui dovremmo fare in modo di visualizzare il secondo formato anziché il primo. Datepicker ci viene incontro con le opzioni altField e altFormat, tramite il quale si può indicare al calendario un secondo campo di input (altField) in cui memorizzare la data selezionata con un secondo formato (altFormat). E’ qui che scriviamo il timestamp. Il codice diventa:

1
2
<input type="text" id="date" name="date" />
<input type="hidden" id="real_date" name="real_date" />
1
2
3
4
5
6
7
8
<script type="text/javascript">
  $(function() {
    $("#date").datepicker({
      altField: '#real_date',
      altFormat: '@'   // formato timestamp
    });
  });
</script>

NOTA BENE: l’intero passato dal datepicker è in millisecondi anziché in secondi, per cui bisogna dividere questo numero per 1000. Inoltre mi sono accorto che in alcuni casi la data è di qualche secondo antecedente a quella scelta. Sul mio pc, selezionando 13 giugno, il datepicker mi dà in millisecondi il valore corrispondente al 12 giugno alle ore 23.36 (quindi 24 secondi più recente), se converto il timestamp in DATETIME con la funzione FROM_UNIXTIME(). Non so se sia un bug, un errore di arrotondamento o un calcolo che io non conosco; prometto di indagare, ma se avete suggerimenti in merito non esitate a comunicarmeli.

Nel frattempo, il codice PHP per ottenere la data selezionata è il seguente:

1
2
3
// addizioniamo un minuto per evitare il "bug" descritto sopra,
// tanto ci interessa soltanto la data, e non l'ora.
$timestamp = $_REQUEST['real_date'] / 1000 + 60;

Riproporre la data all’utente per un’eventuale modifica successiva richiede ancora qualche sforzo. Sfortunatamente non è sufficiente impostare il value del campo #real_date con il timestamp letto da database: il datepicker non è in grado – o semplicemente non è previsto dal suo funzionamento – di leggere in automatico il contenuto del campo ed adeguare anche quello visualizzato dall’utente (#date). Ecco dunque il codice:

1
2
<input type="text" id="date" name="date" value="<?php echo date('d/m/Y', $timestamp); ?>" />
<input type="hidden" id="real_date" name="real_date" value="<?php echo $timestamp * 1000; ?>" />

Nota: il formato d/m/Y è quello di default del datepicker, per la localizzazione in italiano. Per altri locale, adeguare la stringa di formato di conseguenza.

PHP: creare miniature di immagini… con una marcia in più

La creazione di miniature (o thumbnail) di immagini è una delle operazioni più utilizzate sul web. Caricare una foto su un servizio web solitamente comprende il suo ridimensionamento ad un formato ottimizzato per internet (es. 800×600), e la generazione automatica della miniatura, per la visualizzazione di gallerie fotografiche e così via.

Generalmente le foto non sono quadrate, ma rettangolari, e possono avere due orientamenti di visualizzazione: portrait e landscape. Le immagini portrait hanno l’altezza maggiore della base. Le immagini landscape, viceversa, hanno la base maggiore dell’altezza.

immagine landscape

Immagine in formato landscape

immagine portrait

Immagine in formato portrait

Di solito per presentare le miniature si utilizzano delle liste non ordinate – una galleria altro non è che una lista di foto – i cui elementi li hanno lo stile float: left.

Creare gallerie di miniature, avendo a che fare con immagini in questi due formati, può generare problemi nel layout. Vi rimando ad un articolo (in inglese) che, oltre ad illustrare i problemi di cui sto parlando, ne fornisce anche una soluzione cross-browser, utilizzando il valore inline-block della proprietà display. Facebook invece, per citare un sito noto praticamente a tutti, utilizza una tabella per gli album di foto. Sicuramente è il modo più rapido ma io preferisco non usare tabelle per dati non tabulari.

Io invece propongo un altro metodo: tutte le immagini della mia galleria di miniature sono delle stesse dimensioni. E questo non lo ottengo forzando i valori di larghezza ed altezza via CSS, ma nel momento in cui genero la miniatura, ridimensionando l’immagine in proporzione, all’interno di una cornice (canvas) di dimensioni prefissate (es 160×120). Per far capire meglio la cosa ecco un esempio:

miniatura di immagine formato landscape

miniatura di un'immagine in formato landscape

miniatura di immagine formato portrait

miniatura di un'immagine in formato portrait

L’immagine vera e propria viene ridimensionata in modo che possa rientrare, in larghezza o altezza, dentro le dimensioni che ho stabilito (160×120 in questo esempio). Le proporzioni originali vengono preservate, e se avanza dello spazio – nel primo caso in altezza, nel secondo in larghezza – esso viene riempito con un colore che sceglieremo (in questo caso il bianco).

Vediamo come ottenere questo risultato con una funzione php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function resizeImage($src, $imageWidth, $imageHeight, $desiredWidth, $desiredHeight) {
  $newWidth = $desiredWidth;
  $newHeight = $desiredHeight;
  $newX = 0;
  $newY = 0;

  $scaleFactor = min($desiredWidth / $imageWidth, $desiredHeight / $imageHeight);
  $newWidth = ceil($imageWidth * $scaleFactor);
  $newHeight = ceil($imageHeight * $scaleFactor);
  $newX = ($desiredWidth - $newWidth) / 2;
  $newY = ($desiredHeight - $newHeight) / 2;

  $imageResized = imagecreatetruecolor($desiredWidth, $desiredHeight);
  $background = imagecolorallocate($imageResized, 255, 255, 255);
  imagefilledrectangle($imageResized, 0, 0, $desiredWidth, $desiredHeight, $background);
  imagecopyresampled($imageResized, $src, $newX, $newY, 0, 0, $newWidth, $newHeight, $imageWidth, $imageHeight);

  return $imageResized;
}

La funzione riceve 5 parametri:

  • $src è l’immagine che si vuole caricare
  • $imageWidth e $imageHeight sono le dimensioni attuali dell’immagine
  • $desiredWidth e $desiredHeight sono invece le nuove dimensioni che vogliamo darle

Vengono prima calcolate le nuove dimensioni in proporzione ($scaleFactor) e la posizione della miniatura ($newX, $newY) all’interno del canvas.

Successivamente viene creata un’immagine con le dimensioni volute, viene scelto il colore di sfondo ($background, in questo caso bianco), e posizionata l’immagine ridimensionata dentro il canvas bianco.

Ecco un rapido esempio di come utilizzare questa funzione:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// .... codice per ottenere $uploadedFile da un form....

list($width, $height, $type) = getimagesize($uploadedFile);

switch ($type) {
  case 1:   //   gif -> jpg
    $src = imagecreatefromgif($uploadedFile);
    break;
  case 2:   //   jpeg -> jpg
    $src = imagecreatefromjpeg($uploadedFile);
    break;
  case 3:  //   png -> jpg
    $src = imagecreatefrompng($uploadedFile);
    break;
}

$thumbnail = resizeImage($src, $width, $height, 160, 120);
$thumbPath = /* path di salvataggio dell'immagine */;
imagejpeg($thumbnail, $thumbPath, 95);