Amazon Plugin

amazon plugin by Andreas Gohr
Display book information fetched from Amazon

Last updated on 2005-11-29. Provides Syntax.
No compatibility info given!

Conflicts with amazon_heavy!
Similar to isbn.

Tagged with amazon, books, isbn.

This plugin allows you to include book information (and cover art) into a Wiki page. This is somewhat similar to the ISBN plugin but it uses Amazon's WebService API to fetch the data. You can adjust the plugin's output by changing the format function below.

Another approach is the solution of Thomas Baumann tom [at] tiri [dot] li at Another Amazon Plugin. You can see the plugin in action here. It displays the preview and other useful information. Also using Amazon WebServices.

Usage

Usage is simple - just use the amazon tag providing an ASIN as shown below:

{{amazon>0142000280}}

If you want to link to a national Amazon, use the following syntax to access the German, UK, Japanese, French or Canadian Amazon site.

{{amazon>de:0142000280}}
{{amazon>uk:0142000280}}
{{amazon>jp:0142000280}}
{{amazon>fr:0142000280}}
{{amazon>ca:0142000280}}

syntax.php

Place the following code into lib/plugins/amazon/syntax.php

<?php
/**
 * Amazon Plugin: pulls Bookinfo from amazon.com
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Andreas Gohr <andi@splitbrain.org>
 */
 
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
require_once(DOKU_INC.'inc/HTTPClient.php');
 
 
if(!defined('AMAZON_APIKEY')) define('AMAZON_APIKEY','0R9FK149P6SYHXZZDZ82');
if(!defined('AMAZON_PARTNERID')) define('AMAZON_PARTNERID','ballermannsyndic');
 
/**
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_amazon extends DokuWiki_Syntax_Plugin {
    //shorten string to this length on output
    var $MAXCHARS = 25; //set to 0 if not wanted
 
    /**
     * return some info
     */
    function getInfo(){
        return array(
            'author' => 'Andreas Gohr',
            'email'  => 'andi@splitbrain.org',
            'date'   => '2005-11-29',
            'name'   => 'Amazon Plugin',
            'desc'   => 'Pull bookinfo from Amazon',
            'url'    => 'http://wiki.splitbrain.org/plugin:amazon',
        );
    }
 
    /**
     * What kind of syntax are we?
     */
    function getType(){
        return 'substition';
    }
 
    function getPType(){
        return 'block';
    }
 
    /**
     * Where to sort in?
     */
    function getSort(){
        return 160;
    }
 
    /**
     * Connect pattern to lexer
     */
    function connectTo($mode) {
      $this->Lexer->addSpecialPattern('\{\{amazon>[^}]*\}\}',$mode,'plugin_amazon');
    }
 
    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
        $match = substr($match,9,-2); // Strip markup
        list($ctry,$asin) = explode(':',$match);
 
        if(empty($asin)){
            $asin = $ctry;
            $ctry = 'us';
        }
        if($ctry == 'us') $ctry = 'com';
        if($ctry == 'uk') $ctry = 'co.uk';
        if($ctry == 'jp') $ctry = 'co.jp';
 
        // build API Url
        $url = 'http://webservices.amazon.'.$ctry.'/onca/xml?Service=AWSECommerceService'.
               '&AWSAccessKeyId='.AMAZON_APIKEY.
               '&Operation=ItemLookup&IdType=ASIN&ItemId='.$asin.
               '&ResponseGroup=Medium,OfferFull'.
               '&AssociateTag='.AMAZON_PARTNERID;
 
        // fetch it
        $http = new DokuHTTPClient();
        $xml  = $http->get($url);
        if(empty($xml)){
            return array();
        }
 
        return $this->_xml2array('',$xml);
    }            
 
    /**
     * Create output
     */
    function render($mode, &$renderer, $data) {
        if($mode == 'xhtml'){
            if(!count($data)){
//dbg:jal:20080121 Little Bug in original code
                $renderer->doc .= 'failed to fetch data';
                return false;
            }
            $renderer->doc .= $this->_format($data);
            return true;
        }
        return false;
    }
 
    function _format($data){
        global $conf;
 
        if($data['OperationRequest.Errors.Error.Message']){
            return $data['OperationRequest.Errors.Error.Message'];
        }
        if($data['Items.Request.Errors.Error.Message']){
            return $data['Items.Request.Errors.Error.Message'];
        }
 
        ob_start();
        print '<div class="amazon">';
        print '<a href="'.$data['Items.Item.DetailPageURL'].'" target="'.$conf['target']['extern'].'">';
        print '<img height="60" src="'.DOKU_BASE.'lib/exe/fetch.php?media='.
                urlencode($data['Items.Item.ImageSets.ImageSet.SmallImage.URL']).'&amp;h=60" alt="">';
        print '</a>';
 
        print '<div class="amazon_author">';
        if($data['Items.Item.ItemAttributes.Author']){
            $this->display($data['Items.Item.ItemAttributes.Author']);
        }elseif($data['Items.Item.ItemAttributes.Director']){
            $this->display($data['Items.Item.ItemAttributes.Director']);
        }elseif($data['Items.Item.ItemAttributes.Artist']){
            $this->display($data['Items.Item.ItemAttributes.Artist']);
        }
        print '</div>';
 
        print '<div class="amazon_title">';
        $this->display($data['Items.Item.ItemAttributes.Title']);
        print '</div>';
 
 
 
        print '<div class="amazon_isbn">';
        if($data['Items.Item.ItemAttributes.ISBN']){
            print 'ISBN ';
            $this->display($data['Items.Item.ItemAttributes.ISBN']);
        }elseif($data['Items.Item.ItemAttributes.RunningTime']){
            $this->display($data['Items.Item.ItemAttributes.RunningTime'].' ');
            $this->display($data['Items.Item.ItemAttributes.RunningTime/Units']);
        }
        print '</div>';
 
        print '</div>';
        $out = ob_get_contents();
        ob_end_clean();
 
        return $out;
    }
 
    function display($string){
        if($this->MAXCHARS && utf8_strlen($string) > $this->MAXCHARS){
            print '<span title="'.htmlspecialchars($string).'">';
            $string = utf8_substr($string,0,$this->MAXCHARS - 3);
            print htmlspecialchars($string);
            print '&hellip;</span>';
        }else{
            print htmlspecialchars($string);
        }
    }
 
    /**
     * Turn a XML strucutre into a simple array
     *    
     * @author HansP
     * @link   http://www.php.net/manual/en/ref.xml.php#55943
     */
    function _xml2array ($name, $xml, $Echar='.', $Achar='/', $discardempty=true){
        static $Result, $A, $E, $Discard;
 
        if ($name == '') {
            $Result = array ();
            $A = $Achar;
            $E = $Echar;
            $Discard = $discardempty;
        }
 
        $ReElements = '/<(\w+)\s*([^\/>]*)\s*(?:\/>|>(.*?)<\/\s*\\1\s*>)/s';
        $ReAttributes = '/(\w+)=(?:"|\')([^"\']*)(:?"|\')/';
 
        preg_match_all ($ReElements, $xml, $elements);
        foreach ($elements[1] as $ie => $xx) {
            if ( $attributes = trim($elements[2][$ie])) {
                preg_match_all ($ReAttributes, $attributes, $att);
                foreach ($att[1] as $ia => $xx){
                    $Result[$name.$E.$elements[1][$ie].$A.$att[1][$ia]] = $att[2][$ia];
                }
            }
 
            if (preg_match ($ReElements, $elements[3][$ie])) {
                $this->_xml2array ($name ? $name.$E.$elements[1][$ie] : $elements[1][$ie], $elements[3][$ie]);
// UKo 2008-03-26: Quick and dirty fix to get the first element instead of the last one
            } else if (!$Result[$name.$E.$elements[1][$ie]] && (!$Discard || $elements[3][$ie])) {
                $Result[$name.$E.$elements[1][$ie]] = $elements[3][$ie];
            }
        }
        return $Result;
    }
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :
?>

