﻿{"id":1379,"date":"2013-03-14T21:00:38","date_gmt":"2013-03-14T20:00:38","guid":{"rendered":"http:\/\/www.flapane.com\/blog\/?p=1379"},"modified":"2013-10-10T20:52:04","modified_gmt":"2013-10-10T19:52:04","slug":"creare-un-widget-per-goodreads-con-gli-ultimi-libri-letti","status":"publish","type":"post","link":"https:\/\/www.flapane.com\/blog\/2013\/03\/creare-un-widget-per-goodreads-con-gli-ultimi-libri-letti\/","title":{"rendered":"Creare un widget per Goodreads con gli ultimi libri letti"},"content":{"rendered":"<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1380 image\" alt=\"goodreads widget\" src=\"https:\/\/www.flapane.com\/blog\/wp-content\/uploads\/2013\/02\/goodreads-widget.png\" width=\"550\" height=\"208\" \/><\/p>\n<p><strong><a title=\"Goodreads books\" href=\"https:\/\/www.goodreads.com\/\" target=\"_blank\">Goodreads<\/a><\/strong> \u00e8 un&#8217;ottima (e pi\u00f9 flessibile) alternativa al famoso Anobii, che <strong>permette di catalogare i propri libri<\/strong>, recensirli, ricevere consigli su libri affini ai propri interessi.<\/p>\n<p>A differenza di Anobii, <strong>Goodreads offre delle API pi\u00f9 elaborate<\/strong>, e che ci possono permettere di creare <strong>un widget da mettere sul nostro sito, per mostrare gli ultimi libri letti, ed il libro che si sta leggendo adesso<\/strong> (qualora si stia leggendo un libro, e si trovi nello scaffale &#8220;currently-reading&#8221; di Goodreads).<\/p>\n<p>Come mostrato nell&#8217;immagine all&#8217;inizio del post, il widget mostra le copertine dei nostri libri e, passando con la freccina del mouse su uno di questi, apparir\u00e0 un pop-up che mostrer\u00e0 il titolo, l&#8217;autore, e la descrizione del libro.<!--more--><\/p>\n<p>Inoltre, ho aggiunto un po&#8217; di codice CSS (che NON funziona, purtroppo, con Internet Explorer 9) per far ruotare leggermente la copertina del libro, quando la freccina del mouse ci passa sopra.<\/p>\n<p>Cliccando su un libro, si aprir\u00e0 la corrispondente pagina su Goodreads.<\/p>\n<p>Dopo esserci registrati sul sito ed aver inserito i nostri libri, andiamo su <a title=\"Goodreads API key\" href=\"http:\/\/www.goodreads.com\/api\/keys\" target=\"_blank\">http:\/\/www.goodreads.com\/api\/keys<\/a> per recuperare la nostra API Key.<\/p>\n<p>Andiamo a vedere il codice del widget:<span class=\"no_translate\"><\/span><\/p>\n<pre class=\"brush:php\">&lt;?php\r\n\/* Goodreads.com PHP widget for websites. It shows the last $number_books read books or,if there's currently a book you're reading in \"currently-reading\" bookshelf, display it together with $number_books-1 read books.\r\nInsert your API key, your Goodreads userid, the number of books to show, the expiration of the cachefile and the cachefile name.\r\nBook covers images are shown. Title, author and description (120 characters) are shown in the tooltip that appears when you hover on one of the books. Also, the date you started reading the book in the \"currently-reading\" shelf is displayed.\r\nDescriptions are not shown in Internet Explorer because it's a crappy browser and doesn't support break lines in the tooltips. \r\nPlease respect Goodreads API terms!\r\n(c) Flapane.com - Oct-2013*\/\r\n\r\n$api_key = \"yourapikey\";\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/goodreads api key\r\n$number_books = \"5\";\u00a0\u00a0 \u00a0\/\/how many books from \"read\" shelf\r\n$user_id = \"youruserid\";\u00a0\u00a0 \u00a0\/\/goodreads user id\r\n$expiretime=24; \u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0 \/\/ cache expire time in hours. Goodreads does not allow more than 1 day\r\n$cachename=\"goodreads.cache\"; \/\/name of the cachefile\r\n\r\nfunction limitString($string, $limit = 120) {\u00a0\u00a0 \u00a0\/\/shorten book descriptions but do not cut words. From Stackoverflow\r\n\u00a0\u00a0\u00a0 \/\/ Return early if the string is already shorter than the limit\r\n\u00a0\u00a0\u00a0 if(strlen($string) &lt; $limit) {return $string;}\r\n\r\n\u00a0\u00a0\u00a0 $regex = \"\/(.{1,$limit})\\b\/\";\r\n\u00a0\u00a0\u00a0 preg_match($regex, $string, $matches);\r\n\u00a0\u00a0\u00a0 return $matches[1];\r\n}\r\n\r\n\/\/do not mess with the next lines!!!\r\nif (!file_exists($cachename)) {\u00a0\u00a0 \u00a0\/\/create the cachefile if it doesn't exist\r\n\u00a0\u00a0\u00a0\u00a0 $create = fopen($cachename, 'w');\r\n\u00a0\u00a0\u00a0 \u00a0 chmod (\"$cachename\", 0644); \/\/set chmod 644\r\n\u00a0\u00a0 \u00a0 fclose($create);\r\n}\r\n\r\n\/\/ Is the file older than $expiretime, or the file is new\/empty?\r\n$FileAge = time() - filectime($cachename);\u00a0\u00a0 \u00a0\/\/ Calculate file age in seconds\r\nif ($FileAge &gt; ($expiretime * 3600) || 0 == filesize($cachename))\r\n{\r\n$handle = fopen($cachename, 'wb');\r\n\r\n$read_books_path = \"http:\/\/www.goodreads.com\/review\/list\/\".$user_id.\".xml?key=\".$api_key.\"&amp;v=2&amp;shelf=read&amp;sort=date_read&amp;order=d&amp;page=1&amp;per_page=\".$number_books;\r\n$read_books_xml_query = file_get_contents($read_books_path); \r\n$read_books_json_array = json_decode(json_encode((array)simplexml_load_string($read_books_xml_query)),1);\u00a0\u00a0 \u00a0\/\/from http:\/\/gilbert.pellegrom.me\/php-quick-convert-xml-to-array\/\r\nsleep(1);\u00a0\u00a0 \u00a0\/\/Goodreads allows 1 API call per second\r\n$reading_books_path = \"http:\/\/www.goodreads.com\/review\/list\/\".$user_id.\".xml?key=\".$api_key.\"&amp;v=2&amp;shelf=currently-reading&amp;sort=date_read&amp;order=d&amp;page=1&amp;per_page=1\";\u00a0\u00a0 \u00a0\/\/get last currently reading book\r\n$reading_books_xml_query = file_get_contents($reading_books_path); \r\n$reading_books_json_array = json_decode(json_encode((array)simplexml_load_string($reading_books_xml_query)),1);\u00a0\u00a0 \u00a0\/\/from http:\/\/gilbert.pellegrom.me\/php-quick-convert-xml-to-array\/\r\n\r\nfor($i=0; $i&lt;$number_books; $i++){\r\n$read_titles[$i] = $read_books_json_array['reviews']['review'][$i]['book']['title'];\r\n$read_links[$i] = $read_books_json_array['reviews']['review'][$i]['book']['link'];\r\n$read_authors[$i] = $read_books_json_array['reviews']['review'][$i]['book']['authors']['author']['name'];\r\n$read_cover_images[$i] = $read_books_json_array['reviews']['review'][$i]['book']['image_url'];\r\n\r\n\u00a0\u00a0 \u00a0if (!empty($read_books_json_array['reviews']['review'][$i]['book']['description']))\u00a0\u00a0 \u00a0\/\/avoid empty descriptions\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0$read_descriptions[$i] = limitString(strip_tags($read_books_json_array['reviews']['review'][$i]['book']['description'])) . \"[...]\";\u00a0\u00a0 \u00a0\/\/strip html tags and cut description\r\n\u00a0\u00a0 \u00a0$read_descriptions[$i] = str_replace(\"\\\"\", \"'\", $read_descriptions[$i]);\u00a0\u00a0 \u00a0\/\/avoid double quotes\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0else $read_descriptions[$i] = \"No description\/Nessuna descrizione\";\r\n}\r\n\r\nif (!empty($reading_books_json_array['reviews']['review']['book']))\u00a0\u00a0 \u00a0\/\/are you currently reading any book and is it in \"currently-reading\" shelf?\r\n{\r\n$reading_titles = $reading_books_json_array['reviews']['review']['book']['title'];\r\n$reading_links = $reading_books_json_array['reviews']['review']['book']['link'];\r\n$reading_authors = $reading_books_json_array['reviews']['review']['book']['authors']['author']['name'];\r\n$reading_cover_images = $reading_books_json_array['reviews']['review']['book']['image_url'];\r\n$reading_start_date = $reading_books_json_array['reviews']['review']['started_at'];\r\n$reading_descriptions = $reading_books_json_array['reviews']['review']['book']['description'];\r\n\r\n\u00a0\u00a0 \u00a0if (!empty($reading_start_date))\u00a0\u00a0 \u00a0\/\/avoid empty\u00a0 \"date I started this book\" fields\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0$tz = new DateTimeZone('GMT');\r\n\u00a0\u00a0 \u00a0$date = new DateTime($reading_start_date);\r\n\u00a0\u00a0 \u00a0$date-&gt;setTimeZone($tz);\r\n\u00a0\u00a0 \u00a0$reading_start_date = '- Started on '.$date-&gt;format('l, j M Y g:i:s A T');\u00a0\u00a0 \u00a0\/\/date you started reading the current book in UTC time\r\n\u00a0\u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0if (!empty($reading_descriptions))\u00a0\u00a0 \u00a0\/\/avoid empty descriptions\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0$reading_descriptions = limitString(strip_tags($reading_books_json_array['reviews']['review']['book']['description'])) . \"[...]\";\u00a0\u00a0 \u00a0\/\/strip html tags and cut description\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0 if (isset($read_descriptions[$i])){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$read_descriptions[$i] = str_replace(\"\\\"\", \"'\", $read_descriptions[$i]);\u00a0\u00a0 \u00a0\/\/avoid double quotes\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0else $reading_descriptions = \"No description\/Nessuna descrizione\";\r\n\r\n\/\/show the \"currently-reading\" book and the $number_books-1 most recently read books\r\n$data[0] = \"&lt;div class='goodreads-widget'&gt;\";\r\n$data[1] = \"&lt;a href='$reading_links' target='_blank' title=\\\"$reading_authors - $reading_titles $reading_start_date&amp;#13;Desc: $reading_descriptions\\\"&gt;&lt;img src='$reading_cover_images' alt=\\\"$reading_authors - $reading_titles\\\" \/&gt;&lt;\/a&gt;\";\r\n\u00a0\u00a0 \u00a0for($i=0; $i&lt;$number_books-1; $i++){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$data[2+$i] = \"&lt;a href='$read_links[$i]' target='_blank' title=\\\"$read_authors[$i] - $read_titles[$i]&amp;#13;Desc: $read_descriptions[$i]\\\"&gt;&lt;img src='$read_cover_images[$i]' alt=\\\"$read_authors[$i] - $read_titles[$i]\\\" \/&gt;&lt;\/a&gt;\";\r\n\u00a0\u00a0 \u00a0}\r\n$data[$number_books+1] = \"&lt;\/div&gt;\";\u00a0\u00a0 \u00a0\r\n}\r\nelse\u00a0\u00a0 \u00a0{\u00a0\u00a0 \u00a0\/\/else show only $number_books \"read\" books\r\n\u00a0\u00a0 \u00a0$data[0] = \"&lt;div class='goodreads-widget'&gt;\";\r\n\u00a0\u00a0 \u00a0for($i=0; $i&lt;$number_books; $i++){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$data[1+$i] = \"&lt;a href='$read_links[$i]' target='_blank' title=\\\"$read_authors[$i] - $read_titles[$i]&amp;#13;Desc: $read_descriptions[$i]\\\"&gt;&lt;img src='$read_cover_images[$i]' alt=\\\"$read_authors[$i] - $read_titles[$i]\\\" \/&gt;&lt;\/a&gt;\";\r\n\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0$data[$number_books+1] = \"&lt;\/div&gt;\";\r\n}\r\n\r\nforeach($data as $value){\u00a0\u00a0 \u00a0\/\/write the output to the cachefile\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 fwrite($handle, $value);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\nfclose($handle);\r\n}\r\n\r\n\/\/cachefile is fresh enough... outputting data\r\n$data_cache=file_get_contents($cachename);\r\necho $data_cache;\r\n?&gt;<\/pre>\n<p><span class=\"no_translate\"><\/span>Ed il relativo codice CSS:<span class=\"no_translate\"><\/span><\/p>\n<pre class=\"brush:css\">&lt;style type=\"text\/css\"&gt;\r\n.goodreads-widget img {\r\n\twidth: 98px;\r\n\theight: 160px;\r\n\tborder:0px;\r\n\t-webkit-transition: all 0.5s ease;\r\n\t-moz-transition: all 0.5s ease;\r\n\t-o-transition: all 0.5s ease;\r\n\t-ms-transition: all 0.5s ease;\r\n\ttransition: all 0.5s ease;\r\n\t}\r\n.goodreads-widget img:hover {\r\n\t-webkit-transform: rotate(-10deg);\r\n\t-moz-transform: rotate(-10deg);\r\n\t-o-transform: rotate(-10deg);\r\n\t-ms-transform: rotate(-10deg);\r\n\ttransform: rotate(-10deg);\r\n\t-sand-transform: rotate(-10deg);\r\n\t}\r\n&lt;\/style&gt;<\/pre>\n<p><span class=\"no_translate\"><\/span>Come ho scritto nei commenti, \u00e8 sufficiente inserire la propria API Key (<em>$api_key<\/em>), il numero di libri da mostrare (<em>$number_books<\/em>), il proprio user id di Goodreads (<em>$user_id<\/em>), il tempo massimo di durata del file di cache (<em>$expiretime<\/em>)(Goodreads non permette di salvare i dati per pi\u00f9 di 24 ore), ed il nome del file di cache (<em>$cachename<\/em>).<\/p>\n<p>Consiglio di leggere con attenzione le condizioni d&#8217;uso di Goodreads (<a title=\"Goodreads API terms\" href=\"http:\/\/www.goodreads.com\/api\/terms\" target=\"_blank\">clicca qui<\/a>), dove c&#8217;\u00e8 scritto cosa fare per non violare i termini d&#8217;uso.<\/p>\n<p>Lo script recupera il numero di libri letti desiderato (<em>$number_books<\/em>), e va a controllare se, al momento, stiamo leggendo un libro (che, quindi, si trover\u00e0 nello scaffale currently-reading di Goodreads).<\/p>\n<p>Se stiamo leggendo un libro, lo script mostrer\u00e0 questo libro, e gli ultimi <em>$number_books-1<\/em> libri letti.<\/p>\n<p>Se in questo momento non stiamo leggendo alcun libro, lo script mostrer\u00e0 gli ultimi <em>$number_books<\/em> letti.<\/p>\n<p>Le API restituiscono risposte in XML, ma io, per praticit\u00e0 e mia preferenza, ho preferito convertirle in JSON.<\/p>\n<p>Nel file di cache verr\u00e0 salvato solo il codice HTML del widget, in modo da minimizzare le dimensioni del file.<\/p>\n<p>Consigli? Problemi? Avete inserito il widget sul vostro sito? Lasciate pure un commento! \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Goodreads \u00e8 un&#8217;ottima (e pi\u00f9 flessibile) alternativa al famoso Anobii, che permette di catalogare i propri libri, recensirli, ricevere consigli su libri affini ai propri interessi.<br \/>\nA differenza di Anobii, Goodreads offre delle API pi\u00f9 elaborate, e che ci possono permettere di creare un widget da mettere sul nostro sito, per mostrare gli ultimi libri letti, ed il libro che si sta leggendo adesso[&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[212,200,108,228,199,14,15,61,229],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/posts\/1379"}],"collection":[{"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/comments?post=1379"}],"version-history":[{"count":0,"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/posts\/1379\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/media?parent=1379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/categories?post=1379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.flapane.com\/blog\/wp-json\/wp\/v2\/tags?post=1379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}