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 $renderer->doc .= $html; 106 107 return true; 108 } 109 110 /** 111 * @param array $param 112 * @return string 113 */ 114 protected function output($param) 115 { 116 global $conf; 117 118 try { 119 $data = $this->fetchData($param['asin'], $param['country']); 120 } catch (\Exception $e) { 121 msg(hsc($e->getMessage()), -1); 122 return false; 123 } 124 125 $img = ml($data['img'], array('w' => $param['imgw'], 'h' => $param['imgh'])); 126 127 ob_start(); 128 echo '<div class="amazon">'; 129 echo '<a href="' . $data['url'] . '"'; 130 if ($conf['target']['extern']) echo ' target="' . $conf['target']['extern'] . '"'; 131 echo '>'; 132 echo '<img src="' . $img . '" width="' . $param['imgw'] . '" height="' . $param['imgh'] . '" alt="" />'; 133 echo '</a>'; 134 135 echo '<div class="amazon_title">'; 136 echo '<a href="' . $data['url'] . '"'; 137 if ($conf['target']['extern']) echo ' target="' . $conf['target']['extern'] . '"'; 138 echo '>'; 139 echo hsc($data['title']); 140 echo '</a>'; 141 echo '</div>'; 142 143 echo '<div class="amazon_isbn">'; 144 echo hsc($data['isbn']); 145 echo '</div>'; 146 147 if ($param['price'] && $data['price']) { 148 echo '<div class="amazon_price">' . hsc($data['price']) . '</div>'; 149 } 150 echo '</div>'; 151 152 return ob_get_clean(); 153 } 154 155 /** 156 * Fetch the meta data 157 * 158 * @param string $asin 159 * @param string $country 160 * @return array 161 * @throws Exception 162 */ 163 protected function fetchData($asin, $country) 164 { 165 $partner = $this->getConf('partner_' . $country); 166 if (!$partner) $partner = 'none'; 167 $region = self::REGIONS[$country]; 168 169 $attr = [ 170 'ServiceVersion' => '20070822', 171 'OneJS' => '1', 172 'Operation' => 'GetAdHtml', 173 'MarketPlace' => strtoupper($country), 174 'source' => 'ss', 175 'ref' => 'as_ss_li_til', 176 'ad_type' => 'product_link', 177 'tracking_id' => $partner, 178 'marketplace' => 'amazon', 179 'region' => strtoupper($country), 180 'placement' => '0670022411', 181 'asins' => $asin, 182 'show_border' => 'true', 183 'link_opens_in_new_window' => 'true', 184 ]; 185 $url = 'http://' . $region . '.amazon-adsystem.com/widgets/q?' . buildURLparams($attr, '&'); 186 187 $http = new DokuHTTPClient(); 188 $html = $http->get($url); 189 if (!$html) { 190 throw new \Exception('Failed to fetch data. Status ' . $http->status); 191 } 192 193 $result = []; 194 195 if (preg_match('/class="price".*?>(.*?)<\/span>/s', $html, $m)) { 196 $result['price'] = $m[1]; 197 } 198 199 if (preg_match('/<a .* id="titlehref" [^>]*?>([^<]*?)<\/a>/s', $html, $m)) { 200 $result['title'] = $m[1]; 201 } 202 203 if (preg_match('/<a .* id="titlehref" href=(.*?) /s', $html, $m)) { 204 $result['url'] = trim($m[1], '\'"'); 205 } 206 207 if (preg_match('/^\d{10}$/', $asin)) { 208 $result['isbn'] = 'ISBN ' . $asin; 209 } 210 211 $result['img'] = $this->getImageURL($asin, $country); 212 213 return $result; 214 } 215 216 /** 217 * @param $asin 218 * @param $country 219 * @return string 220 */ 221 protected function getImageURL($asin, $country) 222 { 223 $partner = $this->getConf('partner_' . $country); 224 if (!$partner) $partner = 'none'; 225 $region = self::REGIONS[$country]; 226 227 $attr = [ 228 '_encoding' => 'UTF8', 229 'ASIN' => $asin, 230 'Format' => '_SL250_', 231 'ID' => 'AsinImage', 232 'MarketPlace' => strtoupper($country), 233 'ServiceVersion' => '20070822', 234 'WS' => '1', 235 'tag' => $partner, 236 ]; 237 $url = 'http://' . $region . '.amazon-adsystem.com/widgets/q?' . buildURLparams($attr, '&'); 238 239 return $url; 240 } 241 242} 243 244