Correct a little bug in method render() – Jürgen A.Lamers/2008-01-21

Quick and dirty fix of _xml2array() that doesn't consider the attributes to distinguish element paths – Uwe Koloska 2008-03-26

style.css

Just a suggestion to place into lib/plugins/amazon/style.css

div.amazon {
  height: 70px;
}
 
div.amazon img {
  float: left;
  margin-right: 4px;
}
 
div.amazon_isbn{
  font-size: 90%;
}

Some more info

The plugin uses the Amazon Webservice API. To access this API one needs to have a valid developer key issued by Amazon.com. This key is set as the AMAZON_APIKEY define at the very top. You can use the one from the code above which is mine as long as you don't change the way the API is queried. If you want to change the Amazon access methods I ask you to register your own ID.

The second define (AMAZON_PARTNERID) is optional. This specifies an Amazon Affiliates PartnerID. The one you see in the code above is mine. This means if someone buys a book through the link displayed by the plugin I get a few percents from Amazon. Of course you can change this to your own ID if you have one or set it to an empty string if you don't want to give me that benefit.

Please note that the book cover image above is pulled through the fetch.php, this makes sure the image get cached and you're not hammering Amazon's Servers. The raw XML data of the requests is not cached - this is not necessary because of the caching of the final output (refer to Caching for more info).

Ben Pollinger created a button that could be used with this plugin in the quickbuttons toolbar

Looking up and displaying CDs

This plugin retrieves the title and author of books, but if you use the ASIN of a CD, it will only display the author. Of course, you need the artist for the CD, so the script needs to query a different field from the Amazon system.

A quick and dirty hack lets you do this by querying both Author and Artist fields:

after the line

print htmlspecialchars($data['Items.Item.ItemAttributes.Author']);

add

print htmlspecialchars($data['Items.Item.ItemAttributes.Artist']);

