1<?php
2/**
3 * DokuWiki Plugin amazonlight (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr <andi@splitbrain.org>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) {
11    die();
12}
13
14class syntax_plugin_amazonlight extends DokuWiki_Syntax_Plugin
15{
16
17    /** @var array what regions to use for the different countries */
18    const REGIONS = [
19        'us' => 'ws-na',
20        'ca' => 'ws-na',
21        'de' => 'ws-eu',
22        'gb' => 'ws-eu',
23        'fr' => 'ws-eu',
24        'jp' => 'ws-fe',
25    ];
26
27    /** @inheritDoc */
28    public function getType()
29    {
30        return 'substition';
31    }
32
33    /** @inheritDoc */
34    public function getPType()
35    {
36        return 'block';
37    }
38
39    /** @inheritDoc */
40    public function getSort()
41    {
42        return 160;
43    }
44
45    /**
46     * Connect lookup pattern to lexer.
47     *
48     * @param string $mode Parser mode
49     */
50    public function connectTo($mode)
51    {
52        $this->Lexer->addSpecialPattern('\{\{amazon>[\w:\\- =]+\}\}', $mode, 'plugin_amazonlight');
53    }
54
55    /** @inheritDoc */
56    public function handle($match, $state, $pos, Doku_Handler $handler)
57    {
58        $match = substr($match, 9, -2);
59        list($ctry, $asin) = explode(':', $match, 2);
60
61        // no country given?
62        if (empty($asin)) {
63            $asin = $ctry;
64            $ctry = 'us';
65        }
66
67        // default parameters...
68        $params = array(
69            'imgw' => $this->getConf('imgw'),
70            'imgh' => $this->getConf('imgh'),
71            'price' => $this->getConf('showprice'),
72        );
73        // ...can be overridden
74        list($asin, $more) = explode(' ', $asin, 2);
75        $params['asin'] = $asin;
76
77        if (preg_match('/(\d+)x(\d+)/i', $more, $match)) {
78            $params['imgw'] = $match[1];
79            $params['imgh'] = $match[2];
80        }
81        if (preg_match('/noprice/i', $more, $match)) {
82            $params['price'] = false;
83        } elseif (preg_match('/(show)?price/i', $more, $match)) {
84            $params['price'] = true;
85        }
86
87        // correct country given?
88        if ($ctry === 'uk') $ctry = 'gb';
89        if (!preg_match('/^(us|gb|jp|de|fr|ca)$/', $ctry)) {
90            $ctry = 'us';
91        }
92        $params['country'] = $ctry;
93
94        return $params;
95    }
96
97    /** @inheritDoc */
98    public function render($mode, Doku_Renderer $renderer, $data)
99    {
100        if ($mode !== 'xhtml') {
101            return false;
102        }
103
104        $html = $this->output($data);
105        if (!$html) {
106            if ($data['country'] == 'de') {
107                $renderer->interwikilink('Amazon', 'Amazon.de', 'amazon.de', $data['asin']);
108            } else {
109                $renderer->interwikilink('Amazon', 'Amazon', 'amazon', $data['asin']);
110            }
111        }
112
113        $renderer->doc .= $html;
114
115        return true;
116    }
117
118    /**
119     * @param array $param
120     * @return string
121     */
122    protected function output($param)
123    {
124        global $conf;
125
126        try {
127            $data = $this->fetchData($param['asin'], $param['country']);
128        } catch (\Exception $e) {
129            msg(hsc($e->getMessage()), -1);
130            return false;
131        }
132
133        $img = ml($data['img'], array('w' => $param['imgw'], 'h' => $param['imgh']));
134
135        ob_start();
136        echo '<div class="amazon">';
137        echo '<a href="' . $data['url'] . '"';
138        if ($conf['target']['extern']) echo ' target="' . $conf['target']['extern'] . '"';
139        echo '>';
140        echo '<img src="' . $img . '" width="' . $param['imgw'] . '" height="' . $param['imgh'] . '" alt="" />';
141        echo '</a>';
142
143        echo '<div class="amazon_title">';
144        echo '<a href="' . $data['url'] . '"';
145        if ($conf['target']['extern']) echo ' target="' . $conf['target']['extern'] . '"';
146        echo '>';
147        echo hsc($data['title']);
148        echo '</a>';
149        echo '</div>';
150
151        echo '<div class="amazon_isbn">';
152        echo hsc($data['isbn']);
153        echo '</div>';
154
155        if ($param['price'] && $data['price']) {
156            echo '<div class="amazon_price">' . hsc($data['price']) . '</div>';
157        }
158        echo '</div>';
159
160        return ob_get_clean();
161    }
162
163    /**
164     * Fetch the meta data
165     *
166     * @param string $asin
167     * @param string $country
168     * @return array
169     * @throws Exception
170     */
171    protected function fetchData($asin, $country)
172    {
173        $partner = $this->getConf('partner_' . $country);
174        if (!$partner) $partner = 'none';
175        $region = self::REGIONS[$country];
176
177        $attr = [
178            'ServiceVersion' => '20070822',
179            'OneJS' => '1',
180            'Operation' => 'GetAdHtml',
181            'MarketPlace' => strtoupper($country),
182            'source' => 'ss',
183            'ref' => 'as_ss_li_til',
184            'ad_type' => 'product_link',
185            'tracking_id' => $partner,
186            'marketplace' => 'amazon',
187            'region' => strtoupper($country),
188            'placement' => '0670022411',
189            'asins' => $asin,
190            'show_border' => 'true',
191            'link_opens_in_new_window' => 'true',
192        ];
193        $url = 'http://' . $region . '.amazon-adsystem.com/widgets/q?' . buildURLparams($attr, '&');
194
195        $http = new DokuHTTPClient();
196        $html = $http->get($url);
197        if (!$html) {
198            throw new \Exception('Failed to fetch data. Status ' . $http->status);
199        }
200
201        $result = [];
202
203        if (preg_match('/class="price".*?>(.*?)<\/span>/s', $html, $m)) {
204            $result['price'] = $m[1];
205        }
206
207        if (preg_match('/<a .* id="titlehref" [^>]*?>([^<]*?)<\/a>/s', $html, $m)) {
208            $result['title'] = $m[1];
209        } else {
210            throw new \Exception('Could not find title in data');
211        }
212
213        if (preg_match('/<a .* id="titlehref" href=(.*?) /s', $html, $m)) {
214            $result['url'] = trim($m[1], '\'"');
215        } else {
216            throw new \Exception('Could not find url in data');
217        }
218
219        if (preg_match('/^\d{10,13}$/', $asin)) {
220            $result['isbn'] = 'ISBN ' . $asin;
221        }
222
223        $result['img'] = $this->getImageURL($asin, $country);
224
225        return $result;
226    }
227
228    /**
229     * @param $asin
230     * @param $country
231     * @return string
232     */
233    protected function getImageURL($asin, $country)
234    {
235        $partner = $this->getConf('partner_' . $country);
236        if (!$partner) $partner = 'none';
237        $region = self::REGIONS[$country];
238
239        $attr = [
240            '_encoding' => 'UTF8',
241            'ASIN' => $asin,
242            'Format' => '_SL250_',
243            'ID' => 'AsinImage',
244            'MarketPlace' => strtoupper($country),
245            'ServiceVersion' => '20070822',
246            'WS' => '1',
247            'tag' => $partner,
248        ];
249        $url = 'https://' . $region . '.amazon-adsystem.com/widgets/q?' . buildURLparams($attr, '&');
250
251        return $url;
252    }
253
254}
255
256