diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php
index 3ee3e0269122e..252b9d6541918 100644
--- a/htdocs/admin/modules.php
+++ b/htdocs/admin/modules.php
@@ -98,8 +98,10 @@
$options['search_source_github'] = 1;
}
-//$remotestore = new Dolistore(false);
-$remotestore = new ExternalModules();
+//$remotestore = new Dolistore(false);
+$remotestore = new ExternalModules();
+$remotestore->loadRemoteSources();
+
if (!$user->admin) {
accessforbidden();
@@ -484,7 +486,6 @@
-
/*
* View
*/
@@ -1264,6 +1265,8 @@
print '
';
+ print '';
+
// Marketplace and community modules
print '
';
print '
'."\n";
@@ -1277,19 +1280,38 @@
// Marketplace
print ''."\n";
$url = 'https://www.dolistore.com';
- print ' | ';
+ print ' | ';
print ''.$langs->trans("DoliStoreDesc").' | ';
- print ''.$url.' | ';
- print '' . $remotestore->libStatut($remotestore->dolistoreApiStatus).' | ';
+ print ''.img_picto('', 'url', 'class="pictofixedwidth"').''.$url.' | ';
+ print '';
+ if (!getDolGlobalString('MAIN_DISABLE_DOLISTORE_SEARCH') && getDolGlobalInt('MAIN_ENABLE_DOLISTORE')) {
+ $messagetoadd = '';
+ if ($remotestore->dolistoreApiStatus <= 0) {
+ $messagetoadd = ' '.$remotestore->dolistoreApiError.' Failed to login to: '.$remotestore->dolistore_api_url;
+ $messagetoadd .= ' using API public key: '.$remotestore->dolistore_api_key;
+ // Add basic auth if needed
+ $basicAuthLogin = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_LOGIN');
+ $basicAuthPassword = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_PASSWORD');
+ if ($basicAuthLogin) {
+ $messagetoadd .= ' using basic auth login: base64('.$basicAuthLogin.':'.$basicAuthPassword.')';
+ }
+ }
+ print $remotestore->libStatus($remotestore->dolistoreApiStatus, 2, $messagetoadd);
+ }
+ print ' | ';
print '
';
// Community
print ''."\n";
$url = 'https://github.com/Dolibarr/dolibarr-community-modules';
- print ' | ';
+ print ' | ';
print ''.$langs->trans("CommunityModulesDesc").' | ';
- print ''.$url.' | ';
- print '' . $remotestore->libStatut($remotestore->githubFileStatus) . ' | ';
+ print ''.img_picto('', 'url', 'class="pictofixedwidth"').''.$url.' | ';
+ print '';
+ if (!getDolGlobalString('MAIN_DISABLE_DOLISTORE_SEARCH') && getDolGlobalInt('MAIN_ENABLE_COMMUNITY_REPO')) {
+ print $remotestore->libStatus($remotestore->githubFileStatus, 2, ' Content of repository file '.$remotestore->file_source_url.' is in the cache file '.$remotestore->cache_file.'');
+ }
+ print ' | ';
print '
';
print "
\n";
@@ -1301,10 +1323,10 @@
$conf->global->MAIN_DISABLE_DOLISTORE_SEARCH = 0; // avoid warning with the new Dolistore website
- if (!getDolGlobalString('MAIN_DISABLE_DOLISTORE_SEARCH') && getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2 && $remotestore->numberOfProviders > 0) {
+ if (!getDolGlobalString('MAIN_DISABLE_DOLISTORE_SEARCH') && $remotestore->numberOfProviders > 0) {
// $options is array with filter criteria
- if (getDolGlobalInt('MAIN_ENANLE_OLD_DOLISTORE')) {
+ if (getDolGlobalInt('MAIN_ENABLE_DOLISTORE')) {
$nbmaxtoshow = $options['per_page'];
$options['per_page']++;
diff --git a/htdocs/admin/remotestore/class/externalModules.class.php b/htdocs/admin/remotestore/class/externalModules.class.php
index cdb0362ca0a9b..fa0e87fb49038 100644
--- a/htdocs/admin/remotestore/class/externalModules.class.php
+++ b/htdocs/admin/remotestore/class/externalModules.class.php
@@ -110,6 +110,7 @@ class ExternalModules
*/
public $products;
+
/**
* Constructor
*
@@ -119,29 +120,41 @@ public function __construct($debug = false)
{
global $langs;
- $cachedelayforgithubrepo = getDolGlobalInt('MAIN_REMOTE_GITHUBREPO_CACHE_DELAY', 86400);
-
$this->dolistore_api_url = getDolGlobalString('MAIN_MODULE_DOLISTORE_API_SRV');
$this->dolistore_api_key = getDolGlobalString('MAIN_MODULE_DOLISTORE_API_KEY');
$this->url = DOL_URL_ROOT.'/admin/modules.php?mode=marketplace';
$this->shop_url = 'https://www.dolistore.com/product.php?id=';
+
$this->debug_api = $debug;
$this->file_source_url = "https://raw.githubusercontent.com/Dolibarr/dolibarr-community-modules/refs/heads/main/index.yaml";
$this->cache_file = DOL_DATA_ROOT.'/admin/temp/remote_github_modules_file.yaml';
- $this->getRemoteYamlFile($this->file_source_url, $cachedelayforgithubrepo);
-
$lang = $langs->defaultlang;
$lang_array = array('en_US', 'fr_FR', 'es_ES', 'it_IT', 'de_DE');
if (!in_array($lang, $lang_array)) {
$lang = 'en_US';
}
$this->lang = $lang;
+ }
+
+ /**
+ * loadRemoteSources
+ *
+ * @param boolean $debug Enable debug of request on screen
+ * @return void
+ */
+ public function loadRemoteSources($debug = false)
+ {
+ $cachedelayforgithubrepo = getDolGlobalInt('MAIN_REMOTE_GITHUBREPO_CACHE_DELAY', 86400);
+
+ $this->getRemoteYamlFile($this->file_source_url, $cachedelayforgithubrepo);
+
// Check access to Dolistore API
$this->dolistoreApiStatus = $this->checkApiStatus();
+
$this->githubFileStatus = dol_is_file($this->cache_file) ? 1 : 0;
// Count the number of online providers
@@ -153,7 +166,6 @@ public function __construct($debug = false)
*
* @param string $resource Resource name
* @param array
|false $options Options for the request
- *
* @return array{status_code:int,response:null|string|array,header:string}
*/
public function callApi($resource, $options = false)
@@ -164,52 +176,29 @@ public function callApi($resource, $options = false)
return array('status_code' => 0, 'response' => null, 'header' => '');
}
- $curl = curl_init();
- $httpheader = ['DOLAPIKEY: '.$this->dolistore_api_key];
-
// Add basic auth if needed
$basicAuthLogin = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_LOGIN');
$basicAuthPassword = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_PASSWORD');
- if (!empty($basicAuthLogin) && !empty($basicAuthPassword)) {
- curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
- $login = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_LOGIN');
- $password = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_PASSWORD');
- curl_setopt($curl, CURLOPT_USERPWD, $login . ':' . $password);
+ $httpheader = array('DOLAPIKEY: '.$this->dolistore_api_key);
+ if ($basicAuthLogin) {
+ $httpheader[] = 'Authorization: Basic '.base64_encode($basicAuthLogin.':'.$basicAuthPassword);
}
- $url = $this->dolistore_api_url . $resource;
+ $url = $this->dolistore_api_url . (preg_match('/\/$/', $this->dolistore_api_url) ? '' : '/') . $resource;
if ($options) {
$url .= '?' . http_build_query($options);
}
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $httpheader);
- curl_setopt($curl, CURLOPT_HEADER, true);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
+ $response = getURLContent($url, 'POST', '', 1, $httpheader);
- $response = curl_exec($curl);
+ $body = $response['content'];
+ $body = json_decode($body, true);
- if ($response === false) {
- return array('status_code' => 0, 'response' => 'CURL Error: ' . curl_error($curl), 'header' => '');
- }
-
- $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
- $header = substr($response, 0, $header_size);
- $body = substr($response, $header_size);
-
- // convert body to array if it is json
- if (strpos($header, 'Content-Type: application/json') !== false) {
- $body = json_decode($body, true);
- }
-
- $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
+ $status_code = $response['http_code'];
- return array('status_code' => $status_code, 'response' => $body, 'header' => $header);
+ return array('status_code' => $status_code, 'response' => $body);
}
/**
@@ -321,8 +310,8 @@ public function getProducts($options)
// fetch from github repo
$fileProducts = array();
if (!empty($this->githubFileStatus) && $options['search_source_github'] == 1) {
- $fileProducts = $this->fetchModulesFromFile($data);
- $fileProducts = $this->adaptData($fileProducts, 'github');
+ $fileProducts = $this->fetchModulesFromFile($data); // Return an array with all modules from the cache filecontent in $data
+ $fileProducts = $this->adaptData($fileProducts, 'githubcommunity');
$fileProducts = $this->applyFilters($fileProducts, $data);
}
@@ -374,14 +363,24 @@ static function ($a, $b) {
} else {
$download_link = '#';
$price = ''.$langs->trans('Free').'
';
- if ($product['source'] === 'Dolistore') {
- $download_link = '
';
- $download_link .= '
';
- }
- if ($product['source'] === 'Github') {
+ if ($product['source'] === 'githubcommunity') {
$download_link = '
';
- $download_link .= '
';
+ if (!empty($product['direct-download']) && $product['direct-download'] == 'yes') {
+ $urldownload = $product["dolistore-download"]; // In a future, we will have the download to the zip file
+ $reg = array();
+ if (preg_match('/https:.*\?id=(\d+)$/', $urldownload, $reg)) {
+ $urldownload = 'https://www.dolistore.com/_service_download.php?t=free&p='.$reg[1];
+ }
+ $download_link .= '
';
+ }
+ }
+
+ if ($product['source'] === 'dolistore') {
+ $download_link = '
';
+ if (!empty($product['direct-download']) && $product['direct-download'] == 'yes') {
+ $download_link .= '
';
+ }
}
}
@@ -428,7 +427,15 @@ static function ($a, $b) {
$html .= '
';
$html .= $version; // No dol_escape_htmltag, it is already escape html
$html .= '';
- $html .= ' '.dol_print_date(dol_stringtotime($product['tms']), 'day').' - '.$langs->trans('Ref').': '.dol_escape_htmltag($product["ref"]).' - '.dol_escape_htmltag($langs->trans('Id')).': '.((int) $product["id"]).'
';
+ $html .= ' ';
+ if (empty($product['tms'])) {
+ $html .= ''.$langs->trans("DateCreation").': '.$langs->trans("Unknown").'';
+ } else {
+ $html .= dol_print_date(dol_stringtotime($product['tms']), 'day');
+ }
+ $html .= ' - '.$langs->trans('Ref').' '.dol_escape_htmltag($product["ref"]);
+ //$html .= ' - '.dol_escape_htmltag($langs->trans('Id')).': '.((int) $product["id"]);
+ $html .= '
';
$html .= ''.$langs->trans('Source').': '.$product["source"].'
';
$html .= '
'.dol_escape_htmltag(dol_string_nohtmltag($product["description"]));
$html .= '';
@@ -632,11 +639,11 @@ protected function checkStatusCode($request)
}
/**
- * get YAML file from remote source and put it in cache file for one day
- * @param string $file_source_url URL of the remote source
- * @param int $cache_time Cache time
+ * Get YAML file from remote source and put it into the cache file
*
- * @return string Uri of the cache file
+ * @param string $file_source_url URL of the remote source
+ * @param int $cache_time Cache time
+ * @return string Uri of the cache file
*/
public function getRemoteYamlFile($file_source_url, $cache_time)
{
@@ -649,13 +656,17 @@ public function getRemoteYamlFile($file_source_url, $cache_time)
}
if (!file_exists($cache_file) || filemtime($cache_file) < (dol_now() - $cache_time)) {
- $yaml = file_get_contents($file_source_url);
- if (!empty($yaml)) {
+ // We get remote url
+ $result = getURLContent($file_source_url);
+ if (!empty($result) && $result['http_code'] == 200) {
+ $yaml = $result['content'];
file_put_contents($cache_file, $yaml);
}
+ } else {
+ $yaml = file_get_contents($cache_file);
}
- return $cache_file;
+ return $yaml;
}
@@ -680,6 +691,7 @@ public function readYaml($yaml)
}
// Match a new package entry (e.g., "- modulename: 'helloasso'")
+ $matches = array();
if (preg_match('/^\s*-\s*modulename:\s*["\']?(.*?)["\']?$/', $trimmedLine, $matches)) {
if ($currentPackage !== null) {
$data[] = $currentPackage;
@@ -740,7 +752,7 @@ public function adaptData($data, $source)
return $adaptedData;
}
- if ($source === 'github') {
+ if ($source === 'githubcommunity') {
foreach ($data as $package) {
if (empty($package['modulename'])) {
continue;
@@ -766,6 +778,12 @@ public function adaptData($data, $source)
'dolibarr_max' => !empty($package['dolibarrmax'])
? $package['dolibarrmax']
: 'unknown',
+ 'phpmin' => !empty($package['phpmin'])
+ ? $package['phpmin']
+ : 'unknown',
+ 'phpmax' => !empty($package['phpmax'])
+ ? $package['phpmax']
+ : 'unknown',
'module_version' => !empty($package['current_version'])
? $package['current_version']
: 'unknown',
@@ -778,7 +796,13 @@ public function adaptData($data, $source)
'link' => !empty($package['git'])
? $package['git']
: '#',
- 'source' => 'Github'
+ 'source' => 'githubcommunity',
+ 'direct-download' => !empty($package['direct-download'])
+ ? $package['direct-download']
+ : '',
+ 'dolistore-download' => !empty($package['dolistore-download'])
+ ? $package['dolistore-download']
+ : '',
];
$adaptedData[] = $adaptedPackage;
@@ -797,9 +821,11 @@ public function adaptData($data, $source)
'price_ttc' => $package['price_ttc'],
'dolibarr_min' => $package['dolibarr_min'],
'dolibarr_max' => $package['dolibarr_max'],
+ 'phpmin' => $package['phpmin'],
+ 'phpmax' => $package['phpmax'],
'module_version' => $package['module_version'],
'cover_photo_url' => $urldolibarrmodules.$package['cover_photo_url'],
- 'source' => 'Dolistore'
+ 'source' => 'dolistore'
];
$adaptedData[] = $adaptedPackage;
@@ -881,7 +907,6 @@ static function ($package) use ($options) {
*/
public function checkApiStatus()
{
-
$testRequest = $this->callApi('categories');
if (!isset($testRequest['response']) || !is_array($testRequest['response']) || ($testRequest['status_code'] != 200 && $testRequest['status_code'] != 201)) {
@@ -894,28 +919,29 @@ public function checkApiStatus()
/**
* Retrieve the status icon
- * @param mixed $status Status
- * @param mixed $mode Mode
*
- * @return string
+ * @param mixed $status Status
+ * @param mixed $mode Mode
+ * @param string $moretext More text to show on tooltip
+ * @return string
*/
- public function libStatut($status, $mode = 3)
+ public function libStatus($status, $mode = 3, $moretext = '')
{
global $langs;
$statusType = 'status4';
if ($status == 0) {
- $statusType = 'status6';
+ $statusType = 'status8';
}
$labelStatus = [];
$labelStatusShort = [];
- $labelStatus[0] = $this->dolistoreApiError;
+ $labelStatus[0] = $langs->transnoentitiesnoconv("offline");
$labelStatus[1] = $langs->transnoentitiesnoconv("online");
- $labelStatusShort[0] = $this->dolistoreApiError;
+ $labelStatusShort[0] = $langs->transnoentitiesnoconv("offline");
$labelStatusShort[1] = $langs->transnoentitiesnoconv("online");
- return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', $statusType, $mode);
+ return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', $statusType, $mode, '', array('badgeParams' => array('attr' => array('class' => 'classfortooltip', 'title' => $labelStatusShort[$status].$moretext))));
}
}
diff --git a/htdocs/theme/dolibarr_logo.svg b/htdocs/theme/dolibarr_logo.svg
index 9c9259d0b3305..72795dfbbe7fd 100644
--- a/htdocs/theme/dolibarr_logo.svg
+++ b/htdocs/theme/dolibarr_logo.svg
@@ -2,39 +2,49 @@
+ id="layer1"
+ transform="translate(-11.600433,-16.785229)">ERP/CRM