If the ASIN refers to a book, only Author will return anything. If it's a CD, only Artist will. You'll get an empty answer in each case, but the script doesn't return any error messages so the result looks fine in your wiki.

You can work out fields for different types of items from the Amazon web services 'manual'. — Ben Pollinger 2005-12-15

How to use one Amazon (e.g. UK) only

I wanted to simplify this slightly to use Amazon UK only, and cut the syntax a bit shorter to

{{amazon:0142000280}}

It just needs 2 changes to the syntax above. Change:

$this->Lexer->addSpecialPattern('\{\{amazon>[^}]*\}\}',$mode,'plugin_amazon');

to

$this->Lexer->addSpecialPattern('\{\{amazon[^}]*\}\}',$mode,'plugin_amazon');

and change

$url = 'http://webservices.amazon.'.$ctry.'/onca/xml?Service=AWSECommerceService'.

to

$url = 'http://webservices.amazon.co.uk/onca/xml?Service=AWSECommerceService'.

Ben Pollinger 2005-12-15

How to show prices

You can display the list price (i.e. the recommended retail price) using this code:

print htmlspecialchars($data['Items.Item.ItemAttributes.ListPrice.FormattedPrice']);

This is often different to the Amazon price, which you can show using:

print htmlspecialchars($data['Items.Item.OfferSummary.LowestNewPrice.FormattedPrice']);

Ben Pollinger 2005-12-16

Discussion

It doesn't work for me. Produces no output at all when i do this:

{{amazon>de:3409125760}}

Am i missing something? –LM, 4.11.2006

ISBN13

It's a small hack for retrieve data by using ISBN-13 keys. If the length of amazon code is smaller than 13, data will be retrieved by using ASIN. On the other hand, the length of amazon code is 13, data will be retrieved by using ISBN-13.

  // Replace the lines from $url = 'http://webservices.amazon... ..._PARTNERID;' to the following.
  if (strlen($asin)<13){
    // ASIN
    $url = 'http://webservices.amazon.'.$ctry.'/onca/xml?Service=AWSECommerceService'.
           '&AWSAccessKeyId='.AMAZON_APIKEY.
           '&Operation=ItemLookup&IdType=ASIN&ItemId='.$asin.
           '&ResponseGroup=Medium,OfferFull'.
           '&AssociateTag='.AMAZON_PARTNERID;
  }else{
    // ISBN13
    $url = 'http://webservices.amazon.'.$ctry.'/onca/xml?Service=AWSECommerceService'.
           '&AWSAccessKeyId='.AMAZON_APIKEY.
           '&Operation=ItemLookup&SearchIndex=Books&IdType=ISBN&ItemId='.$asin.
           '&ResponseGroup=Medium,OfferFull'.
           '&AssociateTag='.AMAZON_PARTNERID;
  }

Ikuo Obataya 2007-03-25

Imagelinks

I'd tried to install the plugin on my website, but the image of a cd is always the backside not the front. I can't find the right tag in the amazon-api-description, and would like to know if this is an amazon error or my fault. And, of course, I'm not very handy with php and the amazon-stuff - what do i have to edit to get more fields like price or format? I think I've got the right “tags” but the plugin displays nothing…

Manuel 2007-01-18

Multiple editors of a book - anyone help?

Editors are in a different response field to Author. I can display the editors of a book using:

print htmlspecialchars($data['Items.Item.ItemAttributes.Creator']);

But if there is more than one editor, this displays them altogether, like this:

Alice KnightG. Turpin

Can anyone hack the syntax to show these editors separated by commas, like this:

Alice Knight, G. Turpin

The relevant XML response snippet is at Items>Item>ItemAttributes>Creator Role=“Editor”, but I'm such a noob I can't work out how to process this — Ben Pollinger 2005-12-16

Multiple authors?

It seems that if there are more than one authors, only the last one shows up. Is that a bug?

Quick fix for first image and first author

The simple function _xml2array() is not able to get more than one element path that is only different by attribute 1). An item with more than one image looks like this

<ImageSets>
  <ImageSet Category="primary">
    <SwatchImage>
      <URL>...</URL>
      ...
    </SwatchImage>
    <SmallImage>
      <URL>...</URL>
      ...
    </SmallImage>
  </ImageSet>
  <ImageSet Category="variant">
    <SwatchImage>
      <URL>...</URL>
      ...
    </SwatchImage>
    <SmallImage>
      <URL>...</URL>
      ...
    </SmallImage>
  </ImageSet>

The old version of the code has overwritten all preceding paths and only retained the last one. My quick and dirty fix keeps the first occurence. This is as bad as the former code for multiple authors, but solves the problem of the missing title page. – Uwe Koloska 2008-03-26

1) or not different at all for some authors, like Joseph Ratzinger from ASIN 3451298619
 
plugin/amazon.txt · Last modified: 2008/05/21 15:04 by ach
 
Imprint Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki
WikiForumIRCBugsTranslate