11c1d842eSAndreas Gohr<?php 238539ccdSAndreas Gohr 338539ccdSAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient; 438539ccdSAndreas Gohruse DOMWrap\Document; 538539ccdSAndreas Gohr 61c1d842eSAndreas Gohr/** 71c1d842eSAndreas Gohr * DokuWiki Plugin amazonlight (Syntax Component) 81c1d842eSAndreas Gohr * 91c1d842eSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 101c1d842eSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 111c1d842eSAndreas Gohr */ 121c1d842eSAndreas Gohrclass syntax_plugin_amazonlight extends DokuWiki_Syntax_Plugin 131c1d842eSAndreas Gohr{ 141c1d842eSAndreas Gohr 151c1d842eSAndreas Gohr /** @var array what regions to use for the different countries */ 161c1d842eSAndreas Gohr const REGIONS = [ 1738539ccdSAndreas Gohr 'us' => 'www.amazon.com', 1838539ccdSAndreas Gohr 'ca' => 'www.amazon.ca', 1938539ccdSAndreas Gohr 'de' => 'www.amazon.de', 2038539ccdSAndreas Gohr 'gb' => 'www.amazon.co.uk', 2138539ccdSAndreas Gohr 'fr' => 'www.amazon.fr', 2238539ccdSAndreas Gohr 'jp' => 'www.amazon.co.jp', 231c1d842eSAndreas Gohr ]; 241c1d842eSAndreas Gohr 251c1d842eSAndreas Gohr /** @inheritDoc */ 261c1d842eSAndreas Gohr public function getType() 271c1d842eSAndreas Gohr { 281c1d842eSAndreas Gohr return 'substition'; 291c1d842eSAndreas Gohr } 301c1d842eSAndreas Gohr 311c1d842eSAndreas Gohr /** @inheritDoc */ 321c1d842eSAndreas Gohr public function getPType() 331c1d842eSAndreas Gohr { 341c1d842eSAndreas Gohr return 'block'; 351c1d842eSAndreas Gohr } 361c1d842eSAndreas Gohr 371c1d842eSAndreas Gohr /** @inheritDoc */ 381c1d842eSAndreas Gohr public function getSort() 391c1d842eSAndreas Gohr { 401c1d842eSAndreas Gohr return 160; 411c1d842eSAndreas Gohr } 421c1d842eSAndreas Gohr 431c1d842eSAndreas Gohr /** 441c1d842eSAndreas Gohr * Connect lookup pattern to lexer. 451c1d842eSAndreas Gohr * 461c1d842eSAndreas Gohr * @param string $mode Parser mode 471c1d842eSAndreas Gohr */ 481c1d842eSAndreas Gohr public function connectTo($mode) 491c1d842eSAndreas Gohr { 501c1d842eSAndreas Gohr $this->Lexer->addSpecialPattern('\{\{amazon>[\w:\\- =]+\}\}', $mode, 'plugin_amazonlight'); 511c1d842eSAndreas Gohr } 521c1d842eSAndreas Gohr 531c1d842eSAndreas Gohr /** @inheritDoc */ 541c1d842eSAndreas Gohr public function handle($match, $state, $pos, Doku_Handler $handler) 551c1d842eSAndreas Gohr { 561c1d842eSAndreas Gohr $match = substr($match, 9, -2); 57*74b25907SAndreas Gohr list($ctry, $asin) = sexplode(':', $match, 2); 581c1d842eSAndreas Gohr 591c1d842eSAndreas Gohr // no country given? 601c1d842eSAndreas Gohr if (empty($asin)) { 611c1d842eSAndreas Gohr $asin = $ctry; 621c1d842eSAndreas Gohr $ctry = 'us'; 631c1d842eSAndreas Gohr } 641c1d842eSAndreas Gohr 651c1d842eSAndreas Gohr // default parameters... 661c1d842eSAndreas Gohr $params = array( 671c1d842eSAndreas Gohr 'imgw' => $this->getConf('imgw'), 681c1d842eSAndreas Gohr 'imgh' => $this->getConf('imgh'), 691c1d842eSAndreas Gohr 'price' => $this->getConf('showprice'), 701c1d842eSAndreas Gohr ); 711c1d842eSAndreas Gohr // ...can be overridden 72*74b25907SAndreas Gohr list($asin, $more) = sexplode(' ', $asin, 2); 731c1d842eSAndreas Gohr $params['asin'] = $asin; 741c1d842eSAndreas Gohr 751c1d842eSAndreas Gohr if (preg_match('/(\d+)x(\d+)/i', $more, $match)) { 761c1d842eSAndreas Gohr $params['imgw'] = $match[1]; 771c1d842eSAndreas Gohr $params['imgh'] = $match[2]; 781c1d842eSAndreas Gohr } 791c1d842eSAndreas Gohr if (preg_match('/noprice/i', $more, $match)) { 801c1d842eSAndreas Gohr $params['price'] = false; 811c1d842eSAndreas Gohr } elseif (preg_match('/(show)?price/i', $more, $match)) { 821c1d842eSAndreas Gohr $params['price'] = true; 831c1d842eSAndreas Gohr } 841c1d842eSAndreas Gohr 851c1d842eSAndreas Gohr // correct country given? 861c1d842eSAndreas Gohr if ($ctry === 'uk') $ctry = 'gb'; 871c1d842eSAndreas Gohr if (!preg_match('/^(us|gb|jp|de|fr|ca)$/', $ctry)) { 881c1d842eSAndreas Gohr $ctry = 'us'; 891c1d842eSAndreas Gohr } 901c1d842eSAndreas Gohr $params['country'] = $ctry; 911c1d842eSAndreas Gohr 921c1d842eSAndreas Gohr return $params; 931c1d842eSAndreas Gohr } 941c1d842eSAndreas Gohr 951c1d842eSAndreas Gohr /** @inheritDoc */ 961c1d842eSAndreas Gohr public function render($mode, Doku_Renderer $renderer, $data) 971c1d842eSAndreas Gohr { 981c1d842eSAndreas Gohr if ($mode !== 'xhtml') { 991c1d842eSAndreas Gohr return false; 1001c1d842eSAndreas Gohr } 1011c1d842eSAndreas Gohr 1021c1d842eSAndreas Gohr $html = $this->output($data); 1032681f07aSAndreas Gohr if (!$html) { 1042681f07aSAndreas Gohr if ($data['country'] == 'de') { 1052681f07aSAndreas Gohr $renderer->interwikilink('Amazon', 'Amazon.de', 'amazon.de', $data['asin']); 1062681f07aSAndreas Gohr } else { 1072681f07aSAndreas Gohr $renderer->interwikilink('Amazon', 'Amazon', 'amazon', $data['asin']); 1082681f07aSAndreas Gohr } 1092681f07aSAndreas Gohr } 1102681f07aSAndreas Gohr 1111c1d842eSAndreas Gohr $renderer->doc .= $html; 1121c1d842eSAndreas Gohr 1131c1d842eSAndreas Gohr return true; 1141c1d842eSAndreas Gohr } 1151c1d842eSAndreas Gohr 1161c1d842eSAndreas Gohr /** 1171c1d842eSAndreas Gohr * @param array $param 1181c1d842eSAndreas Gohr * @return string 1191c1d842eSAndreas Gohr */ 1201c1d842eSAndreas Gohr protected function output($param) 1211c1d842eSAndreas Gohr { 1221c1d842eSAndreas Gohr global $conf; 1231c1d842eSAndreas Gohr 1241c1d842eSAndreas Gohr try { 1251c1d842eSAndreas Gohr $data = $this->fetchData($param['asin'], $param['country']); 12638539ccdSAndreas Gohr } catch (Exception $e) { 1271c1d842eSAndreas Gohr msg(hsc($e->getMessage()), -1); 1281c1d842eSAndreas Gohr return false; 1291c1d842eSAndreas Gohr } 1301c1d842eSAndreas Gohr 1311c1d842eSAndreas Gohr $img = ml($data['img'], array('w' => $param['imgw'], 'h' => $param['imgh'])); 1321c1d842eSAndreas Gohr 1331c1d842eSAndreas Gohr ob_start(); 1341c1d842eSAndreas Gohr echo '<div class="amazon">'; 1351c1d842eSAndreas Gohr echo '<a href="' . $data['url'] . '"'; 1361c1d842eSAndreas Gohr if ($conf['target']['extern']) echo ' target="' . $conf['target']['extern'] . '"'; 1371c1d842eSAndreas Gohr echo '>'; 1381c1d842eSAndreas Gohr echo '<img src="' . $img . '" width="' . $param['imgw'] . '" height="' . $param['imgh'] . '" alt="" />'; 1391c1d842eSAndreas Gohr echo '</a>'; 1401c1d842eSAndreas Gohr 1411c1d842eSAndreas Gohr echo '<div class="amazon_title">'; 1421c1d842eSAndreas Gohr echo '<a href="' . $data['url'] . '"'; 1431c1d842eSAndreas Gohr if ($conf['target']['extern']) echo ' target="' . $conf['target']['extern'] . '"'; 1441c1d842eSAndreas Gohr echo '>'; 1451c1d842eSAndreas Gohr echo hsc($data['title']); 1461c1d842eSAndreas Gohr echo '</a>'; 1471c1d842eSAndreas Gohr echo '</div>'; 1481c1d842eSAndreas Gohr 14938539ccdSAndreas Gohr echo '<div class="amazon_author">'; 15038539ccdSAndreas Gohr echo hsc($data['author']); 15138539ccdSAndreas Gohr echo '</div>'; 15238539ccdSAndreas Gohr 153e20d6e78SAndreas Gohr echo '<div class="amazon_isbn">'; 154e20d6e78SAndreas Gohr echo hsc($data['isbn']); 155e20d6e78SAndreas Gohr echo '</div>'; 156e20d6e78SAndreas Gohr 1571c1d842eSAndreas Gohr if ($param['price'] && $data['price']) { 1581c1d842eSAndreas Gohr echo '<div class="amazon_price">' . hsc($data['price']) . '</div>'; 1591c1d842eSAndreas Gohr } 1601c1d842eSAndreas Gohr echo '</div>'; 1611c1d842eSAndreas Gohr 1621c1d842eSAndreas Gohr return ob_get_clean(); 1631c1d842eSAndreas Gohr } 1641c1d842eSAndreas Gohr 1651c1d842eSAndreas Gohr /** 1661c1d842eSAndreas Gohr * Fetch the meta data 1671c1d842eSAndreas Gohr * 1681c1d842eSAndreas Gohr * @param string $asin 1691c1d842eSAndreas Gohr * @param string $country 1701c1d842eSAndreas Gohr * @return array 1711c1d842eSAndreas Gohr * @throws Exception 1721c1d842eSAndreas Gohr */ 1731c1d842eSAndreas Gohr protected function fetchData($asin, $country) 1741c1d842eSAndreas Gohr { 1751c1d842eSAndreas Gohr $partner = $this->getConf('partner_' . $country); 1761c1d842eSAndreas Gohr if (!$partner) $partner = 'none'; 1771c1d842eSAndreas Gohr $region = self::REGIONS[$country]; 1781c1d842eSAndreas Gohr 17938539ccdSAndreas Gohr $url = 'https://' . $region . '/dp/' . $asin; 1801c1d842eSAndreas Gohr 1811c1d842eSAndreas Gohr $http = new DokuHTTPClient(); 18238539ccdSAndreas Gohr $http->headers['User-Agent'] = 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'; 1831c1d842eSAndreas Gohr $html = $http->get($url); 1841c1d842eSAndreas Gohr if (!$html) { 18538539ccdSAndreas Gohr throw new Exception('Failed to fetch data. Status ' . $http->status); 1861c1d842eSAndreas Gohr } 1871c1d842eSAndreas Gohr 1881c1d842eSAndreas Gohr 18938539ccdSAndreas Gohr $doc = new Document(); 19038539ccdSAndreas Gohr $doc->html($html); 19138539ccdSAndreas Gohr 19238539ccdSAndreas Gohr $result = [ 19338539ccdSAndreas Gohr 'title' => $this->extract($doc, '#productTitle'), 19438539ccdSAndreas Gohr 'author' => $this->extract($doc, '#bylineInfo a'), 19538539ccdSAndreas Gohr 'rating' => $this->extract($doc, '#averageCustomerReviews span.a-declarative a > span'), 19638539ccdSAndreas Gohr 'price' => $this->extract($doc, '.priceToPay'), 19738539ccdSAndreas Gohr 'isbn' => $this->extract($doc, '#rpi-attribute-book_details-isbn10 .rpi-attribute-value'), 19838539ccdSAndreas Gohr 'img' => $this->extract($doc, '#imgTagWrapperId img', 'src'), 19938539ccdSAndreas Gohr 'url' => $url . '?tag=' . $partner, 20038539ccdSAndreas Gohr ]; 20138539ccdSAndreas Gohr 20238539ccdSAndreas Gohr if (!$result['title']) { 20338539ccdSAndreas Gohr $result['title'] = $this->extract($doc, 'title'); 2041c1d842eSAndreas Gohr } 20538539ccdSAndreas Gohr if (!$result['title']) { 20638539ccdSAndreas Gohr throw new Exception('Could not find title in data'); 2071c1d842eSAndreas Gohr } 2081c1d842eSAndreas Gohr 2091c1d842eSAndreas Gohr return $result; 2101c1d842eSAndreas Gohr } 2111c1d842eSAndreas Gohr 2121c1d842eSAndreas Gohr /** 21338539ccdSAndreas Gohr * Extract text or attribute from a selector 21438539ccdSAndreas Gohr * 21538539ccdSAndreas Gohr * @param Document $doc 21638539ccdSAndreas Gohr * @param string $selector 21738539ccdSAndreas Gohr * @param string|null $attr attribute to extract, omit for text 2181c1d842eSAndreas Gohr * @return string 2191c1d842eSAndreas Gohr */ 22038539ccdSAndreas Gohr protected function extract(Document $doc, string $selector, $attr = null): string 2211c1d842eSAndreas Gohr { 22238539ccdSAndreas Gohr $element = $doc->find($selector)->first(); 22338539ccdSAndreas Gohr if($element === null) { 22438539ccdSAndreas Gohr return ''; 2251c1d842eSAndreas Gohr } 22638539ccdSAndreas Gohr if ($attr) { 22738539ccdSAndreas Gohr return $element->attr($attr); 22838539ccdSAndreas Gohr } else { 22938539ccdSAndreas Gohr return $element->text(); 23038539ccdSAndreas Gohr } 23138539ccdSAndreas Gohr } 2321c1d842eSAndreas Gohr} 2331c1d842eSAndreas Gohr 234