dolistore.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <?php
  2. /*
  3. * Copyright (C) 2017 Oscss-Shop <support@oscss-shop.fr>.
  4. *
  5. * This program is free software; you can redistribute it and/or modifyion 2.0 (the "License");
  6. * it under the terms of the GNU General Public License as published bypliance with the License.
  7. * the Free Software Foundation; either version 3 of the License, or
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. * or see https://www.gnu.org/
  17. */
  18. include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
  19. if (!class_exists('PrestaShopWebservice')) { // We keep this because some modules add this lib too into a different path. This is to avoid "Cannot declare class PrestaShopWebservice" errors.
  20. include_once DOL_DOCUMENT_ROOT.'/admin/dolistore/class/PSWebServiceLibrary.class.php';
  21. }
  22. /**
  23. * Class Dolistore
  24. */
  25. class Dolistore
  26. {
  27. /**
  28. * beginning of pagination
  29. * @var int
  30. */
  31. public $start;
  32. /**
  33. * end of pagination
  34. * @var int
  35. */
  36. public $end;
  37. public $per_page; // pagination: display per page
  38. public $categorie; // the current categorie
  39. public $search; // the search keywords
  40. // setups
  41. public $url; // the url of this page
  42. public $shop_url; // the url of the shop
  43. public $lang; // the integer representing the lang in the store
  44. public $debug_api; // usefull if no dialog
  45. /**
  46. * Constructor
  47. *
  48. * @param boolean $debug Enable debug of request on screen
  49. */
  50. public function __construct($debug = false)
  51. {
  52. global $conf, $langs;
  53. $this->url = DOL_URL_ROOT.'/admin/modules.php?mode=marketplace';
  54. $this->shop_url = 'https://www.dolistore.com/index.php?controller=product&id_product=';
  55. $this->debug_api = $debug;
  56. $langtmp = explode('_', $langs->defaultlang);
  57. $lang = $langtmp[0];
  58. $lang_array = array('en'=>1, 'fr'=>2, 'es'=>3, 'it'=>4, 'de'=>5); // Into table ps_lang of Prestashop - 1
  59. if (!in_array($lang, array_keys($lang_array))) {
  60. $lang = 'en';
  61. }
  62. $this->lang = $lang_array[$lang];
  63. }
  64. /**
  65. * Load data from remote Dolistore market place.
  66. * This fills ->categories
  67. *
  68. * @return void
  69. */
  70. public function getRemoteCategories()
  71. {
  72. global $conf;
  73. try {
  74. $this->api = new PrestaShopWebservice($conf->global->MAIN_MODULE_DOLISTORE_API_SRV, $conf->global->MAIN_MODULE_DOLISTORE_API_KEY, $this->debug_api);
  75. dol_syslog("Call API with MAIN_MODULE_DOLISTORE_API_SRV = ".getDolGlobalString('MAIN_MODULE_DOLISTORE_API_SRV'));
  76. // $conf->global->MAIN_MODULE_DOLISTORE_API_KEY is for the login of basic auth. There is no password as it is public data.
  77. // Here we set the option array for the Webservice : we want categories resources
  78. $opt = array();
  79. $opt['resource'] = 'categories';
  80. $opt['display'] = '[id,id_parent,nb_products_recursive,active,is_root_category,name,description]';
  81. $opt['sort'] = 'id_asc';
  82. // Call
  83. dol_syslog("Call API with opt = ".var_export($opt, true));
  84. $xml = $this->api->get($opt);
  85. $this->categories = $xml->categories->children();
  86. } catch (PrestaShopWebserviceException $e) {
  87. // Here we are dealing with errors
  88. $trace = $e->getTrace();
  89. if ($trace[0]['args'][0] == 404) {
  90. die('Bad ID');
  91. } elseif ($trace[0]['args'][0] == 401) {
  92. die('Bad auth key');
  93. } else {
  94. print 'Can not access to '.$conf->global->MAIN_MODULE_DOLISTORE_API_SRV.'<br>';
  95. print $e->getMessage();
  96. }
  97. }
  98. }
  99. /**
  100. * Load data from remote Dolistore market place.
  101. * This fills ->products
  102. *
  103. * @param array $options Options. If 'categorie' is defined, we filter products on this category id
  104. * @return void
  105. */
  106. public function getRemoteProducts($options = array('start' => 0, 'end' => 10, 'per_page' => 50, 'categorie' => 0, 'search' => ''))
  107. {
  108. global $conf;
  109. $this->start = $options['start'];
  110. $this->end = $options['end'];
  111. $this->per_page = $options['per_page'];
  112. $this->categorie = $options['categorie'];
  113. $this->search = $options['search'];
  114. if ($this->end == 0) {
  115. $this->end = $this->per_page;
  116. }
  117. try {
  118. $this->api = new PrestaShopWebservice($conf->global->MAIN_MODULE_DOLISTORE_API_SRV, $conf->global->MAIN_MODULE_DOLISTORE_API_KEY, $this->debug_api);
  119. dol_syslog("Call API with MAIN_MODULE_DOLISTORE_API_SRV = ".getDolGlobalString('MAIN_MODULE_DOLISTORE_API_SRV'));
  120. // $conf->global->MAIN_MODULE_DOLISTORE_API_KEY is for the login of basic auth. There is no password as it is public data.
  121. // Here we set the option array for the Webservice : we want products resources
  122. $opt = array();
  123. $opt['resource'] = 'products';
  124. $opt2 = array();
  125. // make a search to limit the id returned.
  126. if ($this->search != '') {
  127. $opt2['url'] = $conf->global->MAIN_MODULE_DOLISTORE_API_SRV.'/api/search?query='.$this->search.'&language='.$this->lang; // It seems for search, key start with
  128. // Call
  129. dol_syslog("Call API with opt2 = ".var_export($opt2, true));
  130. $xml = $this->api->get($opt2);
  131. $products = array();
  132. foreach ($xml->products->children() as $product) {
  133. $products[] = (int) $product['id'];
  134. }
  135. $opt['filter[id]'] = '['.implode('|', $products).']';
  136. } elseif ($this->categorie != 0) { // We filter on category, so we first get list of product id in this category
  137. // $opt2['url'] is set by default to $this->url.'/api/'.$options['resource'];
  138. $opt2['resource'] = 'categories';
  139. $opt2['id'] = $this->categorie;
  140. // Call
  141. dol_syslog("Call API with opt2 = ".var_export($opt2, true));
  142. $xml = $this->api->get($opt2);
  143. $products = array();
  144. foreach ($xml->category->associations->products->children() as $product) {
  145. $products[] = (int) $product->id;
  146. }
  147. $opt['filter[id]'] = '['.implode('|', $products).']';
  148. }
  149. $opt['display'] = '[id,name,id_default_image,id_category_default,reference,price,condition,show_price,date_add,date_upd,description_short,description,module_version,dolibarr_min,dolibarr_max]';
  150. $opt['sort'] = 'id_desc';
  151. $opt['filter[active]'] = '[1]';
  152. $opt['limit'] = "$this->start,$this->end";
  153. // $opt['filter[id]'] contais list of product id that are result of search
  154. // Call API to get the detail
  155. dol_syslog("Call API with opt = ".var_export($opt, true));
  156. $xml = $this->api->get($opt);
  157. $this->products = $xml->products->children();
  158. } catch (PrestaShopWebserviceException $e) {
  159. // Here we are dealing with errors
  160. $trace = $e->getTrace();
  161. if ($trace[0]['args'][0] == 404) {
  162. die('Bad ID');
  163. } elseif ($trace[0]['args'][0] == 401) {
  164. die('Bad auth key');
  165. } else {
  166. print 'Can not access to '.$conf->global->MAIN_MODULE_DOLISTORE_API_SRV.'<br>';
  167. print $e->getMessage();
  168. }
  169. }
  170. }
  171. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  172. /**
  173. * Return tree of Dolistore categories. $this->categories must have been loaded before.
  174. *
  175. * @param int $parent Id of parent category
  176. * @return string
  177. */
  178. public function get_categories($parent = 0)
  179. {
  180. // phpcs:enable
  181. if (!isset($this->categories)) {
  182. die('not possible');
  183. }
  184. if ($parent != 0) {
  185. $html = '<ul>';
  186. } else {
  187. $html = '';
  188. }
  189. $nbofcateg = count($this->categories);
  190. for ($i = 0; $i < $nbofcateg; $i++) {
  191. $cat = $this->categories[$i];
  192. if ($cat->is_root_category == 1 && $parent == 0) {
  193. $html .= '<li class="root"><h3 class="nomargesupinf"><a class="nomargesupinf link2cat" href="?mode=marketplace&categorie='.((int) $cat->id).'" ';
  194. $html .= 'title="'.dol_escape_htmltag(strip_tags($cat->description->language[$this->lang - 1])).'">'.dol_escape_htmltag($cat->name->language[$this->lang - 1]).' <sup>'.dol_escape_htmltag($cat->nb_products_recursive).'</sup></a></h3>';
  195. $html .= self::get_categories($cat->id);
  196. $html .= "</li>\n";
  197. } elseif (trim($cat->id_parent) == $parent && $cat->active == 1 && trim($cat->id_parent) != 0) { // si cat est de ce niveau
  198. $select = ($cat->id == $this->categorie) ? ' selected' : '';
  199. $html .= '<li><a class="link2cat'.$select.'" href="?mode=marketplace&categorie='.((int) $cat->id).'"';
  200. $html .= ' title="'.dol_escape_htmltag(strip_tags($cat->description->language[$this->lang - 1])).'" ';
  201. $html .= '>'.dol_escape_htmltag($cat->name->language[$this->lang - 1]).' <sup>'.dol_escape_htmltag($cat->nb_products_recursive).'</sup></a>';
  202. $html .= self::get_categories($cat->id);
  203. $html .= "</li>\n";
  204. }
  205. }
  206. if ($html == '<ul>') {
  207. return '';
  208. }
  209. if ($parent != 0) {
  210. return $html.'</ul>';
  211. } else {
  212. return $html;
  213. }
  214. }
  215. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  216. /**
  217. * Return list of product formated for output
  218. *
  219. * @return string HTML output
  220. */
  221. public function get_products()
  222. {
  223. // phpcs:enable
  224. global $langs, $conf;
  225. $html = "";
  226. $last_month = time() - (30 * 24 * 60 * 60);
  227. foreach ($this->products as $product) {
  228. // check new product ?
  229. $newapp = '';
  230. if ($last_month < strtotime($product->date_add)) {
  231. $newapp .= '<span class="newApp">'.$langs->trans('New').'</span> ';
  232. }
  233. // check updated ?
  234. if ($last_month < strtotime($product->date_upd) && $newapp == '') {
  235. $newapp .= '<span class="updatedApp">'.$langs->trans('Updated').'</span> ';
  236. }
  237. // add image or default ?
  238. if ($product->id_default_image != '') {
  239. $image_url = DOL_URL_ROOT.'/admin/dolistore/ajax/image.php?id_product='.urlencode(((int) $product->id)).'&id_image='.urlencode(((int) $product->id_default_image));
  240. $images = '<a href="'.$image_url.'" class="documentpreview" target="_blank" rel="noopener noreferrer" mime="image/png" title="'.dol_escape_htmltag($product->name->language[$this->lang - 1].', '.$langs->trans('Version').' '.$product->module_version).'">';
  241. $images .= '<img src="'.$image_url.'&quality=home_default" style="max-height:250px;max-width: 210px;" alt="" /></a>';
  242. } else {
  243. $images = '<img src="'.DOL_URL_ROOT.'/admin/dolistore/img/NoImageAvailable.png" />';
  244. }
  245. // free or pay ?
  246. if ($product->price > 0) {
  247. $price = '<h3>'.price(price2num($product->price, 'MT'), 0, $langs, 1, -1, -1, 'EUR').' '.$langs->trans("HT").'</h3>';
  248. $download_link = '<a target="_blank" href="'.$this->shop_url.urlencode($product->id).'"><img width="32" src="'.DOL_URL_ROOT.'/admin/dolistore/img/follow.png" /></a>';
  249. } else {
  250. $price = '<h3>'.$langs->trans('Free').'</h3>';
  251. $download_link = '<a target="_blank" rel="noopener noreferrer" href="'.$this->shop_url.urlencode($product->id).'"><img width="32" src="'.DOL_URL_ROOT.'/admin/dolistore/img/Download-128.png" /></a>';
  252. $download_link .= '<br><br><a target="_blank" href="'.$this->shop_url.urlencode($product->id).'"><img width="32" src="'.DOL_URL_ROOT.'/admin/dolistore/img/follow.png" /></a>';
  253. }
  254. // Set and check version
  255. $version = '';
  256. if ($this->version_compare($product->dolibarr_min, DOL_VERSION) <= 0) {
  257. if ($this->version_compare($product->dolibarr_max, DOL_VERSION) >= 0) {
  258. //compatible
  259. $version = '<span class="compatible">'.$langs->trans(
  260. 'CompatibleUpTo',
  261. $product->dolibarr_max,
  262. $product->dolibarr_min,
  263. $product->dolibarr_max
  264. ).'</span>';
  265. $compatible = '';
  266. } else {
  267. //never compatible, module expired
  268. $version = '<span class="notcompatible">'.$langs->trans(
  269. 'NotCompatible',
  270. DOL_VERSION,
  271. $product->dolibarr_min,
  272. $product->dolibarr_max
  273. ).'</span>';
  274. $compatible = 'NotCompatible';
  275. }
  276. } else {
  277. //need update
  278. $version = '<span class="compatibleafterupdate">'.$langs->trans(
  279. 'CompatibleAfterUpdate',
  280. DOL_VERSION,
  281. $product->dolibarr_min,
  282. $product->dolibarr_max
  283. ).'</span>';
  284. $compatible = 'NotCompatible';
  285. }
  286. //output template
  287. $html .= '<tr class="app oddeven '.dol_escape_htmltag($compatible).'">';
  288. $html .= '<td class="center" width="210"><div class="newAppParent">';
  289. $html .= $newapp.$images; // No dol_escape_htmltag, it is already escape html
  290. $html .= '</div></td>';
  291. $html .= '<td class="margeCote"><h2 class="appTitle">';
  292. $html .= dol_escape_htmltag($product->name->language[$this->lang - 1]);
  293. $html .= '<br><small>';
  294. $html .= $version; // No dol_escape_htmltag, it is already escape html
  295. $html .= '</small></h2>';
  296. $html .= '<small> '.dol_print_date(dol_stringtotime($product->date_upd), 'dayhour').' - '.$langs->trans('Ref').': '.dol_escape_htmltag($product->reference).' - '.dol_escape_htmltag($langs->trans('Id')).': '.((int) $product->id).'</small><br><br>'.dol_escape_htmltag($product->description_short->language[$this->lang - 1]).'</td>';
  297. // do not load if display none
  298. //$html .= '<td style="display:none;" class="long_description">'.$product->description->language[$this->lang - 1].'</td>';
  299. $html .= '<td class="margeCote center">';
  300. $html .= $price;
  301. $html .= '</td>';
  302. $html .= '<td class="margeCote">'.$download_link.'</td>';
  303. $html .= '</tr>';
  304. }
  305. return $html;
  306. }
  307. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  308. /**
  309. * get previous link
  310. *
  311. * @param string $text symbol previous
  312. * @return string html previous link
  313. */
  314. public function get_previous_link($text = '<<')
  315. {
  316. // phpcs:enable
  317. return '<a href="'.$this->get_previous_url().'" class="button">'.dol_escape_htmltag($text).'</a>';
  318. }
  319. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  320. /**
  321. * get next link
  322. *
  323. * @param string $text symbol next
  324. * @return string html next link
  325. */
  326. public function get_next_link($text = '>>')
  327. {
  328. // phpcs:enable
  329. return '<a href="'.$this->get_next_url().'" class="button">'.dol_escape_htmltag($text).'</a>';
  330. }
  331. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  332. /**
  333. * get previous url
  334. *
  335. * @return string previous url
  336. */
  337. public function get_previous_url()
  338. {
  339. // phpcs:enable
  340. $param_array = array();
  341. if ($this->start < $this->per_page) {
  342. $sub = 0;
  343. } else {
  344. $sub = $this->per_page;
  345. }
  346. $param_array['start'] = $this->start - $sub;
  347. $param_array['end'] = $this->end - $sub;
  348. if ($this->categorie != 0) {
  349. $param_array['categorie'] = $this->categorie;
  350. }
  351. $param = http_build_query($param_array);
  352. return $this->url."&".$param;
  353. }
  354. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  355. /**
  356. * get next url
  357. *
  358. * @return string next url
  359. */
  360. public function get_next_url()
  361. {
  362. // phpcs:enable
  363. $param_array = array();
  364. if (count($this->products) < $this->per_page) {
  365. $add = 0;
  366. } else {
  367. $add = $this->per_page;
  368. }
  369. $param_array['start'] = $this->start + $add;
  370. $param_array['end'] = $this->end + $add;
  371. if ($this->categorie != 0) {
  372. $param_array['categorie'] = $this->categorie;
  373. }
  374. $param = http_build_query($param_array);
  375. return $this->url."&".$param;
  376. }
  377. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  378. /**
  379. * version compare
  380. *
  381. * @param string $v1 version 1
  382. * @param string $v2 version 2
  383. * @return int result of compare
  384. */
  385. public function version_compare($v1, $v2)
  386. {
  387. // phpcs:enable
  388. $v1 = explode('.', $v1);
  389. $v2 = explode('.', $v2);
  390. $ret = 0;
  391. $level = 0;
  392. $count1 = count($v1);
  393. $count2 = count($v2);
  394. $maxcount = max($count1, $count2);
  395. while ($level < $maxcount) {
  396. $operande1 = isset($v1[$level]) ? $v1[$level] : 'x';
  397. $operande2 = isset($v2[$level]) ? $v2[$level] : 'x';
  398. $level++;
  399. if (strtoupper($operande1) == 'X' || strtoupper($operande2) == 'X' || $operande1 == '*' || $operande2 == '*') {
  400. break;
  401. }
  402. if ($operande1 < $operande2) {
  403. $ret = -$level;
  404. break;
  405. }
  406. if ($operande1 > $operande2) {
  407. $ret = $level;
  408. break;
  409. }
  410. }
  411. //print join('.',$versionarray1).'('.count($versionarray1).') / '.join('.',$versionarray2).'('.count($versionarray2).') => '.$ret.'<br>'."\n";
  412. return $ret;
  413. }
  414. }