21include_once DOL_DOCUMENT_ROOT.
'/core/lib/admin.lib.php';
55 public $file_source_url;
82 public $dolistore_api_url;
86 public $dolistore_api_key;
91 public $dolistoreApiStatus;
96 public $dolistoreApiError;
101 public $githubFileStatus;
106 public $githubFileError;
116 public $numberOfProviders;
126 public $numberTotalOfProducts;
131 public $numberTotalOfPages;
136 public $numberOfProducts;
147 $this->debug_api = $debug;
149 $this->url = DOL_URL_ROOT.
'/admin/modules.php?mode=marketplace';
152 $this->dolistore_api_url =
getDolGlobalString(
'MAIN_MODULE_DOLISTORE_API_SRV',
'https://www.dolistore.com/api/');
153 $this->dolistore_api_key =
getDolGlobalString(
'MAIN_MODULE_DOLISTORE_API_KEY',
'dolistorepublicapi');
154 $this->shop_url =
getDolGlobalString(
'MAIN_MODULE_DOLISTORE_SHOP_URL',
'https://www.dolistore.com');
157 $this->file_source_url =
"https://raw.githubusercontent.com/Dolibarr/dolibarr-community-modules/refs/heads/main/index.yaml";
158 $this->cache_file = DOL_DATA_ROOT.
'/admin/temp/remote_github_modules_file.yaml';
160 $lang = $langs->defaultlang;
161 $lang_array = array(
'en_US',
'fr_FR',
'es_ES',
'it_IT',
'de_DE');
162 if (!in_array($lang, $lang_array)) {
178 $cachedelayforgithubrepo =
getDolGlobalInt(
'MAIN_REMOTE_GITHUBREPO_CACHE_DELAY', 86400);
182 $this->githubFileError = $this->error;
183 $this->githubFileStatus =
dol_is_file($this->cache_file) ? 1 : 0;
192 $this->numberOfProviders = $this->dolistoreApiStatus + $this->githubFileStatus;
202 public function callApi($resource, $options =
false)
205 if (empty($this->dolistore_api_key) || empty($this->dolistore_api_url)) {
206 return array(
'status_code' => 0,
'response' =>
null);
213 $httpheader = array(
'DOLAPIKEY: '.$this->dolistore_api_key);
214 if ($basicAuthLogin) {
215 $httpheader[] =
'Authorization: Basic '.base64_encode($basicAuthLogin.
':'.$basicAuthPassword);
218 $url = $this->dolistore_api_url . (preg_match(
'/\/$/', $this->dolistore_api_url) ?
'' :
'/') . $resource;
220 $options[
'apikey'] = $this->dolistore_api_key;
223 $url .=
'?' . http_build_query($options);
226 $response =
getURLContent($url,
'GET',
'', 1, $httpheader, array(
'https'), 0, -1, 5, 5);
228 $status_code = $response[
'http_code'];
231 if ($status_code == 200) {
232 $body = $response[
'content'];
233 $body = json_decode($body,
true);
234 $returnarray = array(
235 'status_code' => $status_code,
239 $returnarray = array(
240 'status_code' => $status_code,
243 if (!empty($response[
'curl_error_no'])) {
244 $returnarray[
'curl_error_no'] = $response[
'curl_error_no'];
246 if (!empty($response[
'curl_error_msg'])) {
247 $returnarray[
'curl_error_msg'] = $response[
'curl_error_msg'];
264 if (!empty($this->cache_file) && file_exists($this->cache_file)) {
265 dol_syslog(__METHOD__ .
" - Loading cache file: " . $this->cache_file, LOG_DEBUG);
267 $content = file_get_contents($this->cache_file);
268 if ($content !==
false) {
269 $modules = $this->
readYaml($content);
271 dol_syslog(__METHOD__ .
" - Error reading cache file", LOG_ERR);
286 $organized_tree = array();
290 'lang' => $this->lang
295 $resCategories = $this->
callApi(
'categories', $data);
296 if (isset($resCategories[
'response']) && is_array($resCategories[
'response'])) {
297 $organized_tree = $resCategories[
'response'];
303 foreach ($organized_tree as $key => $value) {
304 if ($value[
'label'] !=
"Versions" && $value[
'label'] !=
"Specials") {
305 $html .=
'<li' . ($current == $value[
'rowid'] ?
' class="active"' :
'') .
'>';
306 $html .=
'<a href="?mode=marketplace&categorie=' . $value[
'rowid'] .
'">' . $value[
'label'] .
'</a>';
307 if (isset($value[
'children'])) {
309 usort($value[
'children'], $this->
buildSorter(
'position'));
310 foreach ($value[
'children'] as $key_children => $value_children) {
311 $html .=
'<li' . ($current == $value_children[
'rowid'] ?
' class="active"' :
'') .
'>';
312 $html .=
'<a href="?mode=marketplace&categorie=' . $value_children[
'rowid'] .
'" title="' .
dol_escape_htmltag(strip_tags($value_children[
'description'])) .
'">' . $value_children[
'label'] .
'</a>';
333 $langs->load(
"products");
336 $last_month =
dol_now() - (30 * 24 * 60 * 60);
337 $dolibarrversiontouse = DOL_VERSION;
339 $this->products = array();
341 $this->categorie = $options[
'categorie'] ?? 0;
342 $this->per_page = $options[
'per_page'] ?? 11;
343 $this->no_page = $options[
'no_page'] ?? 1;
344 $this->search = $options[
'search'] ??
'';
346 $this->per_page = 11;
349 if (!empty($this->search) && strlen(str_replace(
' ',
'', (
string) $this->search)) < 2) {
350 $html .=
'<tr class=""><td colspan="3" class="center">';
352 $html .= $langs->trans(
"SearchStringMinLength").
'...';
354 $html .=
'</td></tr>';
359 'categorieid' => $this->categorie,
360 'limit' => $this->per_page,
361 'page' => $this->no_page,
362 'search' => $this->search,
363 'lang' => $this->lang
367 $this->numberTotalOfProducts = 0;
371 $dolistoreProducts = array();
372 $dolistoreProductsTotal = 0;
373 if ($this->dolistoreApiStatus > 0 &&
getDolGlobalInt(
'MAIN_ENABLE_EXTERNALMODULES_DOLISTORE')) {
374 $getDolistoreProducts = $this->
callApi(
'products', $data);
376 if (!isset($getDolistoreProducts[
'response']) || !is_array($getDolistoreProducts[
'response']) || ($getDolistoreProducts[
'status_code'] != 200 && $getDolistoreProducts[
'status_code'] != 201)) {
377 $dolistoreProducts = array();
378 $dolistoreProductsTotal = 0;
380 $dolistoreProducts = $this->
adaptData($getDolistoreProducts[
'response'][
'products'],
'dolistore');
381 $dolistoreProductsTotal = $getDolistoreProducts[
'response'][
'total'];
382 $this->numberTotalOfProducts += $dolistoreProductsTotal;
388 $fileProducts = array();
389 $fileProductsTotal = 0;
390 if (!empty($this->githubFileStatus) &&
getDolGlobalInt(
'MAIN_ENABLE_EXTERNALMODULES_COMMUNITY')) {
393 $fileProducts = $this->
adaptData($fileProducts,
'githubcommunity');
395 $fileProducts = $this->
applyFilters($fileProducts, $data);
397 $fileProductsTotal = $fileProducts[
'total'];
399 $this->numberTotalOfProducts += $fileProductsTotal;
401 $fileProducts = $fileProducts[
'data'];
405 $this->numberTotalOfPages = (int) ceil(max($fileProductsTotal / $this->per_page, $dolistoreProductsTotal / $this->per_page));
408 $this->products = $dolistoreProducts;
409 foreach ($fileProducts as $fileProduct) {
410 $id = $fileProduct[
'id'];
412 if (empty($this->products[
$id])) {
413 array_unshift($this->products, $fileProduct);
415 $this->products[
$id] = $fileProduct;
416 $this->products[
$id][
'category'] = $fileProduct[
'category'];
419 array_unshift($this->products, $fileProduct);
425 foreach ($this->products as $product) {
430 if ($last_month < strtotime($product[
'datec'])) {
431 $newapp .=
'<span class="newApp" title="'.$product[
'tms'].
'">'.$langs->trans(
'New').
'</span> ';
435 if ($last_month < strtotime($product[
'tms']) && $newapp ==
'') {
436 $newapp .=
'<span class="updatedApp" title="'.$product[
'tms'].
'">'.$langs->trans(
'UpdatedRecently').
'</span> ';
440 if ($product[
"cover_photo_url"] !=
'' && $product[
"cover_photo_url"] !=
'#') {
441 $images =
'<a href="'.$product[
"cover_photo_url"].
'" class="documentpreview" target="_blank" rel="noopener noreferrer" mime="image/png" title="'.
dol_escape_htmltag($product[
"label"].
', '.$langs->trans(
'Version').
' '.$product[
"module_version"]).
'">';
442 $images .=
'<img class="imgstore" src="'.$product[
"cover_photo_url"].
'" alt="" /></a>';
444 $images =
'<img class="imgstore" src="'.DOL_URL_ROOT.
'/public/theme/common/nophoto.png" />';
448 if (array_key_exists(
'price_ht', $product) &&
price2num($product[
"price_ht"]) > 0) {
449 $price =
'<h3>'.price(
price2num($product[
"price_ht"],
'MT'), 0, $langs, 1, -1, -1,
'EUR').
' '.$langs->trans(
"HT").
'</h3>';
451 $download_link =
'<a class="paddingleft paddingright" target="_blank" title="'.$langs->trans(
"View").
'" href="'.$this->shop_url.
'/product.php?id='.((int) $product[
'id']).
'">';
452 $download_link .=
img_picto(
'',
'url',
'class="size2x paddingright"');
453 $download_link .=
'</a>';
455 $download_link =
'#';
456 if ($product[
'source'] ===
'dolistore') {
457 $urlview = $this->shop_url.
'/product.php?id='.((int) $product[
"id"]);
458 $price =
'<h3><a href="'.$urlview.
'" target="_blank">'.$langs->trans(
'SeeOnDoliStore').
'</a></h3>';
459 } elseif ($product[
'source'] ===
'githubcommunity') {
460 if (array_key_exists(
'price_ht', $product) && empty($product[
'price_ht'])) {
461 $price =
'<h3>'.$langs->trans(
'Free').
'</h3>';
463 if ($product[
"dolistore-download"]) {
464 $price =
'<h3><a href="'.$product[
"dolistore-download"].
'" target="_blank">'.$langs->trans(
'SeeOnDoliStore').
'</a></h3>';
466 $price =
'<h3>'.$langs->trans(
'Unknown').
'</h3>';
470 $price =
'<h3>'.$langs->trans(
'Unknown').
'</h3>';
473 if ($product[
'source'] ===
'githubcommunity') {
474 $download_link =
'<a class="paddingleft paddingright" target="_blank" title="'.$langs->trans(
"Sources").
'" href="'.$product[
"link"].
'">';
475 $download_link .=
img_picto(
'',
'file-code',
'class="size2x paddingright colorgrey"');
476 $download_link .=
'</a>';
478 $urlview = $product[
"dolistore-download"];
480 $download_link .=
'<a class="paddingleft paddingright" target="_blank" title="'.$langs->trans(
"View").
'" href="'.$urlview.
'" rel="noopener noreferrer">';
481 $download_link .=
img_picto(
'',
'url',
'class="size2x"');
482 $download_link .=
'</a>';
485 if (!empty($product[
'direct-download']) && $product[
'direct-download'] ==
'yes') {
487 if (preg_match(
'/https:.*\?id=(\d+)$/', $urlview, $reg)) {
488 $urldownload =
'https://www.dolistore.com/_service_download.php?t=free&p='.$reg[1];
489 $download_link .=
'<a class="paddingleft paddingright" target="_blank" title="'.$langs->trans(
"Download").
'" href="'.$urldownload.
'" rel="noopener noreferrer">';
490 $download_link .=
img_picto(
'',
'download',
'class="size2x paddingright"');
492 $download_link .=
'</a>';
495 } elseif ($product[
'source'] ===
'dolistore') {
496 $urlview = $this->shop_url.
'/product.php?id='.((int) $product[
"id"]);
497 $urldownload =
'https://www.dolistore.com/_service_download.php?t=free&p=' . $product[
'id'];
498 $download_link =
'<a class="paddingleft paddingright" target="_blank" title="'.$langs->trans(
"View").
'" href="'.$urlview.
'">';
499 $download_link .=
img_picto(
'',
'url',
'class="size2x"');
500 $download_link .=
'</a>';
501 $download_link .=
'<a class="paddingleft paddingright" target="_blank" title="'.$langs->trans(
"Download").
'" href="'.$urldownload.
'" rel="noopener noreferrer">';
502 $download_link .=
img_picto(
'',
'download',
'class="size2x paddingright"');
504 $download_link .=
'</a>';
511 if ($product[
"status"] ==
'soon') {
512 $version =
'<span class="warning">'.$langs->trans(
"NotYetAvailable").
'</span>';
513 } elseif ($this->
versionCompare($product[
"dolibarr_min"], $dolibarrversiontouse) <= 0) {
514 if (!empty($product[
"dolibarr_max"]) && $product[
"dolibarr_max"] !=
'auto' && $product[
"dolibarr_max"] !=
'unknown' && $this->
versionCompare($product[
"dolibarr_max"], $dolibarrversiontouse) >= 0) {
516 $version =
'<span class="compatible">'.$langs->trans(
518 $dolibarrversiontouse,
519 $product[
"dolibarr_min"],
520 $product[
"dolibarr_max"]
525 $version =
'<span class="warning hideonsmartphone">'.$langs->trans(
527 $dolibarrversiontouse,
528 $product[
"dolibarr_min"],
529 $product[
"dolibarr_max"]
531 $compatible =
'NotCompatible';
534 if ($product[
"dolibarr_min"] ==
'auto' || $product[
"dolibarr_min"] !=
'unknown') {
536 $version =
'<span class="warning">'.$langs->trans(
538 $dolibarrversiontouse,
539 $product[
"dolibarr_min"],
540 $product[
"dolibarr_max"]
542 $compatible =
'NotCompatible';
545 $version =
'<span class="compatibleafterupdate">'.$langs->trans(
546 'CompatibleAfterUpdate',
547 $dolibarrversiontouse,
548 $product[
"dolibarr_min"],
549 $product[
"dolibarr_max"]
551 $compatible =
'NotCompatible';
556 $html .=
'<tr class="app oddeven nohover '.dol_escape_htmltag($compatible).
'">';
559 $html .=
'<td class="center width150"><div class="newAppParent">';
560 $html .= $newapp.$images;
561 $html .=
'</div></td>';
564 $html .=
'<td class="margeCote minwidth500imp"><h2 class="appTitle">';
566 if (!empty($product[
'author']) && $product[
'author'] !=
'unkownauthor') {
567 $html .=
'<small> - '.img_picto(
'',
'company',
'class="pictofixedwidth"');
568 if (!empty($product[
'author_url'])) {
569 $html .=
'<a href="'.$product[
'author_url'].
'" target="_blank">'.$product[
'author'].
'</a>';
571 $html .= $product[
'author'];
575 $html .=
'<br><small>';
580 $html .=
'<small class="appDateCreation appRef"> ';
581 if (empty($product[
'tms'])) {
582 $html .=
img_picto($langs->trans(
'DateCreation'),
'calendar',
'class="pictofixedwidth"').
'<span class="opacitymedium"><span class="hideonsmartphone">'.$langs->trans(
"DateCreation").
': </span>';
585 $html .=
img_picto($langs->trans(
'DateModification'),
'calendar',
'class="pictofixedwidth"').
'<span class="opacitymedium">'.
dol_print_date(
dol_stringtotime($product[
'tms']),
'day').
'</span>';
587 $html .=
' '.$langs->trans(
'Ref').
' '.
dolPrintHTML(preg_replace(
'/@.*$/',
'', $product[
"ref"]));
589 $html .=
'</small><br>';
591 $html .=
'<div class="appSource valignmiddle inline-block">';
592 if ($product[
"source"] ==
'dolistore') {
594 $html .=
'<img border="0" title="'.dolPrintHTML($langs->trans(
'Source').
": DoliStore").
'" class="imgautosize imgmaxwidth100 valignmiddle" style="height: 14px" src="'.DOL_URL_ROOT.
'/theme/dolistore_squarred.svg">';
595 } elseif ($product[
"source"] ==
'githubcommunity') {
596 $html .=
img_picto($langs->trans(
'Source').
': GitHub community repo',
'group',
'class="pictofixedwidth valignmiddle"');
598 $html .=
img_picto($langs->trans(
'Source').
': '.$langs->trans(
'Other'),
'generic',
'class="pictofixedwidth"');
601 $html .=
'</div> ';
602 if (!empty($product[
'phpmin']) && $product[
'phpmin'] !=
'unknown') {
603 $html .=
' <span class="badge-secondary small" style="padding: 3px; border-radius: 5px">PHP min '.$product[
'phpmin'].
'</span>';
605 if (!empty($product[
'phpmax']) && $product[
'phpmax'] !=
'unknown') {
606 $html .=
' <span class="badge-secondary small" style="padding: 3px; border-radius: 5px">PHP max '.$product[
'phpmax'].
'</span>';
611 $html .=
'<div class="storedesc">'.dolPrintHTML(
dol_string_nohtmltag($product[
"description"])).
'</div>';
615 $html .=
'<td class="margeCote center amount">';
620 $html .=
'<td class="margeCote nowraponall">'.$download_link.
'</td>';
625 if (empty($this->products)) {
626 $html .=
'<tr class=""><td colspan="3" class="center">';
628 $langs->load(
"website");
629 $html .= $langs->trans(
"noResultsWereFound").
'...';
631 $html .=
'</td></tr>';
634 $this->numberOfProducts = count($this->products);
653 function (array $a, array $b) use ($key) {
654 $valA = isset($a[$key]) && is_scalar($a[$key]) ? (string) $a[$key] :
'';
655 $valB = isset($b[$key]) && is_scalar($b[$key]) ? (string) $b[$key] :
'';
657 return strnatcmp($valA, $valB);
671 $v1 = str_replace(array(
'v',
'V'),
'', $v1);
672 $v2 = str_replace(array(
'v',
'V'),
'', $v2);
674 $v1 = explode(
'.', $v1);
675 $v2 = explode(
'.', $v2);
678 $count1 = count($v1);
679 $count2 = count($v2);
680 $maxcount = max($count1, $count2);
681 while ($level < $maxcount) {
682 $operande1 = isset($v1[$level]) ? $v1[$level] :
'x';
683 $operande2 = isset($v2[$level]) ? $v2[$level] :
'x';
685 if (strtoupper($operande1) ==
'X' || strtoupper($operande2) ==
'X' || $operande1 ==
'*' || $operande2 ==
'*') {
688 if ($operande1 < $operande2) {
692 if ($operande1 > $operande2) {
711 return '<a href="'.$this->get_previous_url().
'" class="button">'.
dol_escape_htmltag($text).
'</a>';
724 return '<a href="'.$this->get_next_url().
'" class="button">'.
dol_escape_htmltag($text).
'</a>';
736 $param_array = array();
737 if ($this->no_page > 1) {
742 if (!empty($this->search)) {
743 $param_array[
'search_keyword'] = $this->search;
745 $param_array[
'no_page'] = $this->no_page - $sub;
746 if ($this->categorie != 0) {
747 $param_array[
'categorie'] = $this->categorie;
749 $param = http_build_query($param_array);
750 return $this->url.
"&".$param;
762 $param_array = array();
763 if ($this->products !==
null && count($this->products) < $this->per_page) {
768 if (!empty($this->search)) {
769 $param_array[
'search_keyword'] = $this->search;
771 $param_array[
'no_page'] = $this->no_page + $add;
772 if ($this->categorie != 0) {
773 $param_array[
'categorie'] = $this->categorie;
775 $param = http_build_query($param_array);
776 return $this->url.
"&".$param;
789 $page = $this->no_page;
790 $limit = $this->per_page;
791 $totalnboflines = $this->numberTotalOfProducts ?: 0;
792 $num = $this->numberOfProducts;
798 if ($page > 0 || $num > $limit) {
799 if ($totalnboflines) {
801 $nbpages = $this->numberTotalOfPages;
808 $pagelist .=
'<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious reposition" href="'.$this->get_previous_url().
'"><i class="fa fa-chevron-left" title="'.
dol_escape_htmltag($langs->trans(
"Previous")).
'"></i></a></li>';
811 $pagelist .=
'<li class="pagination">';
812 $pagelist .=
'<label for="page_input">Page </label>';
813 if ($this->categorie != 0) {
814 $pagelist .=
'<input type="hidden" name="categorie" value="' . $this->categorie .
'">';
816 $pagelist .=
'<input type="text" id="page_input" name="no_page" value="'.($page).
'" min="1" max="'.$nbpages.
'" class="width40 page_input" oninput="if(this.value > '.$nbpages.
') this.value='.$nbpages.
'">';
817 $pagelist .=
' / '.$nbpages;
818 $pagelist .=
'</li>';
821 if ($page < $nbpages) {
822 $pagelist .=
'<li class="pagination paginationpage paginationpageright"><a class="paginationnext reposition" href="'.$this->get_next_url().
'"><i class="fa fa-chevron-right" title="'.
dol_escape_htmltag($langs->trans(
"Next")).
'"></i></a></li>';
827 if ($limit || $pagelist) {
828 $html .=
'<div class="pagination" style="padding: 7px;">';
851 400 =>
'Bad Request',
852 401 =>
'Unauthorized',
854 405 =>
'Method Not Allowed',
855 500 =>
'Internal Server Error',
859 if ($request[
'status_code'] === 200 || $request[
'status_code'] === 201) {
864 $error_message = $error_messages[$request[
'status_code']] ??
'Unexpected HTTP status: ' . $request[
'status_code'];
867 if (!empty($request[
'response']) && isset($request[
'response'][
'errors']) && is_array($request[
'response'][
'errors'])) {
868 foreach ($request[
'response'][
'errors'] as $error) {
869 $error_message .=
' - (Code ' . $error[
'code'] .
'): ' . $error[
'message'];
873 if (!empty($request[
'curl_error_msg'])) {
874 $error_message .=
' - ' . $request[
'curl_error_msg'];
878 return sprintf(
'This call to the API failed and returned an HTTP status of %d. That means: %s.', $request[
'status_code'], $error_message);
891 $cache_file = $this->cache_file;
892 $cache_folder = dirname($cache_file);
899 if (!file_exists($cache_file) || filemtime($cache_file) < (
dol_now() - $cache_time)) {
901 $addheaders = array();
902 $result =
getURLContent($file_source_url,
'GET',
'', 1, $addheaders);
903 if (!empty($result) && $result[
'http_code'] == 200) {
904 $yaml = $result[
'content'];
905 $result = file_put_contents($cache_file, $yaml);
906 if ($result ===
false) {
907 $this->error =
'Failed to create cache file: ' . $cache_file;
911 $yaml = file_get_contents($cache_file);
927 $currentPackage =
null;
928 $currentSection =
null;
930 foreach (explode(
"\n", trim($yaml)) as $line) {
931 $trimmedLine = trim($line);
934 if ($trimmedLine ===
'' || strpos($trimmedLine,
'#') === 0) {
940 if (preg_match(
'/^\s*-\s*modulename:\s*["\']?(.*?)["\']?$/', $trimmedLine, $matches)) {
941 if ($currentPackage !==
null) {
943 if (!empty($currentPackage[
'status']) && in_array($currentPackage[
'status'], array(
'enabled',
'soon'))) {
944 $data[] = $currentPackage;
947 $currentPackage = [
'modulename' => $matches[1]];
948 $currentSection =
null;
953 if (!preg_match(
'/^\s*(fr|en|es|it|de):\s*["\']?(.*?)["\']?$/', $trimmedLine)) {
954 $currentSection =
null;
958 if (preg_match(
'/^(\w[\w-]*):\s*["\']?(.*?)["\']?$/', $trimmedLine, $matches)) {
959 if ($currentPackage !==
null) {
960 if ($currentSection) {
962 $currentPackage[$currentSection][$matches[1]] = $matches[2] ===
'' ? null : $matches[2];
965 $currentPackage[$matches[1]] = $matches[2] ===
'' ? null : $matches[2];
970 if (preg_match(
'/^\s*(label|description):\s*$/', $trimmedLine, $matches)) {
971 $currentSection = $matches[1];
972 $currentPackage[$currentSection] = [];
980 if ($currentPackage !==
null) {
981 if (!empty($currentPackage[
'status']) && in_array($currentPackage[
'status'], array(
'enabled',
'soon'))) {
982 $data[] = $currentPackage;
1000 if (!is_array($data) || empty($data) || empty($source)) {
1001 return $adaptedData;
1004 if ($source ===
'githubcommunity') {
1005 foreach ($data as $package) {
1006 if (empty($package[
'modulename'])) {
1013 if (!empty($package[
'dolistore-download']) && preg_match(
'/www\.dolistore\.com\/product\.php\?id=(\d+)/', (
string) $package[
'dolistore-download'], $reg)) {
1019 'ref' => str_replace(
' ',
'', $package[
'modulename'] .
'-' . $package[
'current_version'] .
'@' .
1020 (array_key_exists(
'author', $package) ? $package[
'author'] :
'unkownauthor')),
1021 'label' => !empty($package[
'label'][substr($this->lang, 0, 2)])
1022 ? $package[
'label'][substr($this->lang, 0, 2)]
1023 : (!empty($package[
'label'][
'en']) ? $package[
'label'][
'en'] : $package[
'modulename']),
1024 'description' => !empty($package[
'description'][substr($this->lang, 0, 2)])
1025 ? $package[
'description'][substr($this->lang, 0, 2)]
1026 : (!empty($package[
'description'][
'en']) ? $package[
'description'][
'en'] :
''),
1027 'datec' => (!empty($package[
'created_at']) && is_string($package[
'created_at']))
1028 ? date(
'Y-m-d H:i:s', strtotime($package[
'created_at']))
1030 'tms' => (!empty($package[
'last_updated_at']) && is_string($package[
'last_updated_at']))
1031 ? date(
'Y-m-d H:i:s', strtotime($package[
'last_updated_at']))
1033 'author' => array_key_exists(
'author', $package) ? $package[
'author'] :
'',
1034 'author_url' => array_key_exists(
'author_url', $package) ? $package[
'author_url'] :
'',
1035 'dolibarr_min' => !empty($package[
'dolibarrmin'])
1036 ? $package[
'dolibarrmin']
1038 'dolibarr_max' => !empty($package[
'dolibarrmax'])
1039 ? $package[
'dolibarrmax']
1041 'phpmin' => !empty($package[
'phpmin'])
1042 ? $package[
'phpmin']
1044 'phpmax' => !empty($package[
'phpmax'])
1045 ? $package[
'phpmax']
1047 'module_version' => !empty($package[
'current_version'])
1048 ? $package[
'current_version']
1050 'cover_photo_url' => !empty($package[
'cover'])
1053 'category' => (!empty($package[
'category']) && is_string($package[
'category']))
1054 ? explode(
',', str_replace(
' ',
'', (
string) $package[
'category']))
1056 'link' => !empty($package[
'git'])
1059 'source' =>
'githubcommunity',
1060 'status' => !empty($package[
'status']) ? $package[
'status'] :
'',
1061 'direct-download' => !empty($package[
'direct-download'])
1062 ? $package[
'direct-download']
1064 'dolistore-download' => !empty($package[
'dolistore-download'])
1065 ? $package[
'dolistore-download']
1070 if (array_key_exists(
'price', $package) && $package[
'price'] !=
null) {
1071 $adaptedPackage[
'price_ht'] = $package[
'price'];
1074 $adaptedData[] = $adaptedPackage;
1078 if ($source ===
'dolistore') {
1079 foreach ($data as $package) {
1080 $urlphoto = $this->shop_url.$package[
'cover_photo_url'];
1082 if (preg_match(
'/^\/?wrapper\.php\?hashp=/', $package[
'cover_photo_url']) && !preg_match(
'/attachment=/', $package[
'cover_photo_url'])) {
1083 $urlphoto .=
'&attachment=0';
1087 'id' => $package[
'id'],
1088 'ref' => $package[
'ref'],
1089 'label' => $package[
'label'],
1090 'description' => $package[
'description'],
1091 'datec' => $package[
'datec'],
1092 'tms' => $package[
'tms'],
1093 'author' => array_key_exists(
'author', $package) ? $package[
'author'] :
'',
1094 'author_url' => array_key_exists(
'author_url', $package) ? $package[
'author_url'] :
'',
1095 'price_ttc' => $package[
'price_ttc'],
1096 'price_ht' => $package[
'price_ht'],
1097 'dolibarr_min' => $package[
'dolibarr_min'],
1098 'dolibarr_max' => $package[
'dolibarr_max'],
1099 'phpmin' => empty($package[
'phpmin']) ?
'' : $package[
'phpmin'],
1100 'phpmax' => empty($package[
'phpmax']) ?
'' : $package[
'phpmax'],
1101 'module_version' => $package[
'module_version'],
1102 'cover_photo_url' => $urlphoto,
1103 'source' =>
'dolistore',
1104 'status' => empty($package[
'status']) ?
'' : $package[
'status']
1107 $adaptedData[$package[
'id']] = $adaptedPackage;
1111 return $adaptedData;
1123 $filteredData = $list;
1135 static function ($a, $b) {
1136 return strtotime($b[
'datec'] ??
'0') - strtotime($a[
'datec'] ??
'0');
1140 if (!empty($options[
'search'])) {
1141 $filteredData = array_filter(
1150 static function ($package) use ($options) {
1151 return stripos($package[
'label'], $options[
'search']) !==
false || stripos($package[
'description'], $options[
'search']) !==
false;
1156 if (!empty($options[
'categorieid'])) {
1157 $filteredData = array_filter(
1166 static function ($package) use ($options) {
1167 return in_array($options[
'categorieid'], $package[
'category']);
1172 $total = count($filteredData);
1175 $filteredData = array_values($filteredData);
1176 $filteredData = array_slice($filteredData, ($options[
'page'] - 1) * $options[
'limit'], $options[
'limit']);
1178 return [
'total' => $total,
'data' => $filteredData];
1189 $testRequest = $this->
callApi(
'categories');
1191 if (!isset($testRequest[
'response']) || !is_array($testRequest[
'response']) || ($testRequest[
'status_code'] != 200 && $testRequest[
'status_code'] != 201)) {
1207 public function libStatus($status, $mode = 3, $moretext =
'')
1211 $statusType =
'status4';
1213 $statusType =
'status8';
1217 $labelStatusShort = [];
1219 $labelStatus[0] = $langs->transnoentitiesnoconv(
"NotConnected");
1220 $labelStatus[1] = $langs->transnoentitiesnoconv(
"online");
1221 $labelStatusShort[0] = $langs->transnoentitiesnoconv(
"NotConnected");
1222 $labelStatusShort[1] = $langs->transnoentitiesnoconv(
"online");
1224 return dolGetStatus($labelStatus[$status], $labelStatusShort[$status],
'', $statusType, $mode,
'', array(
'badgeParams' => array(
'attr' => array(
'class' =>
'classfortooltip',
'title' => $labelStatusShort[$status].$moretext))));
$id
Support class for third parties, contacts, members, users or resources.
__construct($debug=false)
Constructor.
libStatus($status, $mode=3, $moretext='')
Retrieve the status icon.
checkApiStatus()
Check if an Dolistore API is up.
getProducts($options)
Generate HTML for products.
versionCompare($v1, $v2)
version compare
getPagination()
Generate pagination for navigating through pages of products.
get_next_url()
get next url
getRemoteYamlFile($file_source_url, $cache_time)
Get YAML file from remote source and put it into the cache file.
get_previous_link($text='<<')
get previous link
buildSorter(string $key)
Sort an array by a key.
getCategories($active=0)
Generate HTML for categories and their children.
applyFilters($list, $options)
Apply filters to the data.
get_next_link($text='> >')
get next link
fetchModulesFromFile($options=array())
Fetch modules from a cache YAML file.
checkStatusCode($request)
Check the status code of the request.
readYaml($yaml)
Read a YAML string and convert it to an array.
callApi($resource, $options=false)
Test if we can access to remote Dolistore market place.
adaptData($data, $source)
Adapter data fetched from github remote source to the expected format.
loadRemoteSources($debug=false)
loadRemoteSources
get_previous_url()
get previous url
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1, $timeoutconnect=0, $timeoutresponse=0)
Function to get a content from an URL (use proxy if proxy defined).